-
Notifications
You must be signed in to change notification settings - Fork 5.8k
8266074: Vtable-based CHA implementation #3727
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
Conversation
👋 Welcome back vlivanov! A progress list of the required criteria for merging this PR into |
/label add hotspot-compiler |
@iwanowww |
6e0836c
to
7d94997
Compare
7d94997
to
3063f97
Compare
Webrevs
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In general looks good. I have few comments.
@@ -32,13 +32,15 @@ | |||
* @run driver jdk.test.lib.helpers.ClassFileInstaller sun.hotspot.WhiteBox | |||
* | |||
* @run main/othervm -Xbootclasspath/a:. -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockDiagnosticVMOptions | |||
* -XX:+UseVtableBasedCHA |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
UseVtableBasedCHA
is true
by default. May be duplicate these runs
for both cases: on and off.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The test assumes UseVtableBasedCHA
is on and some of the assertions it makes fail with -XX:-UseVtableBasedCHA
. So, it would require all the changes in the test logic have to be guarded by a check whether UseVtableBasedCHA is on or off.
Considering UseVtableBasedCHA
is turned on by default in the PR, I don't see much value in complicating the test.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So only new implementation is tested with these changes in test. Got it. May be use:
@requires vm.opt.final.UseVtableBasedCHA == true
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm fine with both approaches.
Explicitly setting the flag looked to me more robust and clearer communicating the intent. But if you prefer @requires
, I'll use it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let hear @iignatev opinion.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
from my point of view, @requires
is clearer and also eliminates "wasted" execution (if someone tries to run this test w/ -XX:-UseVtableBasedCHA
), so I'd prefer if we use it.
I have a more generic comment about UseVtableBasedCHA
. I understand the desire to introduce a flag to switch back to the old implementation, but I'm somewhat concern that it adds a new dimension into configuration space that won't be covered by our existing tests (w/ the test which exercises interesting parts of the related code is inapplicable) and isn't part of our regular test configurations. Can we make it an experimental flag (w/ vtable-based CHA still being enabled by default)? this way, the quality bar for the old implementation will be somewhat lower, yet the end-users will still be able to return to the old implementation if it, for some reason, works better in their use-cases.
-- Igor
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a major bit of stewardship. Here's a bit of old history: The approximate dependencies walk code was hacked in the earliest days, for devirtualization—a new and cool thing back then. At the same time the vtable code was hacked in, and both sets of logic were hacked until the bugs went away. It took a while for all of us to fully understand the VM we were building. (This is why we had to add ACC_SUPER and class loader dependencies constraints, for example.) A couple engineers cleaned up the vtable stuff to its present state, and I and others cleaned up the CHA logic to its present state, before you touched it. It’s great to see those two bodies of code converging; thank you very very much.
Reviewed!
One question, which the review doesn't depend on: Are get getting closer to being able to apply CHA to interfaces? Unique-implementor and unique-method optimizations on interfaces are important. I ask because of some comments that throw shade on interfaces in the unit tests.
bool implements_interface; // initialized by method_at_itable_or_null() | ||
selected_method = recv_klass->method_at_itable_or_null(_declaring_klass, _vtable_index, | ||
implements_interface); // out parameter | ||
assert(implements_interface, "not implemented"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking at recv_klass->method_at_itable_or_null
, I wonder if there can be “holes” in the itable for missing methods. They would lead to AME
if called. They might also trigger your assert here: assert(implements_interface, "not implemented")
. Is there some reason that select_method
cannot possibly encounter a missing method?
Answer to self: I don't remember whether the JVM creates itable methods on the fly, but I suppose it does, so the code would see an synthetic abstract method. (Decades ago we named those Miranda Methods because if you don't have a responding method "one will be provided for you".) And itables are just aliases of vtable slices, so the miranda placed in the vtable will be seen also in the itable.
(Overall comment on this area of the code: It looks great, much better than when I touched it last. Thanks!)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Regarding "holes" in itables: yes, it happens in practice. select_method
is allowed to return a NULL
and it is used by LinkedConcreteMethodFinder
as a sentinel value for AME
(when placed in _found_methods
array). That's why Dependencies::find_unique_concrete_method()
check participant(0)
:
Method* fm = wf.found_method(0); // Will be NULL if num_parts == 0.
Klass* p = wf.participant(0); // Will be NULL if num_parts == 0.
...
if (Dependencies::is_concrete_method(m, ctxk)) {
if (fm == NULL && p == NULL) {
// It turns out that m was always the only implementation.
fm = m;
}
Now I think that it's not clear enough from the code. I'll elaborate on it in the additional comments.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Regarding the assert: the itable should always be found. First, because only implementors are enumerated during the analysis (currently, subclasses of the unique direct implementor). Second, because itable stubs rely on itable contents when performing receiver subtype checks. So, all superinterfaces should have a corresponding itable present (even an empty one).
@iwanowww 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:
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 257 new commits pushed to the
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 |
Yes! Though it is always the case now that any interface method case can be strength-reduced to a virtual call (due to unique implementor constraint), I deliberately kept interface dispatch support in place. Once there's a reliable way to enumerate all implementors, it becomes straightforwad to apply CHA when interface type information can be trusted. And, as a first step, it would be very interesting to experiment with sealed interface support . |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good.
Thanks for the reviews, John, Vladimir, and Dean. /integrate |
@iwanowww Since your change was applied there have been 264 commits pushed to the
Your commit was automatically rebased without conflicts. Pushed as commit 127bfe4. 💡 You may see a message that your pull request was closed with unmerged commits. This can be safely ignored. |
Mailing list message from David Holmes on hotspot-dev: On 2/05/2021 1:39 am, Igor Ignatyev wrote:
Did you mean "experimental" in a generic sense or actually change it Cheers, |
1 similar comment
Mailing list message from David Holmes on hotspot-dev: On 2/05/2021 1:39 am, Igor Ignatyev wrote:
Did you mean "experimental" in a generic sense or actually change it Cheers, |
Mailing list message from Igor Ignatev on hotspot-dev: Hi David, I meant both: in a generic sense, meaning we won't properly document, advertise it or claim it as supported; and changing its type to EXPERIMENTAL, so it will be somewhat harder for people to switch it. -- Igor On May 1, 2021, at 3:03 PM, David Holmes <david.holmes at oracle.com<mailto:david.holmes at oracle.com>> wrote: On 2/05/2021 1:39 am, Igor Ignatyev wrote: Explicitly setting the flag looked to me more robust and clearer communicating the intent. But if you prefer `@requires`, I'll use it. Let hear @iignatev opinion. Did you mean "experimental" in a generic sense or actually change it from DIAGNOSTIC to EXPERIMENTAL? If the latter then I don't agree this is an experimental flag, it is diagnostic. But either way the testing requirements are the same if we expect to tell end users to try this flag if they hit an problem - the flag has to be known to be functional, so we will have to expand the test coverage. Cheers, -- Igor |
1 similar comment
Mailing list message from Igor Ignatev on hotspot-dev: Hi David, I meant both: in a generic sense, meaning we won't properly document, advertise it or claim it as supported; and changing its type to EXPERIMENTAL, so it will be somewhat harder for people to switch it. -- Igor On May 1, 2021, at 3:03 PM, David Holmes <david.holmes at oracle.com<mailto:david.holmes at oracle.com>> wrote: On 2/05/2021 1:39 am, Igor Ignatyev wrote: Explicitly setting the flag looked to me more robust and clearer communicating the intent. But if you prefer `@requires`, I'll use it. Let hear @iignatev opinion. Did you mean "experimental" in a generic sense or actually change it from DIAGNOSTIC to EXPERIMENTAL? If the latter then I don't agree this is an experimental flag, it is diagnostic. But either way the testing requirements are the same if we expect to tell end users to try this flag if they hit an problem - the flag has to be known to be functional, so we will have to expand the test coverage. Cheers, -- Igor |
Mailing list message from David Holmes on hotspot-dev: On 2/05/2021 2:00 pm, Igor Ignatev wrote:
Both forms are as hard to switch to as both must be unlocked. But Regardless, if the intent is to allow the flag to be used to restore Cheers,
|
Mailing list message from Vladimir Ivanov on hotspot-dev: Thanks for your feedback, Igor and David.
I agree.
Yes, it makes perfect sense to test that -XX:-UseVtableBasedCHA is usable. I'd like to point out that new implementation exercise old So, maybe just a smoke test is enough here. Best regards, [1] src/hotspot/share/code/dependencies.cpp: Method* Dependencies::find_unique_concrete_method(InstanceKlass* ctxk,
|
Mailing list message from David Holmes on hotspot-dev: On 2/05/2021 2:00 pm, Igor Ignatev wrote:
Both forms are as hard to switch to as both must be unlocked. But Regardless, if the intent is to allow the flag to be used to restore Cheers,
|
Mailing list message from Vladimir Ivanov on hotspot-dev: Thanks for your feedback, Igor and David.
I agree.
Yes, it makes perfect sense to test that -XX:-UseVtableBasedCHA is usable. I'd like to point out that new implementation exercise old So, maybe just a smoke test is enough here. Best regards, [1] src/hotspot/share/code/dependencies.cpp: Method* Dependencies::find_unique_concrete_method(InstanceKlass* ctxk,
|
As of now, Class Hierarchy Analysis (CHA) employs an approximate algorithm to enumerate all non-abstract methods in a class hierarchy.
It served quite well for many years, but it accumulated significant complexity
to support different corner cases over time and inevitable evolution of the JVM
stretched the whole approach way too much (to the point where it become almost
impossible to extend the analysis any further).
It turns out the root problem is the decision to reimplement method resolution
and method selection logic from scratch and to perform it on JVM internal
representation. It makes it very hard to reason about correctness and the
implementation becomes sensitive to changes in internal representation.
So, the main motivation for the redesign is twofold:
Though I did experiment with relaxing existing constraints (e.g., enable default method support),
any possible enhancements are deliberately kept out of scope for the current PR.
(It does deliver a bit of minor enhancements front as the changes in
compiler/cha/StrengthReduceInterfaceCall.java manifest, but it's a side effect
of the other changes and was not the goal of the current work.)
Proposed implementation (
LinkedConcreteMethodFinder
) mimics method invocationand relies on vtable/itable information to detect target method for every
subclass it visits. It removes all the complexity associated with method
resolution and method selection logic and leaves only essential logic to prepare for method selection.
Vtables are filled during class linkage, so new logic doesn't work on not yet linked classed.
Instead of supporting not yet linked case, it is simply ignored. It is safe to
skip them (treat as "effectively non-concrete") since it is guaranteed there
are no instances created yet. But it requires VM to check dependencies once a
class is linked.
I ended up with 2 separate dependency validation passes (when class is loaded
and when it is linked). To avoid duplicated work, only dependencies
which may be affected by class initialization state change
(
unique_concrete_method_4
) are visited.(I experimented with merging passes into a single pass (delay the pass until
linkage is over), but it severely affected other class-related dependencies and
relevant optimizations.code.)
Compiler Interface (CI) is changed to require users to provide complete information about the call site being analyzed.
Old implementation is kept intact for now (will be removed later) to:
Testing:
-XX:-UseVtableBasedCHA
Thanks!
Progress
Issue
Reviewers
Reviewing
Using
git
Checkout this PR locally:
$ git fetch https://git.openjdk.java.net/jdk pull/3727/head:pull/3727
$ git checkout pull/3727
Update a local copy of the PR:
$ git checkout pull/3727
$ git pull https://git.openjdk.java.net/jdk pull/3727/head
Using Skara CLI tools
Checkout this PR locally:
$ git pr checkout 3727
View PR using the GUI difftool:
$ git pr show -t 3727
Using diff file
Download this PR as a diff file:
https://git.openjdk.java.net/jdk/pull/3727.diff