RFR: 8307084: C2: Vectorized drain loop is not executed for some small trip counts [v4]
Emanuel Peter
epeter at openjdk.org
Fri Jan 16 09:14:59 UTC 2026
On Thu, 15 Jan 2026 16:06:21 GMT, Fei Gao <fgao at openjdk.org> wrote:
>>> * If we cover all phis: we should probably change the naming of the variables to indicate that it is any `phi` and not just the `iv`.
>>>
>>> What do you think?
>>
>> Yes, we’re looking at all variables that change as the loop iterates, not just the trip counter. How about renaming it to `main_exit_value` or `value_after_main_loop`?
>
>> Also: how sure are you that the backedge `main_phi->in(LoopNode::LoopBackControl)` is the same as the the value after main `iv_after_main`? What if we did some split-through-phi action at some point? Example:
>>
>> ```
>> x = ...
>> LOOP:
>> x = op(x);
>> // x now serves as exit value and backedge value
>> exit check;
>> goto LOOP;
>> ```
>>
>> If we split `op` through the LOOP Phi, we get:
>>
>> ```
>> x = ...
>> x = op(x);
>> LOOP:
>> // the exit value is the phi
>> exit check;
>> x = op(x);
>> // x after op is the backedge
>> goto LOOP;
>> ```
>>
>> I'm not sure this currently ever happens, but what if it did?
>
> Great question—and yes, that’s exactly why this logic is a bit more involved.
>
> If I understand correctly, in the second scenario—after a split-through-phi—the value we need after the main loop is not the original x directly. Instead, we also need to apply an additional op(x) so that the resulting value can serve as the input to the drain loop.
>
>
> x = ...
> y = op(x);
> MAIN_LOOP:
> // the exit value is the phi
> exit check;
> y = op(x);
> // x after op is the backedge
> goto MAIN_LOOP;
>
> MAIN_EXIT:
> y = op(x); // the newly created op(x) outside the main-loop body
>
> DRAIN_LOOP:
> // the exit value is the phi
> exit check;
> y = op(x);
> // x after op is the backedge
> goto DRAIN_LOOP;
>
>
>
> Let's walk through the code step by step.
>
>
> Node* drain_input = nullptr;
> Node* iv_after_main = main_phi->in(LoopNode::LoopBackControl);
>
> At this point, we do **not** assume that `iv_after_main` is already the correct “after-main” value.
>
> if (get_ctrl(iv_after_main) != main_backedge_ctrl) {
> drain_input = find_merge_phi_for_vectorized_drain(iv_after_main, main_merge_region);
> }
>
> We first check whether `main_phi->in(LoopNode::LoopBackControl)` is pinned in the backedge block.
>
> 1. If `get_ctrl(iv_after_main) != main_backedge_ctrl`, then `main_phi->in(LoopNode::LoopBackControl)` is **not** in the backedge block. It should be **either in the main loop body or in the main exit block.**
>
> - If it is still in the main loop body, we won’t be able to find a valid merge phi via its uses, so **`drain_input` remains nullptr**.
>
> - If it is already in the main exit block, we can find an existing valid merge phi via its uses—this corresponds to the first scenario you described, **`drain_input` won't be nullptr**.
>
> 2. If `get_ctrl(iv_after_main) == main_backedge_ctrl`, then the `main_phi->in(LoopNode::LoopBackControl)` is pinned in the b...
Ah, I see, so the magic is supposed to happen in `clone_up_backedge_goo`. Thanks for all the detailed explanations! I think I had missed the recursive approach.
I had just relied on the old comment above `clone_up_backedge_goo` that only talks about `n` and its direct clone. It fails to mention any of the recursive part, that walks the whole chain.
I think it would be worth spelling it out a bit more for `clone_up_backedge_goo`, and writing down why what it does is correct. I think that would help a lot, especially because you are now using it in a new way, and so we have to make sure things are correct, and it is easy for the reader to see why :)
-------------
PR Review Comment: https://git.openjdk.org/jdk/pull/22629#discussion_r2697596988
More information about the hotspot-compiler-dev
mailing list