Skip to content

Conversation

@marc-chevalier
Copy link
Member

@marc-chevalier marc-chevalier commented Mar 26, 2025

If TypeInstKlassPtr represents an array type, it has to be java.lang.Object. From contraposition, if it is not java.lang.Object, we can conclude it is not an array, and we can skip some array checks, for instance.

In this PR, we improve this deduction with an interface base reasoning: arrays implements only Cloneable and Serializable, so if a type implements anything else, it cannot be an array.

This change partially reverts the changes from JDK-8348631 (#23331) (in LibraryCallKit::generate_array_guard_common) and the test still passes.

The way interfaces are check might be done differently. The current situation is a balance between visibility (not to leak too much things explicitly private), having not overly general methods for one use-case and avoiding too concrete (and brittle) interfaces.

Tested with tier1..3, hs-precheckin-comp and hs-comp-stress

Thanks,
Marc


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-8348853: Fold layout helper check for objects implementing non-array interfaces (Enhancement - P2)

Reviewers

Reviewing

Using git

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

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

Using Skara CLI tools

Checkout this PR locally:
$ git pr checkout 24245

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

Using diff file

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

Using Webrev

Link to Webrev Comment

@bridgekeeper
Copy link

bridgekeeper bot commented Mar 26, 2025

👋 Welcome back mchevalier! 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 Mar 26, 2025

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

8348853: Fold layout helper check for objects implementing non-array interfaces

Reviewed-by: thartmann, roland

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 28 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.

As you do not have Committer status in this project an existing Committer must agree to sponsor your change. Possible candidates are the reviewers of this PR (@TobiHartmann, @rwestrel) but any other Committer may sponsor as well.

➡️ To flag this PR as ready for integration with the above commit message, type /integrate in a new comment. (Afterwards, your sponsor types /sponsor in a new comment to perform the integration).

@openjdk
Copy link

openjdk bot commented Mar 26, 2025

@marc-chevalier The following labels will be automatically applied to this pull request:

  • graal
  • hotspot-compiler

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-compiler hotspot-compiler-dev@openjdk.org labels Mar 26, 2025
@marc-chevalier marc-chevalier marked this pull request as ready for review March 26, 2025 10:10
@openjdk openjdk bot added the rfr Pull request is ready for review label Mar 26, 2025
@mlbridge
Copy link

mlbridge bot commented Mar 26, 2025

Webrevs

Copy link
Member

@TobiHartmann TobiHartmann left a comment

Choose a reason for hiding this comment

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

@rwestrel Should have a look at this :)

Please add an IR framework test that verifies that layout helper checks are optimized.

return Type::singleton();
}

bool TypeInterfaces::has_non_array_interface() const {
Copy link
Member

Choose a reason for hiding this comment

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

What about using TypeAryPtr::_array_interfaces->contains(_interfaces); instead?

Copy link
Member Author

Choose a reason for hiding this comment

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

Almost!

return !TypeAryPtr::_array_interfaces->contains(this);

Contains is about TypeInterfaces, that is set of interfaces. So I just need to check that this is not a sub-set of array interfaces. That should do it.

Copy link
Member

Choose a reason for hiding this comment

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

Now I'm confused, isn't this what I proposed? I didn't check the exact syntax, I just wondered if the TypeInterfaces::contains method couldn't be used instead of adding a new method.

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, totally! It's just a detail difference. But there is another question: whether we still want has_non_array_interface has a wrapper for this call with a more explicit name, or if we simply inline your suggestion on the callsite of has_non_array_interface. I tend toward the first, I like explicit names, and I suspect it might be useful in more than one place, but not a strong opinion.

Copy link
Member Author

Choose a reason for hiding this comment

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

For now, I just replaced the implementation of has_non_array_interface. If one feels even keeping the method is premature factorization, I can easily inline it.

@marc-chevalier
Copy link
Member Author

I'm not sure how to write such an IR test.

I'm looking at TestArrayGuardWithInterfaces.java.

I see the graphs of test1 before and after, and the new one is smaller. But the nodes used are pretty much the same, or they don't feel clearly linked to interface checking: there is DecodeNKlass or AddP, but it doesn't seem obvious without having the graph under the eyes that it actually checks something meaningful. There are also less If (2 instead of 3), but once again, the test seems brittle. I also see that There is no more Return only Halt since we can now prove the function cannot return normally.

But on the graph of test2 ends with two Halt: traps everywhere, even if there are paths on which test2 doesn't throw. So the lack of Return doesn't sound very robust.

Overall, not sure what a good test would be. I can write a test that would not pass before and pass now, but I'm not convinced they would reliably catch regression, and that they won't break for unrelated reasons.

@TobiHartmann
Copy link
Member

Right, I was hoping that there would be some other suitable users of GraphKit::get_layout_helper that would now be folded but all current uses either trap or don't handle both arrays and non-arrays (and therefore wouldn't fold). So I agree, adding an IR framework test does not make sense. The existing test is sufficient.

) {
// Note: When interfaces are reliable, we can narrow the interface
// test to (klass != Serializable && klass != Cloneable).
!tkls->is_instklassptr()->might_be_an_array() // not the supertype of all T[] (java.lang.Object) or has an interface that is not Serializable or Cloneable
Copy link
Contributor

Choose a reason for hiding this comment

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

Could we do the same by using TypeKlassPtr::maybe_java_subtype_of(TypeAryKlassPtr::BOTTOM) and define a TypeAryKlassPtr::BOTTOM to be a static field for the array_interfaces?

AFAICT, TypeKlassPtr::maybe_java_subtype_of() already covers that case so it would avoid some logic duplication. Also in the test above, maybe you could simplify the test a little but by removing tkls->isa_instklassptr()?

Copy link
Member Author

@marc-chevalier marc-chevalier Apr 2, 2025

Choose a reason for hiding this comment

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

I think it should be

TypeAryKlassPtr::BOTTOM->maybe_java_subtype_of(tkls)

rather than

tkls->maybe_java_subtype_of(TypeAryKlassPtr::BOTTOM)

My reasoning: if TypeAryKlassPtr::BOTTOM is java.lang.Object + Cloneable + Serializable any array is a subtype of that. But so is any class implementing these interfaces. As well as as any Object implementing more interfaces. But for these two last cases, we know they cannot be array, which is what we want to know: are we sure it's not an array, or could it be an array?

But if we check if tkls is a supertype of java.lang.Object + Cloneable + Serializable, then it has to be an Object (the most general class) and it implements a subset of Cloneable and Serializable. In this case, it can be an array. If tkls is not a super-type of java.lang.Object + Cloneable + Serializable, there are 2 cases:

  • either it is an array type directly (so, I think, in a way or another, we need to check for is_instklassptr), and so a fortiori it can be an array type.
  • it's an instance type and then cannot be an array since there is nothing between array types and java.lang.Object + Cloneable + Serializable. I.e. there is no type T that is not an array type, that is a super-type of at least one array type and that is not a super-type of java.lang.Object + Cloneable + Serializable (that is that is not java.lang.Object or that implements at least another interface).

In other words, our question is

\exists T: T is an array type /\ T <= tkls

(where A <= B means A is a subtype of B) which is equivalent to

    tkls >= (java.lang.Object + Cloneable + Serializable)
\/ (tkls <= (java.lang.Object + Cloneable + Serializable) /\ tkls is an array type)

We can spare the call to is_instklassptr by using a virtual method instead or probably other mechanisms, that's an implementation detail. But I think we need to distinguish cases: both int[] and MyClass + Cloneable + Serializable + MyInterface are sub-types of java.lang.Object + Cloneable + Serializable but for one, we can conclude it's definitely an array, and the other, it's definitely not. Without distinguishing cases, the only sound approximation would be to that that everything can be an array (both sub and super types of java.lang.Object + Cloneable + Serializable).

Does that makes sense? Did I get something wrong? is the BOTTOM not what you had in mind?

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes, what I suggested doesn't work indeed.

Copy link
Member

@TobiHartmann TobiHartmann 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 to me.

@openjdk openjdk bot added the ready Pull request is ready to be integrated label Apr 7, 2025
Copy link
Contributor

@rwestrel rwestrel 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 to me.

@marc-chevalier
Copy link
Member Author

/integrate

The branch was a bit old, so I've merged master in it and run tests. It seems all good!
Thanks @TobiHartmann and @rwestrel for the reviews.

@openjdk openjdk bot added the sponsor Pull request is ready to be sponsored label Apr 9, 2025
@openjdk
Copy link

openjdk bot commented Apr 9, 2025

@marc-chevalier
Your change (at version b1fb82a) is now ready to be sponsored by a Committer.

@TobiHartmann
Copy link
Member

/sponsor

@openjdk
Copy link

openjdk bot commented Apr 9, 2025

Going to push as commit a1d566c.
Since your change was applied there have been 28 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 Apr 9, 2025
@openjdk openjdk bot closed this Apr 9, 2025
@openjdk openjdk bot removed ready Pull request is ready to be integrated rfr Pull request is ready for review sponsor Pull request is ready to be sponsored labels Apr 9, 2025
@openjdk
Copy link

openjdk bot commented Apr 9, 2025

@TobiHartmann @marc-chevalier Pushed as commit a1d566c.

💡 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-compiler hotspot-compiler-dev@openjdk.org integrated Pull request has been integrated

Development

Successfully merging this pull request may close these issues.

3 participants