Skip to content

arm backend: every wasm call lowered to __meld_dispatch_import placeholder — no inlining, no relocations, non-linkable ELF #167

@avrabe

Description

@avrabe

Summary

On synth v0.11.0 (53fd41d1), the arm backend lowers every wasm call — both internal (callee defined in the same module) and imported — to an identical __meld_dispatch_import placeholder bl (f000 d000, decoding to a garbage 0xC00000+offset target). It never:

  • inlines a known internal callee,
  • resolves an internal call to a direct PC-relative bl, nor
  • emits an ELF relocation against the callee symbol.

synth compile reports Relocatable ELF: N functions, 0 external symbols, 0 relocations regardless of how many calls the module makes. The resulting ELF is non-functional / non-linkable for any module that contains a function call: the call site branches to an address that nothing ever patches.

This blocks the cross-language-LTO-via-wasm route (wasm-ld merge → synth → ARM ET_REL linked into a Zephyr build): the merged module's z_impl_k_sem_give → gale_k_sem_give_decide seam and its 5 kernel imports all become unrelocated placeholders, so the .o cannot be linked into firmware.

It also appears consistent with the synth backends table, which marks the arm backend ELF: no (vs riscv/w2c2 = yes).

Minimal reproduction (no imports, 2 functions)

(module
  (func $callee (param i32) (result i32)
    local.get 0  i32.const 7  i32.add)
  (func $caller (export "caller") (param i32) (result i32)
    local.get 0  call $callee  i32.const 2  i32.mul)
  (export "callee" (func $callee)))
synth compile mini.wat --target cortex-m4f --all-exports --relocatable -o mini.o
# INFO Relocatable ELF: 2 functions, 0 external symbols, 0 relocations
arm-zephyr-eabi-objdump -d mini.o

caller's internal call $callee (callee is at 0x0 in the same object):

00000008 <caller>:
   8:  f000 d000   bl  c0000c <caller+0xc00004>   ; <- placeholder, target is garbage
   c:  2402        movs r4, #2
   e:  fb00 f504   mul.w r5, r0, r4
  12:  4628        mov  r0, r5
  14:  4770        bx   lr

arm-zephyr-eabi-objdump -r mini.ono relocations.

Not specific to --relocatable

The same placeholder appears in a full --cortex-m binary, where callee has a fixed known address 0xa0:

000000a0 <callee>: ...
000000a8 <caller>:
  a8:  f000 d000   bl  c000ac <caller+0xc00004>   ; should be `bl a0`

So the issue is in ARM call lowering, not ELF emission alone — synth has the callee's address/symbol and still emits the dispatch placeholder.

Expected

For a static-link / standalone target one of:

  1. internal calls → resolved direct bl (callee address known at layout time), or
  2. internal/imported calls → proper R_ARM_THM_CALL relocations against the callee/import symbol, so ld (or the host build) can resolve them.

(The __meld_dispatch_import runtime-dispatch model is presumably intended for the kiln/meld embedded host runtime — issue #34 — but it should not be the only lowering, or there should be a flag to select classic direct/relocated calls for --relocatable / --cortex-m outputs.)

Context / regression note

synth v0.3.0 produced linkable ARM objects for this exact pipeline (a merged gale-ffi module statically linked into a Zephyr bench built + flashed + began executing on NUCLEO-G474RE silicon). On v0.11.0 every call is a dispatch placeholder, so the route no longer links. If meld-dispatch became the default call-lowering between v0.3.0 and v0.11.0, a flag to opt back into direct/relocated calls would unblock the static-link use case.

Environment

  • synth v0.11.0 (53fd41d1), arm backend, default optimization
  • target cortex-m4f
  • objdump: Zephyr SDK 1.0.1 arm-zephyr-eabi-objdump (binutils 2.43.1)

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    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