Skip to content

feat(riscv): i64 Phase 1 — typed vstack + i64 arith/logic/compares/loads/stores#119

Open
avrabe wants to merge 1 commit into
mainfrom
feat/riscv-i64-phase1
Open

feat(riscv): i64 Phase 1 — typed vstack + i64 arith/logic/compares/loads/stores#119
avrabe wants to merge 1 commit into
mainfrom
feat/riscv-i64-phase1

Conversation

@avrabe
Copy link
Copy Markdown
Contributor

@avrabe avrabe commented May 15, 2026

Summary

Phase 1 of i64 support in the RV32IMAC backend. Refactors the selector's vstack from `Vec<Reg>` to a typed `Vec<VstackVal>` enum (`I32(Reg)` / `I64 { lo, hi }`), then layers in i64 arithmetic, logic, comparisons, conversions, and memory ops on top.

Bundled with #116 (RV32 Call leaf-subset) this brings the RV32 surface much closer to ARM-side parity.

What's in

Refactor (no behavior change for existing i32 paths)

  • `vstack: Vec<Reg>` → `Vec<VstackVal>` with typed helpers (`push_i32`, `push_i64`, `pop_i32`, `pop_i64`, `pop_pair_i32`, `pop_pair_i64`, `pop_any` for Drop).
  • New `SelectorError::StackTypeMismatch` variant — selector-internal type bugs surface as typed errors instead of silently mixing halves.
  • All ~43 existing i32 call sites switched to the typed helpers.
  • `Drop` becomes type-agnostic (correctly pops an i64 pair as one wasm value).
  • `LocalTee` enum-matches `vstack.last()` (locals stay i32-only in Phase 1).

New i64 ops

WASM op RV32 sequence Notes
`I64Const` two `emit_load_imm` (lo + hi) uses existing helper
`I64Add` `add lo,al,bl; sltu carry,lo,al; add hi,ah,bh; add hi,hi,carry` unsigned-overflow detection
`I64Sub` `sltu borrow,al,bl; sub lo,al,bl; sub hi,ah,bh; sub hi,hi,borrow` borrow = (al < bl) before sub
`I64And`/`Or`/`Xor` pairwise on lo and hi trivial
`I64Eq`/`Ne` xor halves → or → sltiu/sltu result is i32 0/1
`I64Eqz` `or d,lo,hi; sltiu r,d,1` pops i64, pushes i32
`I64ExtendI32U` `lo = i32; hi = ZERO` zero-extend
`I64ExtendI32S` `lo = i32; sra hi,i32,31` sign-extend via arithmetic shift
`I32WrapI64` pop i64, push lo as i32 drop hi
`I64Load { offset }` `lw lo, offset(addr); lw hi, offset+4(addr)` offset ≤ 2043 (imm12 - 4)
`I64Store { offset }` `sw lo, offset(addr); sw hi, offset+4(addr)` matching offset constraint

Epilogue now handles i64 returns via the RV psABI `(a0=lo, a1=hi)` convention.

What's out (deferred to Phase 2)

These ops still fall through to `SelectorError::Unsupported` — they fail loudly, no silent miscompilation:

  • `I64Mul` — needs 4× mul/mulh sequence
  • `I64DivS`/`U`, `I64RemS`/`U` — need `__divdi3`-style runtime call or 64-bit long-division
  • `I64Shl`, `I64ShrS`, `I64ShrU` — shamt branches at the 32-bit boundary
  • `I64Rotl`, `I64Rotr`
  • `I64Clz`, `I64Ctz`, `I64Popcnt`
  • `I64Lt`/`Le`/`Gt`/`Ge` (S+U) — explicit hi-then-lo comparison ladder
  • `I64Extend8S`/`16S`/`32S`
  • Sub-word i64 loads/stores (`I64Load8S`/`U` etc.)
  • i64 locals (`LocalGet`/`Set`/`Tee` on i64) — would need ABI work to allocate 2 arg regs per i64 param

Validation

  • `cargo test --package synth-backend-riscv` → 110/110 passing (was 98; +12 new i64 tests).
  • `cargo clippy --package synth-backend-riscv --all-targets -- -D warnings` — clean.
  • `cargo fmt --check` — clean.
  • `cargo build --workspace` — clean (no callers broken).

Tests added

One per Phase-1 op:

  • `i64_const_emits_two_load_imm_sequences`
  • `i64_add_emits_add_sltu_add_add_pattern`
  • `i64_sub_emits_borrow_pattern`
  • `i64_and_or_xor_each_emit_two_ops`
  • `i64_eq_emits_xor_xor_or_sltiu`
  • `i64_ne_emits_xor_xor_or_sltu`
  • `i64_eqz_emits_or_sltiu`
  • `i64_extend_i32_u_pushes_zero_hi`
  • `i64_extend_i32_s_emits_sra_31`
  • `i32_wrap_i64_drops_hi`
  • `i64_load_emits_two_lw_at_offset_and_offset_plus_4`
  • `i64_store_emits_two_sw_at_offset_and_offset_plus_4`

Test plan

🤖 Generated with Claude Code

…mpares / loads / stores

Refactors the RV32IMAC selector's virtual stack from `Vec<Reg>` to a typed
`Vec<VstackVal>` enum so i64 values can live on the stack as a `(lo, hi)`
register pair. RV32 doesn't require consecutive pairs (unlike ARM's
LDRD/STRD), so any two distinct temps work.

New typed helpers replace the untyped `push_val`/`pop_val`/`pop_pair`:
  push_i32 / push_i64 / pop_i32 / pop_i64 / pop_pair_i32 / pop_pair_i64
A new `SelectorError::StackTypeMismatch` variant surfaces selector-internal
type bugs (rather than silently mixing halves).

Phase 1 i64 ops implemented (selector-only — encoder already supports
every base instruction needed):

  I64Const          two emit_load_imm sequences (lo + hi)
  I64Add            add lo, sltu carry, add hi, add hi+carry
  I64Sub            sltu borrow, sub lo, sub hi, sub hi-borrow
  I64And/Or/Xor     pairwise on lo and hi
  I64Eq / I64Ne     xor diffs, or them, then sltiu/sltu → i32 0/1
  I64Eqz            or halves, sltiu → i32 0/1
  I64ExtendI32U     hi = 0
  I64ExtendI32S     hi = srai src, 31
  I32WrapI64        zero-instruction; lo continues as i32
  I64Load           lw lo @offset, lw hi @offset+4
  I64Store          sw lo @offset, sw hi @offset+4

The return epilogue now handles both i32 (a0) and i64 (a0=lo, a1=hi)
return values, matching the RV32 psABI for 64-bit returns.

Out of scope (Phase 2): i64 mul/div/rem (runtime helpers), i64 shifts /
rotates / clz / ctz / popcnt (shamt branching at 32-bit boundary), i64
ordered compares (lt/le/gt/ge S+U — hi-then-lo ladder), i64 sign-
extending sub-word loads, I64Extend{8,16,32}S, sub-word i64 stores.
These remain at the existing `_ => Unsupported` arm and error cleanly.

Validation:
- cargo test --package synth-backend-riscv: 98 → 110 passing (+12 new)
- cargo clippy --package synth-backend-riscv -- -D warnings: clean
- cargo fmt --check: clean
- cargo build --workspace: clean

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@codecov
Copy link
Copy Markdown

codecov Bot commented May 16, 2026

Codecov Report

❌ Patch coverage is 90.78431% with 47 lines in your changes missing coverage. Please review.

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

📢 Thoughts on this report? Let us know!

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