< prev index next >

src/hotspot/share/runtime/arguments.cpp

Print this page
@@ -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
  

@@ -98,10 +99,11 @@
  bool   Arguments::_BackgroundCompilation        = BackgroundCompilation;
  bool   Arguments::_ClipInlining                 = ClipInlining;
  size_t Arguments::_default_SharedBaseAddress    = SharedBaseAddress;
  
  bool   Arguments::_enable_preview               = false;
+ bool   Arguments::_module_patching_disables_cds = false;
  
  char*  Arguments::_default_shared_archive_path  = nullptr;
  char*  Arguments::SharedArchivePath             = nullptr;
  char*  Arguments::SharedDynamicArchivePath      = nullptr;
  

@@ -1328,16 +1330,13 @@
    return true;
  }
  
  #if INCLUDE_CDS
  const char* unsupported_properties[] = { "jdk.module.limitmods",
-                                          "jdk.module.upgrade.path",
-                                          "jdk.module.patch.0" };
+                                          "jdk.module.upgrade.path"};
  const char* unsupported_options[] = { "--limit-modules",
-                                       "--upgrade-module-path",
-                                       "--patch-module"
-                                     };
+                                       "--upgrade-module-path"};
  void Arguments::check_unsupported_dumping_properties() {
    assert(CDSConfig::is_dumping_archive(),
           "this function is only used with CDS dump time");
    assert(ARRAY_SIZE(unsupported_properties) == ARRAY_SIZE(unsupported_options), "must be");
    // If a vm option is found in the unsupported_options array, vm will exit with an error message.

@@ -1350,10 +1349,15 @@
        }
      }
      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 (!has_jimage()) {
      vm_exit_during_initialization("Dumping the shared archive is not supported with an exploded module build");
    }
  }

@@ -1376,10 +1380,20 @@
          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;
  }
  #endif
  
  //===========================================================================================================

@@ -1865,11 +1879,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.

@@ -1912,10 +1925,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");
    }

@@ -2038,12 +2065,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;

@@ -2053,31 +2078,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

@@ -2094,11 +2119,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;

@@ -2147,11 +2172,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) {

@@ -2163,22 +2188,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

@@ -2229,11 +2315,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++) {

@@ -2356,11 +2442,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];

@@ -2417,10 +2503,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;
        }

@@ -2899,29 +2989,48 @@
    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;
+ bool match_module(void *module_name, ModulePatchPath *patch) {
+   return (strcmp((char *)module_name, patch->module_name()) == 0);
+ }
+ 
+ static bool _java_base_module_patching_disables_cds = false;
+ bool Arguments::patch_mod_javabase() {
+   return _java_base_module_patching_disables_cds;
+ }
+ 
+ void Arguments::add_patch_mod_prefix(const char* module_name, const char* path, bool allow_append, bool allow_cds) {
+   if (!allow_cds) {
+     _module_patching_disables_cds = true;
+     if (strcmp(module_name, JAVA_BASE_NAME) == 0) {
+       _java_base_module_patching_disables_cds = true;
      }
    }
  
    // 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((void*)module_name, match_module);
+   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

@@ -2960,11 +3069,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);
  

@@ -3034,10 +3143,15 @@
  
    if (!check_vm_args_consistency()) {
      return JNI_ERR;
    }
  
+   // finalize --module-patch and related --enable-preview
+   if (finalize_patch_module() != JNI_OK) {
+     return JNI_ERR;
+   }
+ 
  #if INCLUDE_CDS
    if (CDSConfig::is_dumping_static_archive()) {
      if (!mode_flag_cmd_line) {
        // By default, -Xshare:dump runs in interpreter-only mode, which is required for deterministic archive.
        //

@@ -3079,11 +3193,11 @@
        log_warning(cds)("-XX:+AutoCreateSharedArchive does not work with ArchiveClassesAtExit");
        return JNI_ERR;
      }
    }
  
-   if (UseSharedSpaces && patch_mod_javabase) {
+   if (UseSharedSpaces && patch_mod_javabase() && _module_patching_disables_cds) {
      no_shared_spaces("CDS is disabled when " JAVA_BASE_NAME " module is patched.");
    }
    if (UseSharedSpaces && check_unsupported_cds_runtime_properties()) {
      UseSharedSpaces = false;
    }

@@ -3391,15 +3505,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_shared_archive_path = NEW_C_HEAP_ARRAY(char, len, mtArguments);
-     jio_snprintf(_default_shared_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 = CDSConfig::is_valhalla_preview();
+     jio_snprintf(_default_shared_archive_path, len, "%s%sclasses%s%s.jsa",
+                 jvm_path, os::file_separator(),
+                  nocoops ? "_nocoops" : "",
+                  valhalla ? "_valhalla" : "");
    }
    return _default_shared_archive_path;
  }
  
  int Arguments::num_archives(const char* archive_path) {

@@ -4029,10 +4147,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;
< prev index next >