Skip to content

feat(riscv): full i32 selector — comparisons, division, control flow, memory#88

Open
avrabe wants to merge 1 commit intofeat/riscv-skeletonfrom
feat/riscv-i32-selector
Open

feat(riscv): full i32 selector — comparisons, division, control flow, memory#88
avrabe wants to merge 1 commit intofeat/riscv-skeletonfrom
feat/riscv-i32-selector

Conversation

@avrabe
Copy link
Copy Markdown
Contributor

@avrabe avrabe commented May 3, 2026

Track B2 — extends the RISC-V selector from the B1 "hello world" subset to the full i32 surface. Stacked on #87.

What's added

  • Comparisons: eq/ne/lt_s/lt_u/le_s/le_u/gt_s/gt_u/ge_s/ge_u via SLT/SLTU/XOR/XORI.
  • Division/remainder with WASM trap-on-zero semantics (bne rs2, zero, Lok; ebreak; Lok: div).
  • Shifts: shl, shr_s, shr_u via register-form SLL/SRA/SRL.
  • Memory ops: i32.load/store + sub-word load8/16 (signed/unsigned) and store8/16. Linear-memory base lives in s11 (callee-saved).
  • Control flow: block / loop / if / else / end / br / br_if with proper depth-indexed target resolution. Loops target head, blocks/ifs target end. br_if uses bne against zero.
  • Locals: get / set / tee for params (a0..a7).
  • Misc: drop, nop, unreachable → ebreak.

Selector now covers ~30 wasm ops (was ~8 in B1).

Renode RV32 integration

  • New `synth_riscv.repl` — SiFive FE310-class RV32IMAC platform (CLINT, 64 KB flash, 64 KB SRAM).
  • New `riscv_test.robot` — exercises `add(a, b) → a + b` through full RV32 emulation.
  • Bazel `renode_test(riscv_add_test)` wired into the CI matrix.

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

  • 22 new selector tests (total 82 in synth-backend-riscv).
  • Workspace test count: 600+, all green.
  • clippy clean, fmt clean.

🤖 Generated with Claude Code

… 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>
@temper-pulseengine
Copy link
Copy Markdown

Automated review for PR #88

pulseengine/synth:feat/riscv-i32-selector → pulseengine/synth:feat/riscv-skeleton

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):

  1. tests/renode/BUILD.bazel:4

    exports_files([
    

    The BUILD.bazel file exports necessary files for Renode integration tests, including the RISC-V platform description (synth_riscv.repl). This ensures that the platform is correctly defined and available for testing.

  2. tests/renode/synth_riscv.repl:1

    // Minimal RISC-V RV32IMAC platform for Synth-generated binaries.
    

    The synth_riscv.repl file defines a minimal RISC-V platform suitable for testing. It includes memory layouts, interrupt controllers, and CPU settings to simulate an SiFive FE310-class chip.

  3. tests/renode/riscv_test.robot:4

    *** Settings ***
    

    The riscv_test.robot file sets up the necessary test cases for the RISC-V backend. It defines a test case to verify that a simple add function executes correctly on an RV32IMAC platform.

  4. tests/renode/BUILD.bazel:70

    genrule(
    

    The BUILD.bazel file includes a genrule for compiling the WAT file into an ELF using the RISC-V backend. This ensures that the compilation process is automated and can be tested separately.

  5. tests/renode/BUILD.bazel:71

    renode_test(
    

    The BUILD.bazel file defines a renode_test for executing the compiled ELF on the RISC-V platform. This ensures that the execution process is automated and can be tested separately.


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 9e891b1

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.

1 participant