Skip to content

Spill-range trimming introduces stack traffic in call-free loops #271

Open

Description

@wraitii

issue for poor allocation for the following pseudocode:

acc = 0
n = input_count
while n > 0:
    n -= 1
    acc += 1
helper()
return acc

CLIF concrete example with a float accumulator on aarch64 (which spills):

function %one_loop_then_helper(i64, i64) -> f64 apple_aarch64 {
    sig0 = () apple_aarch64

block0(v0: i64, v2: i64):
    v3 = f64const 0.0
    jump block1(v0, v3)

block1(v4: i64, v5: f64):
    v6 = iconst.i64 0
    v7 = icmp sgt v4, v6
    brif v7, block2, block3

block2:
    v8 = f64const 0x1.0000000000000p0
    v9 = fadd v5, v8
    v10 = iconst.i64 1
    v11 = isub v4, v10
    jump block1(v11, v9)

block3:
    call_indirect sig0, v2()
    return v5
}

(Compile with clif-util compile --target aarch64-apple-darwin --set opt_level=speed --set regalloc_checker=true -p -D one-loop-then-helper.clif)

This produces an unnecessary accumulator store on every loop iteration, in addition to a save immediately before the helper call. A second variant, where the accumulator controls loop termination and the helper is called afterward, produces both a store and a reload on every iteration.

Tracing these cases shows one split near the helper call. Trimming then moves the end of the live ranges back into the loop, introducing repeated stack traffic.

I’m not an expert on this code, but I’ve investigated it with GPT-6 Astra. Two possible improvements seem worth discussing:

  • Stop trimming when it would enter a deeper loop than the split point, to avoid turning preservation outside a loop into per-iteration traffic.
  • Account for block-argument uses and block-parameter definitions during trimming, rather than consulting only the instruction-use lists.

A small patch implementing the loop-depth restriction fixes both examples (and I'll post that PR soon). I had Astra prototype accounting for block-argument uses, and it also fixes this issue, though it requires more substantial work.

Related to #268/#270 in symptom, but these reproductions do not trigger the split-limit fallback. Neither approach removes the inner-loop spills in #268’s attached reproducer.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions