Compiler design / 17. Backend lowering
Lowerers translate validated HIR into target code without reopening source or rewriting language meaning.
One HIR operation may become a JavaScript expression in one partition and a Wasm instruction sequence in another. The meaning stays Moth. The spelling becomes target-native. Same source meaning, different host climates.
Lowerers translate validated inputs. They are not second language frontends.
A lowerer receives explicit records:
A lowerer does not:
If a lowerer needs a fact, an earlier owner should have recorded it.
For the walkthrough, target-labelled start, greet and template work arrive ready. The JavaScript path might own page orchestration. Neutral string work may sit in either partition depending on assignment.
Diagram placeholder: Selected target sends one HIR input to two target paths
Caption: exact frontend boundary simplified. Question: what stays shared?
The JS backend speaks host values and functions. It leans on the host GC for reachability. Runtime helpers implement language operations the surface syntax made look primitive: checked numerics, certain casts, template runtime pieces, error plumbing.
greet in JavaScript form is ordinary host code that preserves Moth contracts. A more permissive JS primitive must not silently weaken checked arithmetic, finite-Float rules or error propagation.
Accepted Wasm design prefers a structured path:
Beginner picture:
Old dispatcher-loop LIR stories, moth_start bootstrap myths, i64 Int bridges as the final story and per-module memory as the default are removed final-design paths. Teach them only as historical "do not rebuild this" notes, not as the architecture.
Diagram placeholder: A derived backend view feeds structured blocks and ABI operations
Caption: accepted Wasm design, simplified records. Question: where does target structure enter?
Accepted HTML mixed-target design wants page-local shared Wasm runtime and memory rather than a scatter of isolated memories per module as the end state. Page-local shared Wasm runtime/memory applies to Moth-linked variants only. Imported WIT components use component-private memory and value conversion.
JavaScript companion facades and wrappers call Wasm exports through explicit handles and layout rules. The bridge is data, not guesswork.
Diagram placeholder: JavaScript companion calls a Wasm export through explicit handles and layout rules
Caption: teaching boundary. Question: how do targets communicate?
Binding-backed calls lower through demand-driven glue. The binder's semantic interface already existed upstream. The lowerer maps stable external ids to imports, helpers or native operations.
Borrow facts may guide optional ownership-aware Wasm lowering: earlier drops, specialised paths, less GC traffic. GC backends may ignore ownership operations. They still required validation upstream. Optimisation must preserve observable behaviour and the same accepted program set.
Diagram placeholder: Ownership side facts guide Wasm operations while GC can ignore them
Caption: semantic invariant. Question: what can optimisation change?
CompilerError laneUser-facing target failures preferably die in validation, not mid-emission.
Alternative: give each backend a source interpreter or separate frontend.
Choice: one validated semantic HIR plus target-owned lowering.
Benefit: source behaviour remains shared across JavaScript and Wasm.
Cost: each backend needs a faithful ABI and runtime implementation.
html_project_backend_wasm_final_implementation_plan.md.You leave with JavaScript, Wasm, wrappers, runtime modules and asset records as data. Next: how the HTML builder assembles and owns those outputs on disk, ending at <p>Hello, Priya!</p>.