2.8 KiB
§NNN — WRITE THE UP-COUNT LOOP: gcc's OWN REVERSAL PRODUCES A COUNTER-INIT INSTRUCTION THAT LANDS AFTER HOISTED MOVABLES, WHICH A HAND-WRITTEN DOWN-COUNT LOOP CANNOT REPRODUCE (P31 S60; func_80180DD8, ov_SC04_005, byte-proven)
The symptom. A target loop counts DOWN from a constant with a bgez/addiu -1 backedge, and the
counter's addiu $reg,$zero,K initializer sits AFTER several other pre-loop constant loads the loop
hoists (e.g. switch-dispatch case constants). Every hand-written down-count C loop
(for (i = K; i >= 0; i--)) emits its counter init BEFORE those hoisted constants — an unclosable
off-by-position residual, because the down-count statement's LUID precedes loop_start's insertion
point for the movables loop.c hoists, no matter how the source is reordered.
The law. Write the loop as an UP-count in the source:
for (i = 0; i < N; i++) { ...switch(*p){...}... }. gcc-2.7.2's own loop-reversal machinery (the
strength-reduction pass that turns a provably-monotonic up-counting biv into a dbra-style
down-count when doing so is cheaper — a bare decrement-and-test-sign beats an increment-and-compare)
rewrites it into the down-count-from-(N-1)/bgez form ITSELF, as a LATE transformation. Because this
reversal happens after the movable-hoisting pass has already placed the loop's hoisted invariants in
the preheader, the new reversed-counter's init instruction is inserted where loop.c puts it — AFTER
the already-hoisted movables — reproducing the target's exact instruction order. A hand-written
down-count loop supplies its own counter-init statement at ordinary source-LUID position, which
predates the movable-hoisting insertion point, and gcc has no later pass that moves a source-level
down-count init downward past movables it did not itself just create.
Evidence. func_80180DD8 (ov_SC04_005, jr_8017BEBC, MATCH, 0x70 bytes / 28 ins). Source:
src/ov_SC04_005/ov_SC04_005_jr_8017BEBC.c:6237-6255 — plain for (i = 0; i < 0x60; i++). Target:
asm/ov_SC04_005/nonmatchings/ov_SC04_005_jr_8017BEBC/func_80180DD8.s lines 4-10 — four addiu loads
of the switch constants (0x1FC,0x1DC,0x210,0x219) at 80180DE4-80180DF0, THEN
addiu $a0,$zero,0x5F at 80180DF4 (the reversed counter init), loop body,
bgez $a0,.L80180DF8 / addiu $a0,$a0,-0x1 backedge at 80180E38-80180E3C. The sibling
func_80180E48 in the same TU (already matched, house style) independently confirms the up-count
spelling for this loop family.
When it applies. Target shows a down-count bgez/decrement loop whose counter-init constant sits
AFTER one or more other hoisted pre-loop constants, and no reordering of a hand-written down-count
loop's source statements relocates the init past them. No jump table need be present (this target's
dense 4-case switch folds to compares, not a jtbl).