← board

i386 text-assembler (EmitAsm386) for cleaner x86-32 codegen

Motivation

ir_codegen386.inc is 2947 lines, ~970 of them raw EmitB($..) with the mnemonic only in a trailing comment, plus 57 manual CodeLen/Patch32 jump sites. Unreadable and the exact shape that produced the 19 DB vs 19 D3 ModRM bug — a class of bug the encode-ModRM-once text assembler cannot make. This is the highest-leverage conversion: biggest file, hottest backend, and it feeds the i386 self-host arc (current wall: Expected: unit miscompile — [[project_i386_selfhost_arc]]).

Why first / cheapest

EmitAsm386 shares the x86 ModRM/SIB core already written for EmitAsmX64 in compiler/asmtext.inc. i386 is the same encoder with REX dropped and the register set narrowed to eax..edi/esp/ebp (no r8..r15, no RIP-relative). Mostly a 32-bit entry point over the existing x86 layer, not new ISA work.

Scope (incremental — mix freely with EmitB)

  1. EmitAsm386(const items: array of const) + single-line overload, over the shared x86 core. Registers 8/16/32-bit (al/ax/eax …), [base±disp], immediates; markers % (width inferred), @data/@glob (via EmitDataRef/ EmitGlobRef), .label: with rel8/rel32 back+forward resolution — the mechanism that deletes the 57 manual jump sites.
  2. Grow the mnemonic table on demand — start with exactly what the converted blocks use (mov push pop add sub and or xor cmp lea inc dec + jmp/jcc
    • int 0x80/ret/leave).
  3. Convert EmitwriteUInt64_386 (fixed body + a backward-jump loop + two @data) and at least one bound site (the IR_LOAD_SYM [ebp+disp] path). Leave heavily-dynamic blocks on EmitB/typed encoders.

Landmines

Acceptance

Deferred

Full ir_codegen386.inc conversion (incremental, on demand); retargeting the user asm … end path (asmenc.inc) onto the shared engine (own follow-up).

Log