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.o → no 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:
- internal calls → resolved direct
bl (callee address known at layout time), or
- 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)
Summary
On synth v0.11.0 (
53fd41d1), thearmbackend lowers every wasmcall— both internal (callee defined in the same module) and imported — to an identical__meld_dispatch_importplaceholderbl(f000 d000, decoding to a garbage0xC00000+offset target). It never:bl, norsynth compilereportsRelocatable ELF: N functions, 0 external symbols, 0 relocationsregardless 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_RELlinked into a Zephyr build): the merged module'sz_impl_k_sem_give → gale_k_sem_give_decideseam and its 5 kernel imports all become unrelocated placeholders, so the.ocannot be linked into firmware.It also appears consistent with the
synth backendstable, which marks the arm backendELF: no(vsriscv/w2c2=yes).Minimal reproduction (no imports, 2 functions)
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.ocaller's internalcall $callee(callee is at0x0in the same object):arm-zephyr-eabi-objdump -r mini.o→ no relocations.Not specific to
--relocatableThe same placeholder appears in a full
--cortex-mbinary, wherecalleehas a fixed known address0xa0: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:
bl(callee address known at layout time), orR_ARM_THM_CALLrelocations against the callee/import symbol, sold(or the host build) can resolve them.(The
__meld_dispatch_importruntime-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-moutputs.)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
53fd41d1),armbackend, default optimizationcortex-m4farm-zephyr-eabi-objdump(binutils 2.43.1)