RFR: 8371716: C2: Phi node fails Value()'s verification when speculative types clash [v3]
Emanuel Peter
epeter at openjdk.org
Tue Nov 25 10:05:14 UTC 2025
On Thu, 20 Nov 2025 16:56:21 GMT, Roland Westrelin <roland at openjdk.org> wrote:
>> I'm not sure if I'm correct, but I think speculative types themselves may not be consistent. For example, if they are consistent, then you will expect that the profiled types of the return values of a method `a` when calling from method `b` would be a subset of the profiled types of the returned values of `a` in general. However, this may not be the case, as we can ask for the second information first, then another type is introduced, then suddenly a method seems not to return a type `C`, but it does seem to return `C` if calling from `b`. As a result, maybe we can abandon trying to verify the correctness of speculative type computations.
>>
>> Additionally, in the test case, the speculative type being empty is correct, the path is speculatively unreachable, maybe we can use that information to cut off the branches, simplify the CFG for better compilation?
>
>> Additionally, in the test case, the speculative type being empty is correct, the path is speculatively unreachable, maybe we can use that information to cut off the branches, simplify the CFG for better compilation?
>
> Attached is another test case that reproduces the same issue.
> [TestSpeculativeTypes.java](https://github.com/user-attachments/files/23659130/TestSpeculativeTypes.java)
>
> I run that one with:
>
> $ for i in `seq 100`; do java -XX:-TieredCompilation -XX:-UseOnStackReplacement -XX:-BackgroundCompilation -XX:CompileOnly=TestSpeculativeTypes::test2 -XX:CompileOnly=TestSpeculativeTypes::inlined3 -XX:CompileCommand=quiet -XX:TypeProfileLevel=200 -XX:+AlwaysIncrementalInline -XX:VerifyIterativeGVN=10 -XX:CompileCommand=dontinline,TestSpeculativeTypes::notInlined1 -XX:+StressIncrementalInlining TestSpeculativeTypes || break; done
>
> It usually fails after a few runs. That one has conflicting profile data but no dead path. What you're suggesting has some risk and unclear benefits so I think would need to be investigated separately.
@rwestrel @merykitty @marc-chevalier Suggestion:
- Marc adds Roland's new reproducer.
- Marc annotates both the existing and Roland/s new reproducer: show where the speculative types come from, and where they flow, and where the Phi with the conflicting speculative type is located.
- We keep the speculative type verification. Because I'm not convinced that we should just remove verification yet.
In future RFE's we could consider:
- Removing verification for speculative types. But I wonder if that is smart.
- Reconsider if we should really do the "widening" if we have `above_centerline`. As Roland said: there are risks to this. There must have been some historical reason for this. But who knows, maybe it hurts in more cases than it helps. Speculation is always based on a heuristic, and we would need a few benchmarks to show the impact, and we would also need to run some bigger benchmarks to see the impact. That is a lot of effort that would need to happen outside this bugfix. I suppose it is a tradeoff between code size (win if we cut paths) and cost of recompilation (if we fail speculative checks)?
--------------------------------
I suppose it depends on what our definition of "consistent" is for speculative types. Of course they can be wrong at runtime, that is the whole point of speculation, so in a sense that makes them "inconsistent". But we could at least enforce some "consistency" in the computation that we do with the speculative types: intersections and unions of types should be done "consistently", I suppose.
I'm trying to reconstruct Quan-Anh's definition of "consistent":
> For example, if they are consistent, then you will expect that the profiled types of the return values of a method a when calling from method b would be a subset of the profiled types of the returned values of a in general.
Does this kind of problem not also arise for non-speculative types, at least during CCP or IGVN? First I'm thinking of Casts, but maybe those are special. They can have narrower type than their input type. What about Phi nodes? During IGVN, do we always expect the input types to be a subtype of the output type? I guess so?
Observation: "widening" like that what happens here because of `above_centerline` and elsewhere like integer types with their "widening" (e.g. widening because loop phi continually grows the range) still means that input types are a subset of the output type.
So according to Quan-Anh's definition, we _start_ with an inconsistent speculative type. But would it not be nice if it _eventually became consistent_, specifically after CCP/IGVN. And we could already get local consistency after every `Value` call, right? If yes: it would be nice to have _verification_ for that.
-------------
PR Comment: https://git.openjdk.org/jdk/pull/28331#issuecomment-3574746918
More information about the hotspot-compiler-dev
mailing list