← board

Port cdecl indirect calls (dynamic library loading) to the other targets

Context

cdecl on proc types + System V indirect-call marshalling landed on x86-64 (c461fce): a dlsym'd C function called through a function(...): R; cdecl pointer marshals int->rdi.., float->xmm0.., 16-byte aligned, float return bridged. Verified live (sqrt/pow/ldexp; OpenSSL loads and reportedly works).

The ProcCdecl signature flag and the parser are target-independent — they are already set on every target. Only the x86-64 IR_CALL_IND emitter honours the flag; the other backends still use PXX's internal all-integer indirect-call convention. So a cdecl proc-type call on a cross target silently uses the wrong ABI (works for int/ptr args that happen to coincide, miscompiles floats / >N args).

Scope

For each of i386, aarch64, arm32 (and later riscv32/xtensa), teach the backend's IR_CALL_IND to branch on ProcCdecl[cpi] and marshal that target's C ABI for an indirect call:

Reuse each backend's existing direct external marshalling (it already encodes the per-target C ABI); the only delta is obtaining the callee from a runtime value and a register-indirect call instead of a PLT/GOT call.

Also: real library-loading test matrix

x86-64 has test/test_dynlib.pas + test/test_cdecl_indirect.pas. Add cross-target runs (under each test-<arch> once the ABI lands), and a broader "load a real .so and call it" smoke (e.g. libm sqrt/pow, and an OpenSSL libcrypto digest round-trip) to prove the loader end to end. First signal is good: OpenSSL loads on x86-64.

Done when

Not in scope

2026-07-14 — RESOLVED for i386 / aarch64 / arm32 (b362)

test_cdecl_indirect.pas (dlsym'd sqrt/pow/ldexp through cdecl proc types) runs byte-identical to x86-64 on all three, and is wired into test-i386 / test-aarch64 / test-arm32. riscv32/xtensa remain out (no dynamic-linking leg there yet) — reopen a target-specific slice when a consumer appears.

Log