Spec M10 — Tier 2: passes and backends taught through the surface
Prerequisite: M9 closed (golden updated). Proves the central requirement: a backend written in
.mc, outside the compiler, produces __text byte for byte identical to the built-in one.
Core (small, on both sides) #
- Function address:
&namewherenameis a function/extern →uptr(adrp/add with relocPAGE21/PAGEOFF12for_name). - Indirect call: intrinsic
callp(p, a1, ..., a7)→ args inx0..x6,blrviax8? No: loadpintox16(IP0, caller-saved, outsidex0..x7) andblr x16. Saving live depths is the same as forbl. Return typei64. - User hook:
src/mc.mcincludessrc/user.mc, which by default includeslib/user_default.mccontainingvoid user_init() { }. The driver callsuser_init()before parsing. Teaching the compiler = editingsrc/user.mcto include your modules and runningmake mc1(mcrecompiles itself with the module included). No dylib, no ABI. - Registration (
src/hooks.mc, part of the compiler):pass(uptr fn)appends to a linear table;backend(uptr name, uptr fn)likewise. Driver: afterparse_unitandfold, applies each pass in order (root = callp(fn, root)); picks the backend via--backend=NAME(defaultmacho, built in) and callscallp(fn, root, out_path). - Splitting gen:
gen_unit(root)now has two halves with public names:gen_lower(root)(AST → per-functionInsbuffers + global/string tables, no encoding) andgen_encode_all()+macho_write(out). The built-inmachobackend =gen_lower+gen_encode_all+macho_write. Each function'sInsbuffer and the tables are reachable via accessors (ins_count(f),ins_at(f, i),func_count(),func_name(f)…).
Proof (in the surface, under lib/) #
lib/backend_arm64.mc: thearm64-surfacebackend, which callsgen_lowerand then reimplements the encoder in.mcusing only the public API: opcode tables (the same formulas asencode, rewritten — copying is fine), labels/fixups,sec_new/sym_new/reloc_add/buf_u32frommacho.mc,macho_write. Registered inuser_initfromlib/user_backend_demo.mc.lib/pass_demo.mc: a pass that walks the AST and replacesx * 1withx(or counts nodes and prints to stderr) — proves the AST is operable from outside.- Acceptance: with
src/user.mcincluding both modules,make mc1; for eachtests/*.mc,build/mc1 --backend=arm64-surface X -o a.ovsbuild/mc1 X -o b.o→cmpidentical (24+ objects); the demo pass changes--dump-astfor one specific test as expected;make checkstays green withsrc/user.mcat its default (the demo is opt-in: enabling/disabling it is just swapping the include).docs/surface.md§ Tier 2 with the real step-by-step walkthrough.
Budget #
C: &fn + callp + splitting gen + hooks ≈ 80 lines (target ≤ 2980). .mc: same, plus the
demonstration modules (which don't count against the budget).