Skip to content

8345826: Do not automatically resolve jdk.internal.vm.ci when libgraal is used #25240

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

Closed
wants to merge 11 commits into from

Conversation

dougxc
Copy link
Member

@dougxc dougxc commented May 14, 2025

The EnableJVMCI flag currently serves 2 purposes:

  • Guards VM code (example).
  • Adds jdk.internal.vm.ci to the root module set.

This PR changes nothing about the first point.

On the second point, to use the jdk.internal.vm.ci module when libgraal is enabled, -XX:+EnableJVMCI must be explicitly specified to the launcher (as opposed to being true as a result of -XX:+UseJVMCICompiler or -XX:+EnableJVMCIProduct). Alternatively, --add-modules=jdk.internal.vm.ci can be specified - it has the same semantics as -XX:+EnableJVMCI.
If libgraal is not enabled, +EnableJVMCI will continue to add jdk.internal.vm.ci to the root module set.

The primary motivation is to make use of libgraal compatible with -XX:+AOTClassLinking. This flag relies on the root module set archive created in a training run. If the root module set is different in the production run, the AOTClassLinking optimizations are disabled. As jdk.internal.vm.ci is not resolved in the training run, it must not be resolved in production run. As such, -XX:+EnableJVMCI must not cause resolution of jdk.internal.vm.ci, otherwise libgraal will not have the startup advantages of AOTClassLinking.

Graal adaption PR: oracle/graal#11212


Progress

  • Change must be properly reviewed (1 review required, with at least 1 Reviewer)
  • Change must not contain extraneous whitespace
  • Commit message must refer to an issue

Issue

  • JDK-8345826: Do not automatically resolve jdk.internal.vm.ci when libgraal is used (Enhancement - P3)

Reviewers

Reviewing

Using git

Checkout this PR locally:
$ git fetch https://git.openjdk.org/jdk.git pull/25240/head:pull/25240
$ git checkout pull/25240

Update a local copy of the PR:
$ git checkout pull/25240
$ git pull https://git.openjdk.org/jdk.git pull/25240/head

Using Skara CLI tools

Checkout this PR locally:
$ git pr checkout 25240

View PR using the GUI difftool:
$ git pr show -t 25240

Using diff file

Download this PR as a diff file:
https://git.openjdk.org/jdk/pull/25240.diff

Using Webrev

Link to Webrev Comment

@bridgekeeper
Copy link

bridgekeeper bot commented May 14, 2025

👋 Welcome back dnsimon! A progress list of the required criteria for merging this PR into master will be added to the body of your pull request. There are additional pull request commands available for use with this pull request.

@openjdk
Copy link

openjdk bot commented May 14, 2025

@dougxc This change now passes all automated pre-integration checks.

ℹ️ This project also has non-automated pre-integration requirements. Please see the file CONTRIBUTING.md for details.

After integration, the commit message for the final commit will be:

8345826: Do not automatically resolve jdk.internal.vm.ci when libgraal is used

Reviewed-by: iklam, never, kvn

You can use pull request commands such as /summary, /contributor and /issue to adjust it as needed.

At the time when this comment was updated there had been 226 new commits pushed to the master branch:

As there are no conflicts, your changes will automatically be rebased on top of these commits when integrating. If you prefer to avoid this automatic rebasing, please check the documentation for the /integrate command for further details.

➡️ To integrate this PR with the above commit message to the master branch, type /integrate in a new comment.

@openjdk
Copy link

openjdk bot commented May 14, 2025

@dougxc The following labels will be automatically applied to this pull request:

  • graal
  • hotspot

When this pull request is ready to be reviewed, an "RFR" email will be sent to the corresponding mailing lists. If you would like to change these labels, use the /label pull request command.

@openjdk openjdk bot added graal graal-dev@openjdk.org hotspot hotspot-dev@openjdk.org labels May 14, 2025
@tkrodriguez
Copy link
Contributor

I found your explanation quite confusing, but the bug title is actually the most clear description of the fix. Basically libjvmci doesn't require the existence of jdk.internal.vm.ci on the HotSpot side since it has effectively compiled that into itself. So we are decoupling the ability to use JVMCI from the presence of the JVMCI module. A short comment along these lines in at least your changes in check_vm_args_consistency would be helpful I think.

I do find it confusing that we are explicitly passing --add-modules=jdk.internal.vm.ci in a bunch of the tests. Is that now necessary or are you just exercising the alternate ways of enabling JVMCI?

@dougxc
Copy link
Member Author

dougxc commented May 15, 2025

I found your explanation quite confusing, but the bug title is actually the most clear description of the fix. Basically libjvmci doesn't require the existence of jdk.internal.vm.ci on the HotSpot side since it has effectively compiled that into itself. So we are decoupling the ability to use JVMCI from the presence of the JVMCI module. A short comment along these lines in at least your changes in check_vm_args_consistency would be helpful I think.

I added the requested comment and tried to clarify the PR description. Let me know if clarification is needed.

I do find it confusing that we are explicitly passing --add-modules=jdk.internal.vm.ci in a bunch of the tests. Is that now necessary or are you just exercising the alternate ways of enabling JVMCI?

Without that option, the module will be missing and without the fail-fast check in check_vm_args_consistency you would get an error such as:

Uncaught exception at src/hotspot/share/jvmci/jvmciRuntime.cpp:1433
java.lang.NoClassDefFoundError: jdk/vm/ci/code/Architecture
#
# A fatal error has been detected by the Java Runtime Environment:
#
#  Internal Error (jvmciRuntime.cpp:1636), pid=1979, tid=9731
#  fatal error: Fatal JVMCI exception (see JVMCI Events for stack trace): Uncaught exception at src/hotspot/share/jvmci/jvmciRuntime.cpp:1433
#

@dougxc dougxc marked this pull request as ready for review May 15, 2025 21:49
@openjdk openjdk bot added the rfr Pull request is ready for review label May 15, 2025
@mlbridge
Copy link

mlbridge bot commented May 15, 2025

@vnkozlov
Copy link
Contributor

Basically libjvmci doesn't require the existence of jdk.internal.vm.ci on the HotSpot side since it has effectively compiled that into itself. So we are decoupling the ability to use JVMCI from the presence of the JVMCI module.

That should be in PR and RFE (JBS) Descriptions! This was my main question about filed REF.

// compiled into the libjvmci image itself. Without libjvmci, there
// is no other representation of the jdk.internal.vm.ci module
// so it needs to be added to the root module set.
if (ClassLoader::is_module_observable("jdk.internal.vm.ci") && !UseJVMCINativeLibrary && !_jvmci_module_added) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In which case ClassLoader::is_module_observable("jdk.internal.vm.ci") == true when _jvmci_module_added == false?
I assume this code is executed after check command line for --add-modules=jdk.internal.vm.ci.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The documentation for is_module_observable is:

  // Determines if the named module is present in the
  // modules jimage file or in the exploded modules directory.

That is, is the module present on disk. On the other hand, _jvmci_module_added is a test of whether an --add-modules option has been seen whose value included jdk.internal.vm.ci.

@dougxc
Copy link
Member Author

dougxc commented May 16, 2025

That should be in PR and RFE (JBS) Descriptions! This was my main question about filed REF.

I've updated both the PR and JBS issue descriptions. Let me know if either still need improvement.

if (ClassLoader::is_module_observable("jdk.internal.vm.ci") && !UseJVMCINativeLibrary && !_jvmci_module_added) {
jio_fprintf(defaultStream::error_stream(),
"'+EnableJVMCI' requires '--add-modules=jdk.internal.vm.ci' when UseJVMCINativeLibrary is false\n");
return false;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's something a bit uncomfortable about an error message naming a JDK internal module to specify to --add-modules.

If I understand correctly, +EnableJVMCI and libgraal is all good, the set of modules in the training run is the same as the production run. However, in the no libgraal scenario, and a mismatch between training and production runs (is that right)? then AOT is disabled. Is it really terrible to disable the AOTClassLinking optimizations in that scenario?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the set of modules in the training run is the same as the production run

I don't think that's true. That is, I don't think +EnableJVMCI is used in the training run is it @iklam ?

Is it really terrible to disable the AOTClassLinking optimizations in that scenario?

Depends if you care as much about VM startup when using libgraal as when using C2. Is there a reason why only one of these should have AOTClassLinking startup benefits?

There's also the issue of all the AOTClassLinking tests having to be disabled/ignored/problem listed in the libgraal mach5 tiers. This is what initially motivated @iklam and I to come up with this solution.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Depends if you care as much about VM startup when using libgraal as when using C2. Is there a reason why only one of these should have AOTClassLinking startup benefits?

I should have been clearer, my question/comment was about the no-libgraal case, not the libgraal case. With the proposed change, I think you are looking to print an error. I'm wondering why it can't continue to add jdk.internal.vm.ci to the set of root modules.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, that's a good suggestion. I'll explore further.

And I now understand your first comment about "+EnableJVMCI and libgraal is all good" is in the context of having applied this PR. In that context, the set of modules in the training run is indeed the same as the production run.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've pushed a commit that implements your suggestion and reduces the size of the overall change nicely. More importantly, I think it's a better design - thanks!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like this latest version!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I ran a recent build of Oracle JDK 25 that has libjvmcicompiler.so (not including your changes):

$ ./bin/java -XX:+UnlockExperimentalVMOptions -XX:+UseGraalJIT -XX:+PrintFlagsFinal --version | \
        egrep '(EnableJVMCI)|(UseJVMCICompiler)|(UseJVMCINativeLibrary)'
     bool EnableJVMCI           = true    {JVMCI product} {default}
     bool EnableJVMCIProduct    = true    {JVMCI product} {command line}
     bool UseJVMCICompiler      = true    {JVMCI product} {default}
     bool UseJVMCINativeLibrary = true    {JVMCI product} {default}
$ ./bin/java -XX:+UnlockExperimentalVMOptions -XX:+EnableJVMCI -XX:+PrintFlagsFinal --version | \
        egrep '(EnableJVMCI)|(UseJVMCICompiler)|(UseJVMCINativeLibrary)'
     bool EnableJVMCI           = true    {JVMCI experimental} {command line}
     bool EnableJVMCIProduct    = false   {JVMCI experimental} {default}
     bool UseJVMCICompiler      = false   {JVMCI experimental} {default}
     bool UseJVMCINativeLibrary = true    {JVMCI experimental} {default}

So If you specify only -XX:+EnableJVMCI in the command-line, UseJVMCINativeLibrary will be true. As a result, with your latest version, the jdk.internal.vm.ci module is not added.

If you have an app that wants to use the jdk.internal.vm.ci API, you must specify both -XX:+EnableJVMCI and --add-modules=jdk.internal.vm.ci. Is this intentional?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you have an app that wants to use the jdk.internal.vm.ci API, you must specify both -XX:+EnableJVMCI and --add-modules=jdk.internal.vm.ci.

You should only have to specify --add-modules=jdk.internal.vm.ci and that now sets +EnableJVMCI. If you also want libgraal to be used as the JIT (instead of C2), then you need to add -XX:+UseGraalJIT.

For the Truffle on Oracle JDK, this means:

  • --add-modules=jdk.internal.vm.ci: Use C2 for JIT ("hosted") compilation and libgraal for Truffle ("guest") compilation
  • --add-modules=jdk.internal.vm.ci -XX:+UseGraalJIT: Use libgraal for both JIT and Truffle compilation

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah I forgot the setting on EnableJVMCI to true. Thanks for the explanation.

Comment on lines 1813 to 1814
PropertyList_unique_add(&_system_properties, "jdk.internal.vm.ci.enabled", "true",
AddProperty, UnwriteableProperty, InternalProperty);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the purpose of the jdk.internal.vm.ci.enabled property? Should it be enabled only if the jdk.internal.vm.ci module is added?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It exists to check in various Java-level entry points that the JVMCI VM support has been enabled so a nicer error message can be thrown that provides a possible corrective action. However, since it's now impossible to load the JVMCI module without enabling the JVMCI VM support, this is no longer of any use. I'll remove it.

@openjdk openjdk bot added the ready Pull request is ready to be integrated label May 16, 2025
@dougxc
Copy link
Member Author

dougxc commented May 16, 2025

While testing this out on Graal, I discovered an interesting corner case.

public class UseJVMCIModule {
    public static void main(String[] args) {
        jdk.vm.ci.runtime.JVMCI.getRuntime();
    }
}

If the JVMCI module is indirectly added to the root module set, it results in an error:

java --add-modules=jdk.graal.compiler --add-exports=jdk.internal.vm.ci/jdk.vm.ci.runtime=ALL-UNNAMED UseJVMCIModule.java
Exception in thread "main" java.lang.InternalError: JVMCI is not enabled
	at jdk.internal.vm.ci/jdk.vm.ci.runtime.JVMCI.initializeRuntime(Native Method)
	at jdk.internal.vm.ci/jdk.vm.ci.runtime.JVMCI.getRuntime(JVMCI.java:64)
	at UseJVMCIModule.main(UseJVMCIModule.java:3)

That is, if an app wants to use the JVMCI module, it needs to explicitly communicate this to the launcher. By the time the root module graph is being initialized in ModuleBootstrap, it's too late to set EnableJVMCI.

I improved the error message to make this clear:

Exception in thread "main" java.lang.InternalError: JVMCI is not enabled. Must specify '--add-modules=jdk.internal.vm.ci' or '-XX:+EnableJVMCI' to the java launcher.

@openjdk openjdk bot removed the ready Pull request is ready to be integrated label May 16, 2025
@@ -35,6 +35,8 @@
#include "gc/g1/g1CardTable.hpp"
#endif // INCLUDE_G1GC

#define JAVA_NOT_ENABLED_ERROR_MESSAGE "JVMCI is not enabled. Must specify '--add-modules=jdk.internal.vm.ci' or '-XX:+EnableJVMCI' to the java launcher."
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You meant JVMCI_NOT_ENABLED_ERROR_MESSAGE I assume?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You meant JVMCI_NOT_ENABLED_ERROR_MESSAGE I assume?

Ha! Nice catch ;-)

@@ -170,6 +170,7 @@ public void jniEnomemTest() throws Exception {
ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder(
"-XX:+UnlockExperimentalVMOptions",
"-XX:+EnableJVMCI",
"--add-modules=jdk.internal.vm.ci",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I stared at this for a while to understand why passing this option was required. It's a bit confusing that explicitly passing -XX:+EnableJVMCI has different effects based on the value of UseJVMCINativeLibrary. I think that if EnableJVMCI is passed on the command line then it should add the module even if libgraal is in use. So something like:
if ((!UseJVMCINativeLibrary || FLAG_IS_CMDLINE(EnableJVMCI) && ClassLoader::is_module_observable

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was not aware FLAG_IS_CMDLINE can be used for altering the semantics of a flag but there seems to be at least one precedent for it with UseCompactObjectHeaders. This is quite nice as now nothing needs to change for Truffle users in terms of enabling the Truffle optimized runtime (cc @chumer).

Copy link
Contributor

@tkrodriguez tkrodriguez left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

new version seems nice and clean.

@openjdk openjdk bot added the ready Pull request is ready to be integrated label May 16, 2025
@dougxc
Copy link
Member Author

dougxc commented May 17, 2025

Any further feedback or concerns @iklam or @AlanBateman ?

Copy link
Member

@iklam iklam left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Latest version looks good to me.

@openjdk openjdk bot removed the ready Pull request is ready to be integrated label May 18, 2025
@dougxc
Copy link
Member Author

dougxc commented May 18, 2025

In addition to loading the JVMCI module when -XX:+EnableJVMCI is on the command line, it should also be done when -XX:+EnableJVMCI is set by the jimage. The latter is how GraalVM sets some defaults and +EnableJVMCI is such a default. This ensures that the root module set is the same in training and production runs for AOTClassLinking on GraalVM.

_jvmci_module_added = true;
}
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This only works if jdk.internal.vm.ci is specified to --add-modules, it won't set _jvmci_module_added if jdk.internal.vm.ci is resolved because some other module require it. As the module is JDK internal and doesn't export an API then I assume it would be rare-to-never to require it, is that right?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct. I realized this might be a bit confusing so improved the error message a little: #25240 (comment)
But as you say, it should never be encountered in practice.

@@ -35,6 +35,8 @@
#include "gc/g1/g1CardTable.hpp"
#endif // INCLUDE_G1GC

#define JVMCI_NOT_ENABLED_ERROR_MESSAGE "JVMCI is not enabled. Must specify '--add-modules=jdk.internal.vm.ci' or '-XX:+EnableJVMCI' to the java launcher."
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's the exception message for an InternalError so maybe this is okay but in general I don't think you want to be asking users to have to name a JDK internal module.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think anyone using a non-standard configuration for Graal would not be surprised about JVMCI and would actually appreciate the extra hint when they get things "wrong".

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Assuming we want users to use -XX:+EnableJVMCI over --add-modules=jdk.internal.vm.ci for said reason, maybe flip the order?

Copy link
Member Author

@dougxc dougxc May 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Copy link
Contributor

@vnkozlov vnkozlov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good.

@dougxc
Copy link
Member Author

dougxc commented May 21, 2025

Thanks for the reviews.

/integrate

@openjdk
Copy link

openjdk bot commented May 21, 2025

Going to push as commit 8153683.
Since your change was applied there have been 229 commits pushed to the master branch:

Your commit was automatically rebased without conflicts.

@openjdk openjdk bot added the integrated Pull request has been integrated label May 21, 2025
@openjdk openjdk bot closed this May 21, 2025
@openjdk openjdk bot removed ready Pull request is ready to be integrated rfr Pull request is ready for review labels May 21, 2025
@openjdk
Copy link

openjdk bot commented May 21, 2025

@dougxc Pushed as commit 8153683.

💡 You may see a message that your pull request was closed with unmerged commits. This can be safely ignored.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
graal graal-dev@openjdk.org hotspot hotspot-dev@openjdk.org integrated Pull request has been integrated
Development

Successfully merging this pull request may close these issues.

6 participants