Skip to content

Commit 65f2217

Browse files
author
Arnaud Riess
committed
refactor: shift complexity from backend to runtime
1 parent 9a4c6d4 commit 65f2217

8 files changed

Lines changed: 801 additions & 93 deletions

File tree

crates/herkos-runtime/src/lib.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,13 @@ pub use table::{FuncRef, Table};
2222
mod module;
2323
pub use module::{LibraryModule, Module};
2424

25+
mod ops;
26+
pub use ops::{
27+
i32_div_s, i32_div_u, i32_rem_s, i32_rem_u, i32_trunc_f32_s, i32_trunc_f32_u, i32_trunc_f64_s,
28+
i32_trunc_f64_u, i64_div_s, i64_div_u, i64_rem_s, i64_rem_u, i64_trunc_f32_s, i64_trunc_f32_u,
29+
i64_trunc_f64_s, i64_trunc_f64_u,
30+
};
31+
2532
/// Wasm execution errors — no panics, no unwinding.
2633
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
2734
pub enum WasmTrap {

crates/herkos-runtime/src/memory.rs

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,21 @@ impl<const MAX_PAGES: usize> IsolatedMemory<MAX_PAGES> {
173173
store_f64_inner(self.flat_mut(), active, offset, value)
174174
}
175175

176+
/// Initialize a region of memory from a byte slice (Wasm data segment).
177+
///
178+
/// Copies `data` into linear memory starting at `offset`. Equivalent to
179+
/// calling `store_u8` for each byte, but avoids emitting N separate
180+
/// function calls in generated code.
181+
///
182+
/// # Errors
183+
/// Returns `Err(WasmTrap::OutOfBounds)` if `offset + data.len()` exceeds
184+
/// `active_pages * PAGE_SIZE`.
185+
#[inline(always)]
186+
pub fn init_data(&mut self, offset: usize, data: &[u8]) -> WasmResult<()> {
187+
let active = self.active_size();
188+
init_data_inner(self.flat_mut(), active, offset, data)
189+
}
190+
176191
// ── Unchecked (verified) load/store ───────────────────────────────
177192
//
178193
// These skip bounds checking entirely. The caller MUST guarantee that
@@ -387,6 +402,18 @@ fn store_f64_inner(
387402
Ok(())
388403
}
389404

405+
#[inline(never)]
406+
fn init_data_inner(
407+
memory: &mut [u8],
408+
active_bytes: usize,
409+
offset: usize,
410+
data: &[u8],
411+
) -> WasmResult<()> {
412+
let dst = checked_slice_mut(memory, active_bytes, offset, data.len())?;
413+
dst.copy_from_slice(data);
414+
Ok(())
415+
}
416+
390417
// ── Unchecked inner functions ─────────────────────────────────────────
391418
//
392419
// SAFETY: the caller (verified backend) guarantees the offset is in-bounds,
@@ -623,6 +650,50 @@ mod tests {
623650
assert_eq!(mem.load_i32(PAGE_SIZE), Ok(99));
624651
}
625652

653+
// ── init_data ──
654+
655+
#[test]
656+
fn init_data_writes_bytes() {
657+
let mut mem = Mem::try_new(1).unwrap();
658+
mem.init_data(10, &[1u8, 2, 3, 4]).unwrap();
659+
assert_eq!(mem.load_u8(10).unwrap(), 1);
660+
assert_eq!(mem.load_u8(11).unwrap(), 2);
661+
assert_eq!(mem.load_u8(12).unwrap(), 3);
662+
assert_eq!(mem.load_u8(13).unwrap(), 4);
663+
}
664+
665+
#[test]
666+
fn init_data_empty_slice_is_noop() {
667+
let mut mem = Mem::try_new(1).unwrap();
668+
assert!(mem.init_data(0, &[]).is_ok());
669+
}
670+
671+
#[test]
672+
fn init_data_out_of_bounds() {
673+
let mut mem = Mem::try_new(1).unwrap();
674+
let data = [0u8; 10];
675+
assert_eq!(
676+
mem.init_data(PAGE_SIZE - 5, &data),
677+
Err(WasmTrap::OutOfBounds)
678+
);
679+
}
680+
681+
#[test]
682+
fn init_data_at_boundary() {
683+
let mut mem = Mem::try_new(1).unwrap();
684+
let data = [42u8; 4];
685+
assert!(mem.init_data(PAGE_SIZE - 4, &data).is_ok());
686+
assert_eq!(mem.load_u8(PAGE_SIZE - 1).unwrap(), 42);
687+
}
688+
689+
#[test]
690+
fn init_data_overwrites_existing() {
691+
let mut mem = Mem::try_new(1).unwrap();
692+
mem.store_u8(5, 0xFF).unwrap();
693+
mem.init_data(5, &[0xABu8]).unwrap();
694+
assert_eq!(mem.load_u8(5).unwrap(), 0xAB);
695+
}
696+
626697
// ── little-endian encoding ──
627698

628699
#[test]

0 commit comments

Comments
 (0)