On Wed, 26 Aug 2026 23:07:24 GMT, Boris Ulasevich <[email protected]>
wrote:
> A for-each loop over ArrayList.Itr carries two phases of one recurrence
> through the loop: cursor (after the increment) and lastRet (before it). Both
> are live at the same time, so the allocator puts a copying mov in the loop
> body - on every iteration, for a value the loop never reads. This can be
> dealt with at the Java level, see #32344, but this change is an attempt to
> improve it at the level of C2 compilation.
>
> In a loop compiled by C2, lastRet is never written to memory: the iterator is
> scalarized and the field lives in a register. In a plain for-each nobody
> reads it (it is only used by set() and remove()) so its only consumers are
> the Phi at the loop exit and the safepoint's debug info.
>
> C2 already knows how to get rid of a second index:
> PhaseIdealLoop::replace_parallel_iv looks for a variable that walks alongside
> the trip counter and rewrites its uses in terms of the counter.
> It only recognizes PARALLEL shape:
>
>
> PARALLEL: int a = 5; for (int iv = 0; iv < limit; iv++) { use(a); a +=
> 3; }
> PREVIOUS: int prev = -1; for (int iv = 0; iv < limit; iv++) { use(prev); prev
> = iv; }
>
>
> The patch teaches the recognition step the second (PREVIOUS) shape: a phi in
> the loop head whose backedge value is the trip counter plus a constant. Its
> uses are then rewritten in terms of the trip counter, the phi becomes dead
> code, and the copy in the loop body is no longer generated.
>
> With `-XX:LoopMaxUnroll=1` the resulting aarch64 assembly looks like the
> following:
>
> @Benchmark
> public void list_foreach(Blackhole bh) {
> for (Object o : list) {
> bh.consume(o);
> }
> }
>
> BEFORE AFTER
> mov w14, w11 -
> add x11, x10, w14, sxtw #2 add x12, x10, w16, sxtw #2
> ldr w11, [x11, #0xc] ldr w12, [x12, #0xc]
> lsl x11, x11, #3 add w16, w16, #0x1
> add w11, w14, #0x1 lsl x12, x12, #3
> cmp w11, w12 cmp w16, w14
> b.lt #-0x18 b.lt #-0x14
>
>
> Removing one instruction gives a speedup of up to 40% with
> `-XX:LoopMaxUnroll=1`, while with default VM options the picture is mixed: a
> 11–15% gain on some CPUs and nothing measurable on others.
>
> ---------
> - [x] I confirm that I make this contribution in accordance with the [OpenJDK
> Interim AI Policy](https://openjdk.org/legal/ai).
@bulasevich do you know why `replace_parallel_iv()` does not work for your case?
-------------
PR Comment: https://git.openjdk.org/jdk/pull/32549#issuecomment-5444148268