[lworld] RFR: 8367245: [lworld] C2 compilation fails with "Missed optimization opportunity in PhaseIterGVN"
Marc Chevalier
mchevalier at openjdk.org
Tue Nov 4 17:22:12 UTC 2025
On Tue, 4 Nov 2025 14:02:10 GMT, Marc Chevalier <mchevalier at openjdk.org> wrote:
> # Analysis
> ## Obervationally
> ### IGVN
> During IGVN, in `PhiNode::Value`, a `PhiNode` has 2 inputs. Their types are:
>
> in(1): java/lang/Object * (speculative=compiler/valhalla/inlinetypes/MyValue2 (compiler/valhalla/inlinetypes/MyInterface):exact * (inline_depth=4))
> in(2): java/lang/Object * (speculative=null)
>
> We compute the join (HS' meet):
> https://github.com/openjdk/valhalla/blob/412ec882767d3ee1792d1e0f98da54ff800c60ce/src/hotspot/share/opto/cfgnode.cpp#L1310-L1317
>
> t=java/lang/Object * (speculative=compiler/valhalla/inlinetypes/MyValue2 (compiler/valhalla/inlinetypes/MyInterface):exact *)
>
> But the current `_type` (of the `PhiNode` as a `TypeNode`) is
>
> _type=java/lang/Object * (speculative=compiler/valhalla/inlinetypes/MyValue3 (compiler/valhalla/inlinetypes/MyInterface):exact *)
>
> We filter `t` by `_type`
> https://github.com/openjdk/valhalla/blob/412ec882767d3ee1792d1e0f98da54ff800c60ce/src/hotspot/share/opto/cfgnode.cpp#L1332
> and we get
>
> ft=java/lang/Object *
>
> which is what we return. After the end of `Value`, the returned becomes the new `PhiNode`'s `_type`.
> https://github.com/openjdk/valhalla/blob/412ec882767d3ee1792d1e0f98da54ff800c60ce/src/hotspot/share/opto/phaseX.cpp#L2150-L2164
> and
> https://github.com/openjdk/valhalla/blob/412ec882767d3ee1792d1e0f98da54ff800c60ce/src/hotspot/share/opto/node.cpp#L1127-L1133
>
>
> ### Verification
> On verification, `in(1)`, `in(2)` have the same value, so does `t`. But this time
>
> _type=java/lang/Object *
>
> and so after filtering `t` by (new) `_type` and we get
>
> ft=java/lang/Object * (speculative=compiler/valhalla/inlinetypes/MyValue2 (compiler/valhalla/inlinetypes/MyInterface):exact *)
>
> which is retuned. Verification gets angry because the new `ft` is not the same as the previous one.
>
> ## But why?!
> ### Details on type computation
> In short, we are doing
>
> t = typeof(in(1)) / typeof(in(2))
> ft = t /\ _type (* IGVN *)
> ft' = t /\ ft (* Verification *)
>
> and observing that `ft != ft'`. It seems our lattice doesn't ensure `(a /\ b) /\ b = a /\ b` which is problematic for this kind of verfication that will just "try again and see if something change".
>
> To me, the surprising fact was that the intersection
>
> java/lang/Object * (speculative=compiler/valhalla/inlinetypes/MyValue2 (compiler/valhalla/inlinetypes/MyInterface):exact *)
> /\
> _type=java/lang/Object * (speculative=compiler/valhalla/inlinetypes/MyValue3 (compiler/valhalla/inlinetypes/MyInterface):exact *)
> ~>
> java/lang/Object *
>
> What happened to the speculative type? Both `MyVal...
I don't think this is quite correct. At first, we have
in(1): java/lang/Object * (speculative=compiler/valhalla/inlinetypes/MyValue3 [...]
in(2): java/lang/Object * (speculative=null)
which make `_type` to be
java/lang/Object * (speculative=compiler/valhalla/inlinetypes/MyValue3 (compiler/valhalla/inlinetypes/MyInterface):exact *)
(which I'm not arguing with: it seems correct at that point).
Then, some incremental inlining happen, the types of inputs change to
in(1): java/lang/Object * (speculative=compiler/valhalla/inlinetypes/MyValue2 (compiler/valhalla/inlinetypes/MyInterface):exact * (inline_depth=4))
in(2): java/lang/Object * (speculative=null)
(`MyValue3` is replaced by `MyValue2` in the type of `in(1)`) and from this point, we run `PhiNode::Value` (since indeed, the input changed) with the previously computed `_type` involving `MyValue3`.
In this invocation of `Value` (the last non-verification invocation), we compute `(typeof(in(1)) / typeof(in(2))) /\ _type` which gives `java/lang/Object *` (dropping the speculative type). This type becomes the `_type` of the `PhiNode`.
After that, we do verification (so no change to the inputs anymore), and we re-call `PhiNode::Value`. We recompute `(typeof(in(1)) / typeof(in(2))) /\ _type` with this new `_type` and here we get
java/lang/Object * (speculative=compiler/valhalla/inlinetypes/MyValue2 (compiler/valhalla/inlinetypes/MyInterface):exact *)
It's not the same as before, so it's a verification failure.
----
As, for why the meet of
in(1): java/lang/Object * (speculative=compiler/valhalla/inlinetypes/MyValue2 (compiler/valhalla/inlinetypes/MyInterface):exact * (inline_depth=4))
in(2): java/lang/Object * (speculative=null)
is
java/lang/Object * (speculative=compiler/valhalla/inlinetypes/MyValue2 (compiler/valhalla/inlinetypes/MyInterface):exact *)
maybe there is a misunderstanding (that I had myself): null here means Java's null, not C++ nullptr. So, basically, the Phi is either null or a MyValue2 (possibly null). It seems natural that the (Hotspot's) meet is the same as `in(1)`, since `typeof(in(1)) < typeof(in(2))`. And I've double checked, this meet really happens, both Region's inputs are non-top. The lack of speculative type is displayed by simply omitting the "speculative=" part.
-------------
PR Comment: https://git.openjdk.org/valhalla/pull/1717#issuecomment-3487177477
More information about the valhalla-dev
mailing list