Skip to content

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

Merged
avrabe merged 1 commit into
mainfrom
feat/riscv-i32-selector
May 10, 2026
Merged

feat(riscv): full i32 selector — comparisons, division, control flow, memory#88
avrabe merged 1 commit into
mainfrom
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

@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

@avrabe avrabe force-pushed the feat/riscv-skeleton branch from 510b193 to 0d46897 Compare May 10, 2026 05:20
@avrabe avrabe force-pushed the feat/riscv-i32-selector branch from 9e891b1 to 0abbecb Compare May 10, 2026 05:21
@avrabe avrabe changed the base branch from feat/riscv-skeleton to main May 10, 2026 05:23
@avrabe avrabe closed this May 10, 2026
@avrabe avrabe reopened this May 10, 2026
@codecov
Copy link
Copy Markdown

codecov Bot commented May 10, 2026

Codecov Report

❌ Patch coverage is 88.31004% with 92 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
crates/synth-backend-riscv/src/selector.rs 88.28% 92 Missing ⚠️

📢 Thoughts on this report? Let us know!

@avrabe avrabe force-pushed the feat/riscv-i32-selector branch 2 times, most recently from 9429cbb to b3fb4d7 Compare May 10, 2026 12:34
… 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>
@avrabe avrabe force-pushed the feat/riscv-i32-selector branch from b3fb4d7 to 87479f6 Compare May 10, 2026 15:15
@avrabe avrabe merged commit aa94006 into main May 10, 2026
7 checks passed
@avrabe avrabe deleted the feat/riscv-i32-selector branch May 10, 2026 15:17
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