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
@@ -73,10 +73,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
 
@@ -1776,11 +1777,10 @@
 // Parsing of main arguments
 
 unsigned int addreads_count = 0;
 unsigned int addexports_count = 0;
 unsigned int addopens_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.
@@ -1942,12 +1942,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;
@@ -1957,31 +1955,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
@@ -1998,11 +1996,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;
@@ -2053,11 +2051,11 @@
 
   return false;
 }
 #endif
 
-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) {
@@ -2069,22 +2067,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
@@ -2135,11 +2194,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++) {
@@ -2262,11 +2321,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, "--sun-misc-unsafe-memory-access=", &tail)) {
       if (strcmp(tail, "allow") == 0 || strcmp(tail, "warn") == 0 || strcmp(tail, "debug") == 0 || strcmp(tail, "deny") == 0) {
@@ -2332,10 +2391,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;
       }
@@ -2807,29 +2870,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
@@ -2868,11 +2943,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);
 
@@ -2942,11 +3017,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);
@@ -3712,10 +3792,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;
