RFR: 8371674: C2 fails with Missed optimization opportunity in PhaseIterGVN for MoveL2D

Benoît Maillard bmaillard at openjdk.org
Thu Nov 13 13:28:16 UTC 2025


This PR addresses yet another missed optimization in `PhaseIterGVN`. The way this optimization is triggered is a bit different this time though, and the notification is missing in `Node::has_special_unique_user`.

## Analysis

The affected optimization is the transformation of `MoveX2Y (LoadX mem)` into `LoadY mem`. This is implemented in `MoveNode::Ideal`. The optimization is as follows:
```c++
// Fold reinterpret cast into memory operation:
//    MoveX2Y (LoadX mem) => LoadY mem
LoadNode* ld = in(1)->isa_Load();
if (ld != nullptr && (ld->outcnt() == 1)) { // replace only
  const Type* rt = bottom_type();
  if (ld->has_reinterpret_variant(rt)) {
    if (phase->C->post_loop_opts_phase()) {
      return ld->convert_to_reinterpret_load(*phase, rt);
    } else {
      // attempt the transformation once loop opts are over
      phase->C->record_for_post_loop_opts_igvn(this);
    }
  }
}


The optimization is triggered only if the input is a `LoadNode` and the `MoveNode` is its only user. This is a relatively unusual pattern.

The bug was found by the fuzzer. At some point during IGVN, we have the following subgraph:


CountedLoop    LoadL
          \     /  \
            Phi    MoveL2D


In `RegionNode::Ideal`, we end up calling `set_req_X` on the `Phi` node to delete the edge from the `Phi` node to `LoadL`. As a result, the `LoadL` node only has one user left, and the `MoveNode::Ideal` gets triggered at the next verification pass. 

## Proposed Solution

The solution is to add this particular case to `Node::has_special_unique_user`, which gets called by `Node::set_req_X`. 

## Summary of changes

This PR brings the following changes:
- Detect the optimization pattern in `Node::has_special_unique_user`
- Add new test `TestMissingOptMoveX2YLoadX.java`, initially obtained from the fuzzer and then heavily reduced, both with the usual tools and manually. I tried to get a reproducer for each of the `Move` nodes, but I was only able to get one for `MoveL2D`

### Testing
- [x] [GitHub Actions](TODO) TODO
- [x] tier1-3, plus some internal testing (TODO)

Thank you for reviewing!

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

Commit messages:
 - Bring back array declaration
 - Add new reduced fuzzer test
 - Add notification in Node::has_special_unique_user

Changes: https://git.openjdk.org/jdk/pull/28290/files
  Webrev: https://webrevs.openjdk.org/?repo=jdk&pr=28290&range=00
  Issue: https://bugs.openjdk.org/browse/JDK-8371674
  Stats: 64 lines in 2 files changed: 62 ins; 0 del; 2 mod
  Patch: https://git.openjdk.org/jdk/pull/28290.diff
  Fetch: git fetch https://git.openjdk.org/jdk.git pull/28290/head:pull/28290

PR: https://git.openjdk.org/jdk/pull/28290


More information about the hotspot-compiler-dev mailing list