Skip to content

Commit 00deacd

Browse files
author
Arnaud Riess
committed
feat: update memory operations to use usize for better type safety
1 parent 0830549 commit 00deacd

8 files changed

Lines changed: 242 additions & 13 deletions

File tree

crates/herkos-runtime/src/memory.rs

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -150,9 +150,9 @@ impl<const MAX_PAGES: usize> IsolatedMemory<MAX_PAGES> {
150150
///
151151
/// Only the low 8 bits of `val` are used (Wasm spec). Traps (`OutOfBounds`)
152152
/// if the region extends beyond the current active memory.
153-
pub fn fill(&mut self, dst: u32, val: u8, len: u32) -> WasmResult<()> {
153+
pub fn fill(&mut self, dst: usize, val: u8, len: usize) -> WasmResult<()> {
154154
let active = self.active_size();
155-
fill_inner(self.flat_mut(), active, dst as usize, val, len as usize)
155+
fill_inner(self.flat_mut(), active, dst, val, len)
156156
}
157157

158158
/// Wasm `memory.init` — copy `len` bytes from `data[src_offset..]` into
@@ -164,13 +164,13 @@ impl<const MAX_PAGES: usize> IsolatedMemory<MAX_PAGES> {
164164
/// beyond active memory.
165165
pub fn init_data_partial(
166166
&mut self,
167-
dst: u32,
167+
dst: usize,
168168
data: &[u8],
169169
src_offset: usize,
170170
len: usize,
171171
) -> WasmResult<()> {
172172
let active = self.active_size();
173-
init_data_partial_inner(self.flat_mut(), active, dst as usize, data, src_offset, len)
173+
init_data_partial_inner(self.flat_mut(), active, dst, data, src_offset, len)
174174
}
175175

176176
// ── Bounds-checked (safe) load/store ──────────────────────────────
@@ -826,16 +826,13 @@ mod tests {
826826
#[test]
827827
fn fill_out_of_bounds() {
828828
let mut mem = Mem::try_new(1).unwrap();
829-
assert_eq!(
830-
mem.fill(PAGE_SIZE as u32 - 3, 0, 10),
831-
Err(WasmTrap::OutOfBounds)
832-
);
829+
assert_eq!(mem.fill(PAGE_SIZE - 3, 0, 10), Err(WasmTrap::OutOfBounds));
833830
}
834831

835832
#[test]
836833
fn fill_at_boundary() {
837834
let mut mem = Mem::try_new(1).unwrap();
838-
assert!(mem.fill(PAGE_SIZE as u32 - 4, 0x42, 4).is_ok());
835+
assert!(mem.fill(PAGE_SIZE - 4, 0x42, 4).is_ok());
839836
assert_eq!(mem.load_u8(PAGE_SIZE - 1).unwrap(), 0x42);
840837
}
841838

@@ -878,7 +875,7 @@ mod tests {
878875
fn init_data_partial_dst_out_of_bounds() {
879876
let mut mem = Mem::try_new(1).unwrap();
880877
assert_eq!(
881-
mem.init_data_partial(PAGE_SIZE as u32 - 2, b"Hello", 0, 5),
878+
mem.init_data_partial(PAGE_SIZE - 2, b"Hello", 0, 5),
882879
Err(WasmTrap::OutOfBounds)
883880
);
884881
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
(module
2+
(func (export "func_0") (param i32) (result i32)
3+
local.get 0
4+
i32.extend16_s
5+
)
6+
)
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
(module
2+
(func (export "func_0") (param i32) (result i32)
3+
local.get 0
4+
i32.extend8_s
5+
)
6+
)
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
(module
2+
(func (export "func_0") (param i64) (result i64)
3+
local.get 0
4+
i64.extend16_s
5+
)
6+
)
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
(module
2+
(func (export "func_0") (param i64) (result i64)
3+
local.get 0
4+
i64.extend32_s
5+
)
6+
)
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
(module
2+
(func (export "func_0") (param i64) (result i64)
3+
local.get 0
4+
i64.extend8_s
5+
)
6+
)

crates/herkos-tests/tests/arithmetic.rs

Lines changed: 203 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@
66
//! 3. The semantics match WebAssembly's wrapping arithmetic
77
88
use herkos_tests::{
9-
abs, add, add_i64, const_i64, const_return, factorial, fibonacci, gcd, mul, nop, sub,
9+
abs, add, add_i64, const_i64, const_return, factorial, fibonacci, gcd, i32_extend16_s,
10+
i32_extend8_s, i64_extend16_s, i64_extend32_s, i64_extend8_s, mul, nop, sub,
1011
};
1112

1213
#[test]
@@ -307,3 +308,204 @@ fn test_add_matches_rust_wrapping() {
307308
);
308309
}
309310
}
311+
312+
// Sign-extension operations
313+
#[test]
314+
fn test_i32_extend8_s() {
315+
let mut extend8_mod = i32_extend8_s::new().unwrap();
316+
317+
// Positive value: 0x42 sign-extended to i32
318+
assert_eq!(
319+
extend8_mod.func_0(0x42).unwrap(),
320+
0x42,
321+
"i32.extend8_s(0x42) should be 0x42"
322+
);
323+
324+
// Negative value: 0xFF sign-extended to i32 (all bits set, -1)
325+
assert_eq!(
326+
extend8_mod.func_0(0xFF).unwrap(),
327+
-1i32,
328+
"i32.extend8_s(0xFF) should be -1"
329+
);
330+
331+
// Another negative: 0x80 sign-extended (bit 7 set)
332+
assert_eq!(
333+
extend8_mod.func_0(0x80).unwrap(),
334+
-128i32,
335+
"i32.extend8_s(0x80) should be -128"
336+
);
337+
338+
// Zero
339+
assert_eq!(
340+
extend8_mod.func_0(0).unwrap(),
341+
0,
342+
"i32.extend8_s(0) should be 0"
343+
);
344+
345+
// 0x7F sign-extended (max positive signed byte)
346+
assert_eq!(
347+
extend8_mod.func_0(0x7F).unwrap(),
348+
127i32,
349+
"i32.extend8_s(0x7F) should be 127"
350+
);
351+
}
352+
353+
#[test]
354+
fn test_i32_extend16_s() {
355+
let mut extend16_mod = i32_extend16_s::new().unwrap();
356+
357+
// Positive value: 0x1234 sign-extended
358+
assert_eq!(
359+
extend16_mod.func_0(0x1234).unwrap(),
360+
0x1234,
361+
"i32.extend16_s(0x1234) should be 0x1234"
362+
);
363+
364+
// Negative value: 0xFFFF sign-extended (all bits set, -1)
365+
assert_eq!(
366+
extend16_mod.func_0(0xFFFF).unwrap(),
367+
-1i32,
368+
"i32.extend16_s(0xFFFF) should be -1"
369+
);
370+
371+
// Another negative: 0x8000 sign-extended (bit 15 set)
372+
assert_eq!(
373+
extend16_mod.func_0(0x8000).unwrap(),
374+
-32768i32,
375+
"i32.extend16_s(0x8000) should be -32768"
376+
);
377+
378+
// Zero
379+
assert_eq!(
380+
extend16_mod.func_0(0).unwrap(),
381+
0,
382+
"i32.extend16_s(0) should be 0"
383+
);
384+
385+
// 0x7FFF sign-extended (max positive signed short)
386+
assert_eq!(
387+
extend16_mod.func_0(0x7FFF).unwrap(),
388+
32767i32,
389+
"i32.extend16_s(0x7FFF) should be 32767"
390+
);
391+
}
392+
393+
#[test]
394+
fn test_i64_extend8_s() {
395+
let mut extend8_mod = i64_extend8_s::new().unwrap();
396+
397+
// Positive value
398+
assert_eq!(
399+
extend8_mod.func_0(0x42).unwrap(),
400+
0x42i64,
401+
"i64.extend8_s(0x42) should be 0x42"
402+
);
403+
404+
// Negative value: 0xFF sign-extended to i64
405+
assert_eq!(
406+
extend8_mod.func_0(0xFF).unwrap(),
407+
-1i64,
408+
"i64.extend8_s(0xFF) should be -1"
409+
);
410+
411+
// Another negative: 0x80 sign-extended
412+
assert_eq!(
413+
extend8_mod.func_0(0x80).unwrap(),
414+
-128i64,
415+
"i64.extend8_s(0x80) should be -128"
416+
);
417+
418+
// Zero
419+
assert_eq!(
420+
extend8_mod.func_0(0).unwrap(),
421+
0i64,
422+
"i64.extend8_s(0) should be 0"
423+
);
424+
425+
// Max positive signed byte
426+
assert_eq!(
427+
extend8_mod.func_0(0x7F).unwrap(),
428+
127i64,
429+
"i64.extend8_s(0x7F) should be 127"
430+
);
431+
}
432+
433+
#[test]
434+
fn test_i64_extend16_s() {
435+
let mut extend16_mod = i64_extend16_s::new().unwrap();
436+
437+
// Positive value
438+
assert_eq!(
439+
extend16_mod.func_0(0x1234).unwrap(),
440+
0x1234i64,
441+
"i64.extend16_s(0x1234) should be 0x1234"
442+
);
443+
444+
// Negative value: 0xFFFF sign-extended to i64
445+
assert_eq!(
446+
extend16_mod.func_0(0xFFFF).unwrap(),
447+
-1i64,
448+
"i64.extend16_s(0xFFFF) should be -1"
449+
);
450+
451+
// Another negative: 0x8000 sign-extended
452+
assert_eq!(
453+
extend16_mod.func_0(0x8000).unwrap(),
454+
-32768i64,
455+
"i64.extend16_s(0x8000) should be -32768"
456+
);
457+
458+
// Zero
459+
assert_eq!(
460+
extend16_mod.func_0(0).unwrap(),
461+
0i64,
462+
"i64.extend16_s(0) should be 0"
463+
);
464+
465+
// Max positive signed short
466+
assert_eq!(
467+
extend16_mod.func_0(0x7FFF).unwrap(),
468+
32767i64,
469+
"i64.extend16_s(0x7FFF) should be 32767"
470+
);
471+
}
472+
473+
#[test]
474+
fn test_i64_extend32_s() {
475+
let mut extend32_mod = i64_extend32_s::new().unwrap();
476+
477+
// Positive value: 0x12345678 sign-extended
478+
assert_eq!(
479+
extend32_mod.func_0(0x12345678i64).unwrap(),
480+
0x12345678i64,
481+
"i64.extend32_s(0x12345678) should be 0x12345678"
482+
);
483+
484+
// Negative value: 0xFFFFFFFF sign-extended to i64 (all bits set, -1)
485+
assert_eq!(
486+
extend32_mod.func_0(0xFFFFFFFFi64).unwrap(),
487+
-1i64,
488+
"i64.extend32_s(0xFFFFFFFF) should be -1"
489+
);
490+
491+
// Another negative: 0x80000000 sign-extended (bit 31 set)
492+
assert_eq!(
493+
extend32_mod.func_0(0x80000000i64).unwrap(),
494+
-2147483648i64,
495+
"i64.extend32_s(0x80000000) should be -2147483648"
496+
);
497+
498+
// Zero
499+
assert_eq!(
500+
extend32_mod.func_0(0).unwrap(),
501+
0i64,
502+
"i64.extend32_s(0) should be 0"
503+
);
504+
505+
// Max positive signed int
506+
assert_eq!(
507+
extend32_mod.func_0(0x7FFFFFFFi64).unwrap(),
508+
2147483647i64,
509+
"i64.extend32_s(0x7FFFFFFF) should be 2147483647"
510+
);
511+
}

crates/herkos/src/backend/safe.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -548,7 +548,7 @@ impl Backend for SafeBackend {
548548
}
549549

550550
fn emit_memory_fill(&self, dst: VarId, val: VarId, len: VarId) -> String {
551-
format!(" memory.fill({dst} as u32, {val} as u8, {len} as u32)?;")
551+
format!(" memory.fill({dst} as usize, {val} as u8, {len} as usize)?;")
552552
}
553553

554554
fn emit_memory_init(
@@ -558,7 +558,7 @@ impl Backend for SafeBackend {
558558
len: VarId,
559559
segment_const_name: &str,
560560
) -> String {
561-
format!(" memory.init_data_partial({dst} as u32, {segment_const_name}, {src_offset} as usize, {len} as usize)?;")
561+
format!(" memory.init_data_partial({dst} as usize, {segment_const_name}, {src_offset} as usize, {len} as usize)?;")
562562
}
563563

564564
fn emit_data_drop(&self, segment: u32) -> String {

0 commit comments

Comments
 (0)