Skip to content

Optimized (non-relocatable) path: exported leaf clobbers callee-saved r4-r8 without save/restore (potential AAPCS violation) #470

Description

@avrabe

Found while scoping VCR-RA-002 (#428). The optimized (non-relocatable) codegen path emits an exported leaf function that uses callee-saved registers r4-r8 as scratch and returns via bx lr with no push/pop — so an AAPCS caller's r4-r8 are corrupted across the call.

Repro (generic fixture)

(module (memory 1)
  (func (export "leaf3") (param $p i32) (result i32)
    (local $a i32) (local $b i32) (local $c i32)
    (local.set $a (i32.add (local.get $p) (i32.const 1)))
    (local.set $b (i32.add (local.get $a) (i32.const 2)))
    (local.set $c (i32.add (local.get $b) (i32.const 3)))
    (i32.add (i32.add (local.get $a) (local.get $b))
             (i32.add (local.get $c) (local.get $p)))))
synth compile leaf3.wat -o /tmp/leaf3.elf -b arm --target cortex-m4 --all-exports
arm-none-eabi-objdump -d -M force-thumb /tmp/leaf3.elf   # see <leaf3>

Output (abridged) — leaf3 is a global symbol:

leaf3:
  movs r7,#1 ; add.w r8,r0,r7 ; mov r4,r8 ; ... uses r4,r5,r6,r7,r8 ...
  mov r0,ip
  bx lr            <-- no push {r4-r8,lr} / pop {..,pc}

Scope / what it is NOT

Open questions (confirm before fixing)

  1. Is the non-relocatable path a whole-program/self-contained model where synth controls every caller and exported entry points are only reached from the vector table / runtime that doesn't rely on r4-r8 preservation? If so this is benign by construction and should be documented as such.
  2. If exported functions can be reached by external AAPCS code (host-link / native-pointer ABI), this corrupts caller state and needs the prologue restored.
  3. Contrast: a non-leaf (with a call) on this same path reportedly does push {r4-r8,lr} — what determines the push, and why is the leaf case skipped?

Filing for investigation, not asserting a fix — needs (1) resolved first.

— issue-hunt loop (avrabe automation), not a maintainer decision.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions