Skip to content

docs: add WASM backend design document#22

Draft
Proxy-alt wants to merge 2 commits into
pound-emu:mainfrom
Proxy-alt:feature-wasm-backend
Draft

docs: add WASM backend design document#22
Proxy-alt wants to merge 2 commits into
pound-emu:mainfrom
Proxy-alt:feature-wasm-backend

Conversation

@Proxy-alt

@Proxy-alt Proxy-alt commented Apr 25, 2026

Copy link
Copy Markdown

Description

Adds docs/WASM_BACKEND_DESIGN.md, a Tier 1 design document for a WebAssembly backend for Ballistic. The doc covers a single-pass dumb emitter scoped strictly to the IR opcodes currently implemented and tested in the engine.

Motivation: a WASM backend is the path to running Ballistic in the browser, on iOS, and on other sandboxed targets where native JIT is not permitted. The design respects the IR's structured-SSA model and PROGRAMMING_RULES.md (compile-time backend selection, no function pointer dispatch in the translation loop, single-pass emission, cursor iteration, read-only fields accessed directly through block).

The doc was written after a Discord thread with @GloriousTacoo (4/19–4/23) clarifying IR encoding, register-file placement, MERGE destination semantics, and Tier 1 scope. Per his direction, branching, control flow, host calls, and Tier 2 are explicitly out of scope; they are listed in §14 with the understanding that the doc gets extended when the IR side stabilizes for them.

Open questions that could not be closed in chat are collected in §16 (Q1-Q6). They block finalizing the §12 instruction mapping table for five opcodes (OPCODE_MOV operand order, OPCODE_DIV sign split, OPCODE_SHIFT direction encoding, OPCODE_CMP result type, the OPCODE_TRAP host-callback ABI, and OPCODE_RETURN under inlining). None of them block starting work on the assembler or module-builder layers.

No code changes. No dependencies.

Resolves: (no issue, design doc submitted ahead of implementation per @GloriousTacoo's CONTRIBUTING guidance: "create [a] design doc similar to IR_DESIGN_DOC.md and create a pull request").

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation update
  • Refactoring (no functional change, code cleanup)
  • Performance improvement

Code Review Process

Acknowledged. I expect this draft to change substantially in review, the IR doc went through ~50 commits over two months before settling, and a backend design touching every opcode will need similar iteration. Six open questions in §16 are explicitly flagged for resolution before the table in §12 can be considered final. I am prepared to defend each section, rework the operand mapping if any of Q1-Q6 resolves differently than assumed, and rewrite the doc to match whatever direction @GloriousTacoo gives.

Breaking Changes

None. Documentation-only addition. No source files, build files, tests, or public headers are modified.

Checklist

  • My code follows the project's coding style guidelines (N/A — markdown only; clang-format does not apply)
  • I have commented my code, particularly in hard-to-understand areas (N/A — prose document; rationale included inline for each non-obvious decision, particularly §8 patching, §11 Rule 3 application, §15 copy-elision constraint)
  • I have made corresponding changes to the documentation (this PR is the documentation)
  • My changes generate no new warnings (N/A — no compilation)
  • I have read the project's CONTRIBUTING guidelines
  • I understand and agree to the rigorous code review process described above

Comment thread docs/WASM_BACKEND_DESIGN_DOC.md Outdated
Comment thread docs/WASM_BACKEND_DESIGN_DOC.md
Comment thread docs/WASM_BACKEND_DESIGN_DOC.md Outdated
Comment thread docs/WASM_BACKEND_DESIGN_DOC.md Outdated
Comment thread docs/WASM_BACKEND_DESIGN_DOC.md Outdated
Comment thread docs/WASM_BACKEND_DESIGN_DOC.md Outdated
Comment thread docs/WASM_BACKEND_DESIGN_DOC.md Outdated
Comment thread docs/WASM_BACKEND_DESIGN_DOC.md Outdated
Comment thread docs/WASM_BACKEND_DESIGN_DOC.md Outdated
Comment thread docs/WASM_BACKEND_DESIGN_DOC.md Outdated
Comment thread docs/WASM_BACKEND_DESIGN_DOC.md Outdated
@Proxy-alt Proxy-alt force-pushed the feature-wasm-backend branch from f0b2b31 to 7512fab Compare May 3, 2026 13:47
Adds a Tier 1 design document for a WebAssembly backend covering the

opcodes currently implemented and tested in the IR layer. Branching,

control flow, host calls, and Tier 2 optimization are out of scope per

direction from @GloriousTacoo and will be added when the IR side

stabilizes for them.

The design follows PROGRAMMING_RULES.md: compile-time backend selection

via static-inline emit functions, cursor-based iteration over the

instruction array, single-pass emission with locals-count patching to

avoid a second cache pass, and read-only fields on the IR block

accessed directly rather than hoisted into locals.

Six open questions are collected in §16 for resolution in review before

the §12 instruction mapping table is finalized.

Signed-off-by: Proxy Alt <proxy-alt@proxy-alt.dev>
@Proxy-alt Proxy-alt force-pushed the feature-wasm-backend branch from 7512fab to 9f24005 Compare May 3, 2026 13:50
@Proxy-alt

Copy link
Copy Markdown
Author

Force-pushed addressing all 16 review comments:

Errors corrected:

  • §7 register file moved out of guest address space (BAL_REGFILE_BASE at high offset; guest RAM at offset 0)
  • §13 alignment removed from all loads/stores (align=0; ARM permits unaligned access)
  • §13 OPCODE_NOR renamed to OPCODE_ORN with corrected lowering (Rn | ~Rm)
  • §13 OPCODE_RETURN row updated to take Rn; arithmetic rows show Rm/imm in src2 to reflect that immediates are valid there
  • §7.1 corrected — XZR/SP disambiguation is the IR layer's job, not the decoder's
  • §6 emit_operand example uses BAL_IS_CONSTANT_BIT_POSITION (the actual codebase symbol) — please confirm I picked the right way to use it
  • §12 misleading comment about OPCODE_GET_REGISTER reading constant_pool removed; added explicit note that only opcodes with imm operands read the constant pool

Removed:

  • §13 D-Cache Compliance section (project-wide standard, redundant)
  • All OPCODE_VOID references (will be removed from engine)
  • §16 Q1 quote restating IR layer details

Added:

  • §3 defining the bal_ir_block_t fields the backend reads
  • §5 + §8 explicitly clarifying that local_count tracks WASM locals (value-producing IR instructions only), distinct from bal_engine_t::instruction_count
  • §9 rewritten to make the WASM binary format constraint explicit (locals declaration precedes instruction sequence, decoder reads it first)

Open question changes:

  • Q1 reframed around the proposed MOV emission given Rd is a guest register index — needs confirmation
  • Q5 import-call proposal removed (contradicted §7); current Tier 1 mapping is unreachable, with non-import alternatives listed for trap-reason notification
  • Q7 (new): BAL_REGFILE_BASE definition and management — how is the offset chosen and communicated?
  • Q8 (new): function signature () → () vs () → i64 — the OPCODE_RETURN emission depends on this and the two need to be made consistent
  • Q9 (new): does the imm form of arithmetic opcodes require special handling beyond i64.const?
  • Q10 (new): does OPCODE_ORN carry shift information, or is shifting separate?

Comment on lines +656 to +658
**All locals declared as i64.** `ssa_bit_widths[]` exists but using narrower
types is deferred. Costs roughly 1 byte per local in the locals section,
trivial.

@GloriousTacoo GloriousTacoo May 5, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is flawed thinking. If the locals are all 64-bits and an ARM instruction overflows you will produce incorrect values.

Comment on lines +362 to +380
static inline void wasm_emit_local_get (BalBackendCtx* ctx, uint32_t idx);
static inline void wasm_emit_local_set (BalBackendCtx* ctx, uint32_t idx);
static inline void wasm_emit_i64_const (BalBackendCtx* ctx, int64_t v);
static inline void wasm_emit_i64_load (BalBackendCtx* ctx, uint32_t align, uint32_t off);
static inline void wasm_emit_i64_store (BalBackendCtx* ctx, uint32_t align, uint32_t off);
static inline void wasm_emit_i64_add (BalBackendCtx* ctx);
static inline void wasm_emit_i64_sub (BalBackendCtx* ctx);
static inline void wasm_emit_i64_mul (BalBackendCtx* ctx);
static inline void wasm_emit_i64_div_s (BalBackendCtx* ctx);
static inline void wasm_emit_i64_div_u (BalBackendCtx* ctx);
static inline void wasm_emit_i64_and (BalBackendCtx* ctx);
static inline void wasm_emit_i64_xor (BalBackendCtx* ctx);
static inline void wasm_emit_i64_or (BalBackendCtx* ctx);
static inline void wasm_emit_i64_shl (BalBackendCtx* ctx);
static inline void wasm_emit_i64_shr_s (BalBackendCtx* ctx);
static inline void wasm_emit_i64_shr_u (BalBackendCtx* ctx);
static inline void wasm_emit_select (BalBackendCtx* ctx);
static inline void wasm_emit_return (BalBackendCtx* ctx);
static inline void wasm_emit_unreachable(BalBackendCtx* ctx);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rename BalBackendCtx to bal_backend_context_t

@Proxy-alt Proxy-alt marked this pull request as draft June 18, 2026 11:37
@Proxy-alt

Copy link
Copy Markdown
Author

Will deal with updating the branch (with atomic commits too since I accidentally clicked the update button) and finishing PR after API has stabilized

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants