feat(riscv): full i32 selector — comparisons, division, control flow, memory#88
feat(riscv): full i32 selector — comparisons, division, control flow, memory#88avrabe wants to merge 1 commit intofeat/riscv-skeletonfrom
Conversation
… memory Track B2: extends the RISC-V backend's instruction selector from the B1 "hello world" subset to cover the i32 surface used by typical embedded WASM modules (calculator, sensor loops, FSMs). ## Selector additions * **Comparisons** — eq, ne, lt_s/u, le_s/u, gt_s/u, ge_s/u via SLT/SLTU/XOR/XORI. Greater-than is encoded as swapped less-than; le/ge as `lt ^ 1`. * **Division & remainder** — div_s/u, rem_s/u using the M-extension's DIV/DIVU/REM/REMU. WASM's "divide-by-zero traps" semantics are enforced by emitting a `bne rs2, zero, Lok ; ebreak ; Lok:` guard before each division — mirrors the ARM backend's CMP/BNE/UDF pattern. * **Shifts** — shl, shr_s, shr_u via register-form SLL/SRA/SRL. * **Memory ops** — i32.load/store and the sub-word variants (load8_s/u, load16_s/u, store8, store16). The wasm linear-memory base lives in `s11` (callee-saved); the selector emits `add tmp, s11, addr` and then the load/store with the wasm offset as a 12-bit immediate. Memory offsets > 2047 fail loudly (deferred to B3 with proper +/- 2 KB splitting). * **Control flow** — block, loop, if/else/end, br, br_if. Each frame keeps an end label and (for loops) a head label; br depth 0..N indexes back through the control stack. Loops target the head; blocks/ifs target the end. br_if uses `bne cond, zero, target`. * **Locals** — get/set/tee for params (a0..a7). Stack-allocated locals remain unsupported (requires frame allocation, deferred to B3). * **Misc** — drop, nop; unreachable lowers to ebreak. The selector now spans 10× more wasm ops than the B1 skeleton (~30 vs ~8) and is structured around a `Selector` struct with `vstack` (value-register stack) and `ctrl` (control-flow frames) — closely mirroring the ARM backend's `select_with_stack` shape. ## Renode RV32 platform * `tests/renode/synth_riscv.repl` — SiFive FE310-class RV32IMAC platform (CLINT, 64 KB flash @ 0x0, 64 KB SRAM @ 0x80000000). * `tests/renode/riscv_test.robot` — exercises a synth-compiled `add(a,b)` through Renode RV32 emulation. Validates that the .text section loads, the AAPCS-style psABI register passing works (a0/a1 → result in a0), and the function returns correctly. * Bazel `genrule(test_add_riscv_elf)` + `renode_test(riscv_add_test)` wire it into the CI test target. ## Integration smoke test `tests/integration/riscv_codegen_smoke.sh` exercises four representative WASM modules through the full pipeline (arith, control-flow, division, memory) and verifies each output ELF reports EM_RISCV (0xF3). Network-free, suitable for CI. ## Test summary * **22 new selector tests** in `synth-backend-riscv::selector` (comparisons, div with zero-trap, control-flow shape, memory ops, br depth bounds, unreachable→ebreak). Total now 82 (was 60). * All workspace tests still pass; clippy/fmt clean. * End-to-end smoke: 4 module categories compile to RV32IMAC ELFs. ## What's still out of scope (B3 / B4) * br_table (jump table emission — needs a separate auipc+jr sequence) * Cross-function calls (relocations + Call-resolution at link time) * Stack-allocated locals (need frame prologue/epilogue with fp setup) * mtvec / trap handler / startup code (B3) * i64 lowering (RV32 register pairs — B4) * Calculator demo running on Renode (B4) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Automated review for PR #88pulseengine/synth: Verdict: 💬 Comment Summary: The pull request is approved as it adds new tests for the RISC-V backend of Synth, including compilation and execution through Renode. The added files and configurations ensure that the RISC-V codegen path is tested thoroughly. Findings: 0 mechanical (rivet) · 5 from local AI model. Findings (5):
Generated by a local AI model and post-validated against a strict JSON contract. Each finding includes the verbatim line being criticised — verify by reading the file at the cited location. Reviewed at |
Track B2 — extends the RISC-V selector from the B1 "hello world" subset to the full i32 surface. Stacked on #87.
What's added
bne rs2, zero, Lok; ebreak; Lok: div).Selector now covers ~30 wasm ops (was ~8 in B1).
Renode RV32 integration
Smoke test
`tests/integration/riscv_codegen_smoke.sh` — offline check for arith, control-flow, division, memory modules. All compile to EM_RISCV ELFs.
Test summary
🤖 Generated with Claude Code