← board

Assembler as a first-class citizen (umbrella)

Sequencing update (2026-06-30): head #3 fast-tracked, urgent

User call: don't land heads in 1→2→3 order. Fast-track a minimal head #3 first — see urgent ticket [[feature-asm-mvp-frontend]] — so Track B has a trivial .asm-file-in, run-it-and-check-behavior test path for lib/asmcore as it grows, instead of only hand-derived byte-comparison Pascal tests. That MVP deliberately needs none of layer 2's label/relocation work (today's lib/asmcore coverage is straight-line mov/add/ret, no branches yet) — it's a real shortcut, not a reordering for its own sake. The full [[feature-asm-source-frontend]] (labels, -c, .so, multi-target) still follows the layered plan below once layer 2 exists.

Two-layer architecture (2026-06-30 — owner split)

The user drew an explicit line: the actual instruction-encoding tables are library legwork (Track B); the symbolic-resolution "magic" that makes labels/globals/relocations work, plus migrating the compiler's own codegen onto the result, is Track A's — it touches shared compiler internals (elfwriter.inc, ir_codegen*.inc, the symbol table) and per the repo's file-ownership rule that's not Track B's lane to implement, only to file.

Heads 2 and 3 below sit on top of layer 2 and inherit lib/asmcore's textual printer once it exists.

Goal

Three user-facing heads, one underlying engine:

  1. Inline asm compiles through a real encoderasm ... end / assembler routines get labels, branches, global-var operands, explicit memory operands, and (eventually) every backend, not just x86-64.
  2. Codegen can emit assembly text instead of object bytes — a debug/ readability mode (-S-style flag). Accepted perf cost: compile time is dominated elsewhere, not in the emit step, so a textual pass is cheap insurance for readable codegen output and diffing across compiler changes.
  3. The compiler can assemble — feed it a .asm file and get a valid object file (.o), executable (ELF), or shared library (.so) out, the same way .c is already a first-class frontend alongside .pas.

Why these are one project, not three

compiler/asmenc.inc (x86-64 inline-asm encoder) already documents its own ceiling in devdocs/developer/inline-asm.md: it encodes straight into a flat byte buffer at parse time, with no symbolic/relocation layer. That's exactly why labels, branches, and global-var operands don't work yet (TODO #1-3 in that doc) — there's nowhere to hang a fixup.

A structured, symbolic instruction-list IR (mnemonic + operands + label refs + global refs, encoded/resolved at codegen-or-link time through the same relocation machinery elfwriter.inc already runs for normal Pascal codegen — EmitGlobRef, GOT patching, etc.) fixes that ceiling once, and every head consumes it:

Best validation of the whole stack: round-trip a program through head 2 (emit .s text) then head 3 (reassemble that text) and diff against direct binary emission. Byte-identical output proves the textual form is faithful and the assembler is real, not a toy.

Per-target scope — corrected 2026-06-30

All targets are in scope: x86-64, i386, aarch64, arm32, riscv32, xtensa. Initial research undersold this — turns out a label-aware, relocation-aware text-to-binary engine (compiler/asmtext.inc + one asmtext_<target>.inc each) already exists for every one of the six targets, used internally by each ir_codegen*.inc backend as a readable alternative to hand-encoding. See [[feature-asm-structured-ir-library]] for the full audit. This means:

Land x86-64 first end-to-end (richest existing groundwork, all three heads), then the rollout to the other five is comparatively cheap per [[feature-asm-structured-ir-library]]'s analysis.

Acceptance (umbrella-level)

All three sub-tickets land; self-host stays byte-identical; make test + cross green; at least one nontrivial test program round-trips head 2 → head 3 byte-identical on x86-64.

Log