8381546: C2: Improve static checks on SubTypeCheck#30690
Conversation
|
👋 Welcome back ghan! A progress list of the required criteria for merging this PR into |
|
❗ This change is not yet ready to be integrated. |
Webrevs
|
|
The total number of required reviews for this PR has been set to 2 based on the presence of this label: |
|
|
||
| @Run(test = "test1") | ||
| boolean runTest() { | ||
| Object[] arr = new Object[] { new C(), new D() }; |
There was a problem hiding this comment.
Wow, this is quite a test! I suppose new C() in the array is there to "force" C to be loaded, right?
There was a problem hiding this comment.
Hi @dafedafe, yes , new C() is there to make sure the compiler sees that B has a loaded final subclass.
| if (!improved_ik->has_subklass()) { | ||
| if (!improved_other->_interfaces->contains(this_one->_interfaces)) { | ||
| if (!improved_ik->is_final()) { | ||
| Compile::current()->dependencies()->assert_leaf_type(improved_ik); |
There was a problem hiding this comment.
It would be nice to have a test that checks when a later class loading breaks the leaf assumption, but maybe we already have a more generic one...
There was a problem hiding this comment.
That's a good point. However, this patch only reuses the existing assert_leaf_type() dependency mechanism, so I did not add a separate test for later class loading. That should already be covered by the generic dependency tests.
|
Can you improve the static checks of |
iwanowww
left a comment
There was a problem hiding this comment.
I agree with @merykitty that it would be better to unify SubTypeCheck analysis with receiver type computation. Or, at least, verify that both agree.
| ((this_is_subtype && (other->klass() == this_one->klass())) || !this_is_subtype)) { | ||
| const TypeKlassPtr* otherk = other->isa_klassptr() ? other->is_klassptr() : | ||
| other->is_oopptr()->as_klass_type(); | ||
| const TypeKlassPtr* improved_other = otherk->try_improve(); |
There was a problem hiding this comment.
It's the wrong place to improve the type. The culprit is that while GraphKit::gen_checkcast() tries to improve the type, GraphKit::gen_instanceof() does not which is a missed optimization opportunity. IMO it's better to shape it as a separate RFE.
Hi @merykitty and @iwanowww , Sorry for late response. I have recently explored the possibility of unifying the SubTypeCheck analysis with receiver type computation (using filter() or higher_equal and so on). However, my findings suggest that a complete unification is difficult, Specifically, I’d like to highlight two scenarios where type computation based logic fails to align with the requirements of static_subtype_check 1.Risk of Missing SSC_always_true Cases When using type lattice operations to determine SSC_always_true, such as: or This logic frequently triggers assertions in SubTypeCheckNode::verify_helper. The detailed error log as below: The value of cmp_t in SubTypeCheckNode::verify_helper is TypeInt::CC_EQ. This implies that type lattice operations failed to capture some cases that should be SSC_always_true. However, this case is benign for CheckCastPP, as it just miss a chance of narrowing type. 2. The Risk of Wrongly Deleting Branches Using filter() == Type::TOP to return SSC_always_false is risky . Consider the following: filter() operates based on the currently loaded classes. filter() will returns Type::TOP when sub_t (e.g., Object with {I1, I2}) and super_t (e.g., Object with {I3, I4, I5}) share no common subtypes among currently loaded classes. Consequently, the corresponding branch would be statically deleted. However, the JVM may later load a new class that implements all interfaces {I1, I2, I3, I4, I5}. To my knowledge, there is no existing Dependency mechanism to detect such a class loading event and trigger the necessary deoptimization to correct the compiled code . While I am aware of existing dependency types like assert_leaf_type, they are not suitable for this specific case. However, this case is benign for CheckCastPP. I would appreciate your thoughts on whether we should accept this divergence as a design necessity or if you see a viable path for a safe, dependency-backed unification. Thanks! |
|
I can only guess how the patch looked like, but there are 2 peculiarities when it comes to
In the latter case, it's not correct to use I made an experiment [1] and spotted only a single divergence with [1] https://github.com/openjdk/jdk/compare/master...iwanowww:jdk:sub_type?expand=1 |
|
|
|
The patch has been updated with the following changes:
@iwanowww Could you please take a look? |
Looking at the dump, I'm fairly certain you are checking the incorrect thing. The correct thing to check is to verify that the type of an oop that would satisfy the
No, it is not benign for |
|
@hgqxjj Thanks for looking into it.
I suggest to upstream it separately.
Are you talking about JDK-8383823? It's better to fix it separately as well. |
|
I have been playing with the prototype [1] in background and got some more insights.
|
|
|
||
| bool subk_e_higher = subk_e->higher_equal(superk_e); | ||
|
|
||
| if (subk_e_higher && (superk_is_exact || superk_e->klass_is_exact())) { |
There was a problem hiding this comment.
Why does superk_e->klass_is_exact() part make a difference?
There was a problem hiding this comment.
My understanding is that superk_e->klass_is_exact() can become true while superk_is_exact is still false when a non-final super klass currently has no subclasses. In that case, superk_e->klass_is_exact() together with subk_e_higher proves that the subtype check is always true.
There was a problem hiding this comment.
Right, I was thinking about whether superk_e->klass_is_exact() implies superk_is_exact == true, but it's evidently not the case. (Forgot that superk_is_exact is captured on the original superk before its "constness" is casted away.)
OK, I will split them out and upstream them separately.
Thank you for the explanation. I will continue refining these changes |
@merykitty Yes, that makes sense. I misunderstood some details here, thank you for the clarification. I will refine the changes with these points in mind. |
Description:
There are scenarios where SubTypeCheckNode was not eliminated when receiver implements an interface not related to the class being checked against.
Solution:
This PR enhances maybe_java_subtype_of_helper_for_instance by using CHA to improve the target type and verify it against the receiver's interface constraints. If the improved leaf type does not implement the required interfaces, the relationship is proven impossible and the node is folded.
Test:
GHA
Please review this change. Thanks!
Progress
Issue
Reviewing
Using
gitCheckout this PR locally:
$ git fetch https://git.openjdk.org/jdk.git pull/30690/head:pull/30690$ git checkout pull/30690Update a local copy of the PR:
$ git checkout pull/30690$ git pull https://git.openjdk.org/jdk.git pull/30690/headUsing Skara CLI tools
Checkout this PR locally:
$ git pr checkout 30690View PR using the GUI difftool:
$ git pr show -t 30690Using diff file
Download this PR as a diff file:
https://git.openjdk.org/jdk/pull/30690.diff
Using Webrev
Link to Webrev Comment