Skip to content

Conversation

@dougxc
Copy link
Member

@dougxc dougxc commented Sep 13, 2023

This PR adds HotSpotResolvedJavaMethod.getOopMapAt to get the oop map for a method at a given BCI. This is required to do correct clearing of oops at OSR entry points.

As part of this addition, I needed to be able to detect requests for oop maps at invalid BCIs. For this, I added InterpreterOopMap::has_valid_mask(). When an oop map computation is requested for an invalid BCI, this method returns false.


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-8315954: getArgumentValues002.java fails on Graal (Bug - P4)

Reviewers

Reviewing

Using git

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

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

Using Skara CLI tools

Checkout this PR locally:
$ git pr checkout 15705

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

Using diff file

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

Webrev

Link to Webrev Comment

@bridgekeeper
Copy link

bridgekeeper bot commented Sep 13, 2023

👋 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 Sep 13, 2023

@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 Sep 13, 2023
void InterpreterOopMap::initialize() {
_method = nullptr;
_mask_size = USHRT_MAX; // This value should cause a failure quickly
_mask_size = INT_MAX; // This value should cause a failure quickly
Copy link
Member Author

@dougxc dougxc Sep 13, 2023

Choose a reason for hiding this comment

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

Unless I'm mistaken, USHRT_MAX is a legal (but unlikely) value (i.e. max_locals in a class file can be 65635) so I changed this to use INT_MAX instead.

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 actually not legal since _mask_size is always a multiple of bits_per_entry which is 2 so any odd value would work. I would be leery of touching this unless you really need to.

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, I'll revert it to USHRT_MAX.

@dougxc dougxc marked this pull request as ready for review September 13, 2023 09:52
@openjdk openjdk bot added the rfr Pull request is ready for review label Sep 13, 2023
@mlbridge
Copy link

mlbridge bot commented Sep 13, 2023

Webrevs

@dougxc dougxc marked this pull request as draft September 13, 2023 15:26
@openjdk openjdk bot removed the rfr Pull request is ready for review label Sep 13, 2023
}

@Override
public long getLiveObjectLocalsAt(int bci, BitSet bigOopMap) {
Copy link
Contributor

Choose a reason for hiding this comment

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

This seems overly complicated to me. Why isn't it simply:

   public BitSet getLiveObjectLocalsAt(int bci) {
        int locals = getMaxLocals();
        int nwords = ((locals - 1) / 64) + 1;
        long liveness[] = new long[nwords];
        compilerToVM().getLiveObjectLocalsAt(this, bci, liveness);
        return new BitSet(liveness);
    }

Copy link
Member Author

Choose a reason for hiding this comment

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

In the common case we can avoid the overhead of allocating a long array and initializing each of it elements with a JNI upcall.

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 premature optimization. This is called once per OSR compile and the call itself is doing a dataflow over the bytecodes which seems more expensive then a couple JNI calls. The array can be filled in using copy_bytes_to from HotSpot, which is a pair of JNI calls. You already have 2 JNI calls because of the unsafe allocation. I can maybe accept that returning a long from the JVM to avoid an allocation for the < 64 case isn't a terrible idea but I can't see any universe where we care about that.

This method should be returning a BitSet not a long. Having the caller do that fixup is super ugly.

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, I'm convinced.

* @throws UnsupportedOperationException if local variable liveness is not provided
* by the current JVMCI runtime
*/
default long getLiveObjectLocalsAt(int bci, BitSet bigOopMap) {
Copy link
Contributor

Choose a reason for hiding this comment

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

I think this should be in HotSpotResolvedJavaMethod since there's no apparent need for it in SVM.

tmp->fill(method, bci);
entry->resource_copy(tmp);
if (tmp->has_valid_mask()) {
entry->resource_copy(tmp);
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 tmp is invalid (e.g. oop map was requested for invalid BCI), then resource_copy crashes the VM in strange ways since it blindly trusts the mask size to be valid.

Copy link
Contributor

Choose a reason for hiding this comment

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

This is not the only place where resource_copy() is called, could you add an assert in resource_copy() itself to check that it is never called with an invalid bci/mask_size.
Thank you.

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, I've added that assertion.

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.

This looks good now.

/**
* Computes which local variables contain live object values
* at the instruction denoted by {@code bci}. This is the "oop map" used
* by the garbage collector.
Copy link
Contributor

Choose a reason for hiding this comment

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

by the garbage collector for interpreter frames.

int nwords = ((nlocals - 1) / 64) + 1;
JVMCIPrimitiveArray oop_map = JVMCIENV->wrap(oop_map_handle);
int oop_map_len = JVMCIENV->get_length(oop_map);
if (nwords > oop_map_len) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Should we sanity check against mask.number_of_entries()? One wrinkle here is that compute_one_oop_map also computes information about the stack so the mask it computes can be larger than just max_locals. For the purposes of OSR this doesn't matter as none of the JITs support OSR with a non-empty stack, so we would never call it for a bci with a non-empty stack. So should we disallow calling it with a non-empty stack or just properly handle it by passing in an array long enough to contain max_locals + max_stack?

Copy link
Member Author

Choose a reason for hiding this comment

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

We only look up the mask for locals and so ignore stack indexes in the mask altogether. I'm assuming therefore that mask.is_oop(i) can never hit any problems.
Note that this API should be safe when called for any valid BCI, not just those for an OSR entry point. Even if called for a BCI with a non-empty stack, the current implementation simply ignores that part of the mask.

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes that's implied by the name of the method. It would make me happy if there was a comment pointing out that we're explicitly ignoring whether the stack is non-empty and contains oops.

Copy link
Member Author

Choose a reason for hiding this comment

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

Instead, I generalized getLiveObjectLocalsAt to getOopMapAt since the VM computation is for both locals and operand stack anyway.
When called for OSR entry points, the result will be the same since (currently) HotSpot requires the stack to be empty.

@dougxc dougxc marked this pull request as ready for review September 15, 2023 13:54
@openjdk
Copy link

openjdk bot commented Sep 15, 2023

@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:

8315954: getArgumentValues002.java fails on Graal

Reviewed-by: never, fparain

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 137 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 openjdk bot added ready Pull request is ready to be integrated rfr Pull request is ready for review labels Sep 15, 2023
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 looks good.

Copy link
Contributor

@fparain fparain left a comment

Choose a reason for hiding this comment

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

Runtime changes look good to me. Thank you for the additional assert.

@dougxc
Copy link
Member Author

dougxc commented Sep 21, 2023

Thanks for the reviews.

/integrate

@openjdk
Copy link

openjdk bot commented Sep 21, 2023

Going to push as commit 542b300.
Since your change was applied there have been 138 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 Sep 21, 2023
@openjdk openjdk bot closed this Sep 21, 2023
@openjdk openjdk bot removed ready Pull request is ready to be integrated rfr Pull request is ready for review labels Sep 21, 2023
@openjdk
Copy link

openjdk bot commented Sep 21, 2023

@dougxc Pushed as commit 542b300.

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

@dougxc dougxc deleted the JDK-8315954 branch August 20, 2024 06:21
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.

3 participants