RFR: 8364970: Redo JDK-8327381 by updating the CmpU type instead of the Bool type [v3]

Christian Hagedorn chagedorn at openjdk.org
Fri Aug 29 13:20:45 UTC 2025


On Thu, 14 Aug 2025 18:07:53 GMT, Francisco Ferrari Bihurriet <fferrari at openjdk.org> wrote:

>> Francisco Ferrari Bihurriet has updated the pull request incrementally with three additional commits since the last revision:
>> 
>>  - Improve the IR test to add the new covered cases
>>    
>>    I also checked the test is now failing in the master branch (at
>>    f95af744b07a9ec87e2507b3d584cbcddc827bbd).
>>  - Remove IR test inverted asserts
>>    
>>    According to my IGV observations, these inversions aren't necessarily
>>    effective. Also, I assume it is safe to remove them because if I apply
>>    this change to the master branch, the test still passes (tested at
>>    f95af744b07a9ec87e2507b3d584cbcddc827bbd).
>>  - Add requested comments from the reviews
>>    
>>    Add a comment with the BoolTest::cc2logical inferences tables, as
>>    suggested by @tabjy.
>>    
>>    Also, add a comment explaining how PhaseCCP::push_cmpu is handling
>>    grandparent updates in the case 1b, as agreed with @chhagedorn.
>
> # Absence note
> 
> Today is the last day before a ~2 weeks vacation, so my next working day is Monday, September 1st.
> 
> Please feel free to keep giving feedback and/or reviews, and I will continue when I'm back.
> 
> Cheers,
> Francisco

Hi @franferrax, hope you had a good vacation!

> Hi @chhagedorn,
> 
> I added the new tests in [e6b1cb8](https://github.com/openjdk/jdk/commit/e6b1cb897d9c75b34744c7d24f72abcec9986b0b). One problem I'm facing is that I'm unable to generate `Bool` nodes with arbitrary `BoolTest` values. Even if I try the assert inversions I removed in [10e1e3f](https://github.com/openjdk/jdk/commit/10e1e3f4f796d05dcd5c56bc2365d5d564d93952), C2 has preference for `BoolTest::ne`, `BoolTest::le` and `BoolTest::lt`. Instead of using `BoolTest::eq`, `BoolTest::gt` or `BoolTest::ge`, it swaps what is put in `IfTrue` and `IfFalse`.
> 
> Even if `javac` generates an `ifeq` and an `ifne` with the same inputs, instead of a single `CmpU` with two `Bool`s (`BoolTest::eq` and `BoolTest::ne`), I get a single `Bool` (`BoolTest::ne`) with two `If` (one of them swapping `IfTrue` with `IfFalse`). I guess this is some sort of canonicalization to enable further optimizations.
> 
> Do you know a way to influence the `Bool`'s `BoolTest` value? Or @rwestrel do you?
> 
> This means the following 8 cases are not really testing what they claim, but repeating other cases with `IfTrue` and `IfFalse` swapped:
> 
> * `testCase1aOptimizeAsFalseForGT(xm|mx)` (they should use `BoolTest::gt`, but use `BoolTest::le`)
> * `testCase1bOptimizeAsFalseForEQ(xm|mx)` (they should use `BoolTest::eq`, but use `BoolTest::ne`)
> * `testCase1bOptimizeAsFalseForGE(xm|mx)` (they should use `BoolTest::ge`, but use `BoolTest::lt`)
> * `testCase1bOptimizeAsFalseForGT(xm|mx)` (they should use `BoolTest::gt`, but use `BoolTest::le`)
> 
> Even if we don't find a way to influence the `BoolTest`, the cases are still valid and can be kept (just in case the described behaviour changes).

Hm, that's a good point. `Parse::do_if()` indeed always canonicalizes the `Bool` nodes... But I was sure we can still somehow end up with non-canonicalized versions again with some tricks. I was curious and played around with some examples and could indeed find test cases for `gt`, `ge` , and `eq`.

I was then also thinking about notification code in IGVN. We already concluded further up that it's not needed for CCP because `CmpU` nodes below `AddI` nodes are put to the worklist again. However, with IGVN, we could modify the graph above the `AndI` as well. We miss notification code for `CmpU` below `AndI`. I changed my test cases further to also run into such a missing optimization case. When run with `-XX:VerifyIterativeGVN=1110`, we indeed get such an assertion failure with the proposed patch (it also triggers an assertion failure already with mainline code). This could be easily fixed with:

diff --git a/src/hotspot/share/opto/phaseX.cpp b/src/hotspot/share/opto/phaseX.cpp
--- a/src/hotspot/share/opto/phaseX.cpp	(revision afa8e79ba1a76066cf969cb3b5f76ea804780872)
+++ b/src/hotspot/share/opto/phaseX.cpp	(date 1756472877934)
@@ -2553,7 +2553,7 @@
   if (use_op == Op_AndI || use_op == Op_AndL) {
     for (DUIterator_Fast i2max, i2 = use->fast_outs(i2max); i2 < i2max; i2++) {
       Node* u = use->fast_out(i2);
-      if (u->Opcode() == Op_RShiftI || u->Opcode() == Op_RShiftL) {
+      if (u->Opcode() == Op_RShiftI || u->Opcode() == Op_RShiftL || u->Opcode() == Op_CmpU) {
         worklist.push(u);
       }
     }

Here are the test cases with some further comments explaining how it works and how to run it:
[Test.java](https://github.com/user-attachments/files/22046942/Test.java)

 This will produce the following IR (at `PhaseIdealLoop1`):
<img width="658" height="167" alt="image" src="https://github.com/user-attachments/assets/293c5348-b4ef-4ae6-bf7d-c429e6002d3d" />
I guess you could easily transform these into IR tests and check that we have 4 `CmoveI/CmpU` nodes in `PhaseIdealLoop1` and then no more in `PhaseIdealLoop2`. What do you think?

-------------

PR Comment: https://git.openjdk.org/jdk/pull/26666#issuecomment-3237014744


More information about the hotspot-compiler-dev mailing list