Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[GR-49525] Option '--future-defaults=<option>' to enable options that are planned to become defaults in future releases. #10531

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions docs/reference-manual/native-image/BuildOutput.md
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,12 @@ This feature is currently only available for code compiled by Graal for Linux AM

The build output may contain one or more of the following recommendations that help you get the best out of Native Image.

#### <a name="recommendation-futr"></a>`FUTR`: Use the Correct Semantics and Prepare for Future Releases

Use `--future-defaults=all` to enable all features that are planned to be default in a future GraalVM release.
This option is unlikely to affect your program's behavior but guarantees that it adheres to the correct execution semantics.
Additionally, it safeguards against unexpected changes in future GraalVM updates.

#### <a name="recommendation-awt"></a>`AWT`: Missing Reachability Metadata for Abstract Window Toolkit

The Native Image analysis has included classes from the [`java.awt` package](https://docs.oracle.com/en/java/javase/22/docs/api/java.desktop/java/awt/package-summary.html) but could not find any reachability metadata for it.
Expand Down
21 changes: 16 additions & 5 deletions sdk/mx.sdk/mx_sdk_benchmark.py
Original file line number Diff line number Diff line change
Expand Up @@ -408,6 +408,8 @@ def __init__(self, vm: NativeImageVM, bm_suite: BenchmarkSuite | NativeImageBenc
base_image_build_args += ['-H:Preserve=all']
if vm.preserve_classpath:
base_image_build_args += ['-H:Preserve=module=ALL-UNNAMED']
if vm.future_defaults_all:
base_image_build_args += ['--future-defaults=all']
if vm.analysis_context_sensitivity:
base_image_build_args += ['-H:AnalysisContextSensitivity=' + vm.analysis_context_sensitivity,
'-H:-RemoveSaturatedTypeFlows', '-H:+AliasArrayTypeFlows']
Expand Down Expand Up @@ -719,6 +721,7 @@ def __init__(self, name, config_name, extra_java_args=None, extra_launcher_args=
self.native_architecture = False
self.preserve_all = False
self.preserve_classpath = False
self.future_defaults_all = False
self.use_upx = False
self.use_open_type_world = False
self.use_compacting_gc = False
Expand Down Expand Up @@ -752,19 +755,22 @@ def canonical_config_name(config_name):
def config_name(self):
# Generates the unique vm config name based on how the VM is actually configured.
# It concatenates the config options in the correct order to match the expected format.
# Note: the order of entries here must match the order of entries in _configure_from_name
config = []
if self.native_architecture is True:
config += ["native-architecture"]
if self.preserve_all is True:
config += ["preserve-all"]
if self.preserve_classpath is True:
config += ["preserve-classpath"]
if self.use_string_inlining is True:
config += ["string-inlining"]
if self.use_open_type_world is True:
config += ["otw"]
if self.use_compacting_gc is True:
config += ["compacting-gc"]
if self.preserve_all is True:
config += ["preserve-all"]
if self.preserve_classpath is True:
config += ["preserve-classpath"]
if self.future_defaults_all is True:
config += ["future-defaults-all"]
if self.is_gate is True:
config += ["gate"]
if self.use_upx is True:
Expand Down Expand Up @@ -828,8 +834,9 @@ def _configure_from_name(self, config_name):
mx.abort(f"config_name must be set. Use 'default' for the default {self.__class__.__name__} configuration.")

# This defines the allowed config names for NativeImageVM. The ones registered will be available via --jvm-config
# Note: the order of entries here must match the order of statements in NativeImageVM.config_name()
rule = r'^(?P<native_architecture>native-architecture-)?(?P<string_inlining>string-inlining-)?(?P<otw>otw-)?(?P<compacting_gc>compacting-gc-)?(?P<preserve_all>preserve-all-)?(?P<preserve_classpath>preserve-classpath-)?' \
r'(?P<gate>gate-)?(?P<upx>upx-)?(?P<quickbuild>quickbuild-)?(?P<gc>g1gc-)?' \
r'(?P<future_defaults_all>future-defaults-all-)?(?P<gate>gate-)?(?P<upx>upx-)?(?P<quickbuild>quickbuild-)?(?P<gc>g1gc-)?' \
r'(?P<llvm>llvm-)?(?P<pgo>pgo-|pgo-sampler-)?(?P<inliner>inline-)?' \
r'(?P<analysis_context_sensitivity>insens-|allocsens-|1obj-|2obj1h-|3obj2h-|4obj3h-)?(?P<no_inlining_before_analysis>no-inline-)?(?P<jdk_profiles>jdk-profiles-collect-|adopted-jdk-pgo-)?' \
r'(?P<profile_inference>profile-inference-feature-extraction-|profile-inference-pgo-|profile-inference-debug-)?(?P<sampler>safepoint-sampler-|async-sampler-)?(?P<optimization_level>O0-|O1-|O2-|O3-|Os-)?(default-)?(?P<edition>ce-|ee-)?$'
Expand All @@ -852,6 +859,10 @@ def _configure_from_name(self, config_name):
mx.logv(f"'preserve-classpath' is enabled for {config_name}")
self.preserve_classpath = True

if matching.group("future_defaults_all") is not None:
mx.logv(f"'future-defaults-all' is enabled for {config_name}")
self.future_defaults_all = True

if matching.group("string_inlining") is not None:
mx.logv(f"'string-inlining' is enabled for {config_name}")
self.use_string_inlining = True
Expand Down
2 changes: 2 additions & 0 deletions substratevm/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ This changelog summarizes major changes to GraalVM Native Image.
* (GR-61492) The experimental JDWP option is now present in standard GraalVM builds.
* (GR-55222) Enabled lazy deoptimization of runtime-compiled code, which reduces memory used for deoptimization. Can be turned off with `-H:-LazyDeoptimization`.
* (GR-54953) Add the experimental option `-H:Preserve` that makes the program work correctly without providing reachability metadata. Correctness is achieved by preserving all classes, resources, and reflection metadata in the image. Usage: `-H:Preserve=[all|none|module=<module>|package=<package>|package=<package-wildcard>|path=<cp-entry>][,...]`.
* (GR-49525) Introduced `--future-defaults=[all|<options>|none]` that enables options that are planned to become defaults in future releases. The enabled options are:
1. `run-time-initialized-jdk` shifts away from build-time initialization of the JDK, instead initializing most of it at run time. This transition is gradual, with individual components of the JDK becoming run-time initialized in each release. This process should complete with JDK 29 when this option should not be needed anymore. Unless you store classes from the JDK in the image heap, this option should not affect you. In case this option breaks your build, follow the suggestions in the error messages.

## GraalVM for JDK 24 (Internal Version 24.2.0)
* (GR-59717) Added `DuringSetupAccess.registerObjectReachabilityHandler` to allow registering a callback that is executed when an object of a specified type is marked as reachable during heap scanning.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
/*
* Copyright (c) 2025, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* 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.oracle.svm.core;

import java.util.Objects;
import java.util.Set;

import org.graalvm.collections.EconomicSet;
import org.graalvm.nativeimage.Platform;
import org.graalvm.nativeimage.Platforms;

import com.oracle.svm.core.option.APIOption;
import com.oracle.svm.core.option.AccumulatingLocatableMultiOptionValue;
import com.oracle.svm.core.option.HostedOptionKey;
import com.oracle.svm.core.option.SubstrateOptionsParser;
import com.oracle.svm.core.util.UserError;
import com.oracle.svm.util.StringUtil;

import jdk.graal.compiler.options.Option;
import jdk.graal.compiler.options.OptionType;

/**
* What makes it into the future-defaults must not be an experimental feature that can be rolled
* back. The changes must be aligning native image with the Java spec and must be thoroughly
* reviewed.
*/
public class FutureDefaultsOptions {
private static final String OPTION_NAME = "future-defaults";

private static final String DEFAULT_NAME = "<default-value>";
private static final String ALL_NAME = "all";
private static final String NONE_NAME = "none";
private static final String RUN_TIME_INITIALIZE_JDK_NAME = "run-time-initialized-jdk";

private static final Set<String> ALL_VALUES = Set.of(RUN_TIME_INITIALIZE_JDK_NAME, ALL_NAME, NONE_NAME);

private static String futureDefaultsAllValues() {
return StringUtil.joinSingleQuoted(ALL_VALUES);
}

static {
assert ALL_VALUES.stream().allMatch(futureDefaultsAllValues()::contains) : "A value is missing in the user-facing help text";
}

@APIOption(name = OPTION_NAME, defaultValue = DEFAULT_NAME) //
@Option(help = "file:doc-files/FutureDefaultsHelp.txt", type = OptionType.User) //
public static final HostedOptionKey<AccumulatingLocatableMultiOptionValue.Strings> FutureDefaults = new HostedOptionKey<>(
AccumulatingLocatableMultiOptionValue.Strings.buildWithCommaDelimiter());

private static EconomicSet<String> futureDefaults;

@Platforms(Platform.HOSTED_ONLY.class)
public static void parseAndVerifyOptions() {
futureDefaults = EconomicSet.create(ALL_VALUES.size());
var valuesWithOrigin = FutureDefaults.getValue().getValuesWithOrigins();
valuesWithOrigin.forEach(valueWithOrigin -> {
String value = valueWithOrigin.value();
if (DEFAULT_NAME.equals(value)) {
throw UserError.abort("The '%s' from %s is forbidden. It can only contain: %s.",
SubstrateOptionsParser.commandArgument(FutureDefaults, DEFAULT_NAME),
valueWithOrigin.origin(),
futureDefaultsAllValues());
}

if (!ALL_VALUES.contains(value)) {
throw UserError.abort("The '%s' option from %s contains invalid value '%s'. It can only contain: %s.",
SubstrateOptionsParser.commandArgument(FutureDefaults, value),
valueWithOrigin.origin(),
value,
futureDefaultsAllValues());
}

if (value.equals(NONE_NAME)) {
if (!valueWithOrigin.origin().commandLineLike()) {
throw UserError.abort("The '%s' option can only be used from the command line. Detected usage from %s.",
SubstrateOptionsParser.commandArgument(FutureDefaults, NONE_NAME),
valueWithOrigin.origin());
}
futureDefaults.clear();
}

futureDefaults.add(value);
});
}

private static EconomicSet<String> getFutureDefaults() {
return Objects.requireNonNull(futureDefaults, "must be initialized before usage");
}

public static boolean allFutureDefaults() {
return getFutureDefaults().contains(ALL_NAME);
}

public static boolean isJDKInitializedAtRunTime() {
return allFutureDefaults() || getFutureDefaults().contains(RUN_TIME_INITIALIZE_JDK_NAME);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[[rule]]
files = "FutureDefaultsOptions.java"
all = [
"[email protected]",
"[email protected]"
]
any = [
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Enable options that are planned to become defaults in future releases. Comma-separated list can contain 'all', 'run-time-initialized-jdk', 'none'.

The preferred way to use this option is '--future-defaults=all'. The meaning of each possible option is as follows:
1. 'all' is the preferred option, and it enables all other behaviors.
2. 'run-time-initialized-jdk' shifts away from build-time initialization of the JDK, instead initializing most of it at run time. This transition is gradual, with individual components of the JDK becoming run-time initialized in each release. This process should complete with JDK 29 when this option should not be needed anymore. Unless you store classes from the JDK in the image heap, this option should not affect you. In case this option breaks your build, follow the suggestions in the error messages.
3. 'none' disables all previous behaviors in case they were added by someone else on the command line.
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
import java.util.function.BooleanSupplier;
import java.util.function.Function;

import com.oracle.svm.core.FutureDefaultsOptions;
import org.graalvm.collections.EconomicSet;
import org.graalvm.collections.Pair;
import org.graalvm.nativeimage.ImageInfo;
Expand Down Expand Up @@ -919,7 +920,7 @@ protected void setupNativeImage(String imageName, OptionValues options, Map<Meth

ImageSingletons.add(SubstrateOptions.ReportingSupport.class, new SubstrateOptions.ReportingSupport(
DiagnosticsMode.getValue() ? DiagnosticsDir.getValue().lastValue().get() : Path.of("reports")));

FutureDefaultsOptions.parseAndVerifyOptions();
if (javaMainSupport != null) {
ImageSingletons.add(JavaMainSupport.class, javaMainSupport);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import org.graalvm.nativeimage.ImageSingletons;

import com.oracle.svm.core.BuildArtifacts;
import com.oracle.svm.core.FutureDefaultsOptions;
import com.oracle.svm.core.SubstrateGCOptions;
import com.oracle.svm.core.SubstrateOptions;
import com.oracle.svm.core.SubstrateUtil;
Expand Down Expand Up @@ -106,12 +107,17 @@ public void createAdditionalArtifactsOnSuccess(@SuppressWarnings("unused") Build

protected List<UserRecommendation> getRecommendations() {
return List.of(// in order of appearance:
new UserRecommendation("FUTR", "Use '--future-defaults=all' to prepare for future releases.", ProgressReporterFeature::recommendFutureDefaults),
new UserRecommendation("AWT", "Use the tracing agent to collect metadata for AWT.", ProgressReporterFeature::recommendTraceAgentForAWT),
new UserRecommendation("HOME", "To avoid errors, provide java.home to the app with '-Djava.home=<path>'.", AnalyzeJavaHomeAccessFeature.instance()::getJavaHomeUsed),
new UserRecommendation("HEAP", "Set max heap for improved and more predictable memory usage.", () -> SubstrateGCOptions.MaxHeapSize.getValue() == 0),
new UserRecommendation("CPU", "Enable more CPU features with '-march=native' for improved performance.", ProgressReporterFeature::recommendMArchNative));
}

private static boolean recommendFutureDefaults() {
return !FutureDefaultsOptions.allFutureDefaults();
}

private static boolean recommendMArchNative() {
if (NativeImageOptions.MicroArchitecture.getValue() != null) {
return false; // explicitly set by user
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@

import java.lang.reflect.Field;

import com.oracle.svm.core.FutureDefaultsOptions;
import org.graalvm.nativeimage.ImageSingletons;
import org.graalvm.nativeimage.Platform;
import org.graalvm.nativeimage.impl.RuntimeClassInitializationSupport;
Expand Down Expand Up @@ -143,9 +144,16 @@ public void afterRegistration(AfterRegistrationAccess access) {
rci.initializeAtBuildTime("java.awt.font.JavaAWTFontAccessImpl", "Required for sun.text.bidi.BidiBase.NumericShapings");

/* XML-related */
rci.initializeAtBuildTime("com.sun.xml", JDK_CLASS_REASON);
rci.initializeAtBuildTime("com.sun.org.apache", JDK_CLASS_REASON);
rci.initializeAtBuildTime("com.sun.org.slf4j.internal", JDK_CLASS_REASON);
if (FutureDefaultsOptions.isJDKInitializedAtRunTime()) {
// GR-50683 should remove this part
rci.initializeAtBuildTime("com.sun.xml", JDK_CLASS_REASON);
rci.initializeAtBuildTime("com.sun.org.apache", JDK_CLASS_REASON);
rci.initializeAtBuildTime("com.sun.org.slf4j.internal", JDK_CLASS_REASON);
} else {
rci.initializeAtBuildTime("com.sun.xml", JDK_CLASS_REASON);
rci.initializeAtBuildTime("com.sun.org.apache", JDK_CLASS_REASON);
rci.initializeAtBuildTime("com.sun.org.slf4j.internal", JDK_CLASS_REASON);
}

/* Security services */
rci.initializeAtBuildTime("com.sun.crypto.provider", JDK_CLASS_REASON);
Expand Down
2 changes: 1 addition & 1 deletion vm/mx.vm/mx_vm_benchmark.py
Original file line number Diff line number Diff line change
Expand Up @@ -518,7 +518,7 @@ def register_graalvm_vms():

for short_name, config_suffix in [('niee', 'ee'), ('ni', 'ce')]:
if any(component.short_name == short_name for component in mx_sdk_vm_impl.registered_graalvm_components(stage1=False)):
for main_config in ['default', 'gate', 'llvm', 'native-architecture', 'preserve-all', 'preserve-classpath'] + analysis_context_sensitivity:
for main_config in ['default', 'gate', 'llvm', 'native-architecture', 'future-defaults-all', 'preserve-all', 'preserve-classpath'] + analysis_context_sensitivity:
final_config_name = f'{main_config}-{config_suffix}'
mx_benchmark.add_java_vm(NativeImageVM('native-image', final_config_name, ['--add-exports=java.base/jdk.internal.misc=ALL-UNNAMED']), _suite, 10)
# ' ' force the empty O<> configs as well
Expand Down
Loading