mirror of
https://github.com/roc-lang/roc.git
synced 2024-09-20 15:27:45 +03:00
add the memory.fill and memory.copy commands to our wasm interpreter
This commit is contained in:
parent
8619f4785b
commit
c7ccc2092a
@ -976,6 +976,47 @@ impl<'a, I: ImportDispatcher> Instance<'a, I> {
|
||||
self.value_store.push(Value::I32(-1));
|
||||
}
|
||||
}
|
||||
MEMORY => {
|
||||
// the first argument determines exactly which memory operation we have
|
||||
let op = module.code.bytes[self.program_counter];
|
||||
self.program_counter += 1;
|
||||
|
||||
match op {
|
||||
8 => {
|
||||
// memory.init
|
||||
todo!("WASM instruction: memory.init")
|
||||
}
|
||||
9 => {
|
||||
// data.drop x
|
||||
todo!("WASM instruction: data.drop")
|
||||
}
|
||||
10 => {
|
||||
// memory.copy
|
||||
let size = self.value_store.pop_u32()? as usize;
|
||||
let source = self.value_store.pop_u32()? as usize;
|
||||
let destination = self.value_store.pop_u32()? as usize;
|
||||
|
||||
// skip two zero bytes; in future versions of WebAssembly this byte may be
|
||||
// used to index additional memories
|
||||
self.program_counter += 2;
|
||||
|
||||
self.memory.copy_within(source..source + size, destination)
|
||||
}
|
||||
11 => {
|
||||
// memory.fill
|
||||
let size = self.value_store.pop_u32()? as usize;
|
||||
let byte_value = self.value_store.pop_u32()? as u8;
|
||||
let destination = self.value_store.pop_u32()? as usize;
|
||||
|
||||
// skip a zero byte; in future versions of WebAssembly this byte may be
|
||||
// used to index additional memories
|
||||
self.program_counter += 1;
|
||||
|
||||
self.memory[destination..][..size].fill(byte_value);
|
||||
}
|
||||
other => unreachable!("invalid memory instruction {:?}", other),
|
||||
}
|
||||
}
|
||||
I32CONST => {
|
||||
let value = i32::parse((), &module.code.bytes, &mut self.program_counter).unwrap();
|
||||
self.write_debug(value);
|
||||
|
@ -49,6 +49,72 @@ fn test_growmemory() {
|
||||
assert_eq!(state.memory.len(), 5 * MemorySection::PAGE_SIZE as usize);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_memory_fill() {
|
||||
let arena = Bump::new();
|
||||
let mut module = WasmModule::new(&arena);
|
||||
|
||||
let pages = 3;
|
||||
let pc = 0;
|
||||
module.memory = MemorySection::new(&arena, pages * MemorySection::PAGE_SIZE);
|
||||
|
||||
const SIZE: i32 = 16;
|
||||
let byte_value = 0xAA;
|
||||
let destination = 0x4;
|
||||
|
||||
let bytes = [OpCode::MEMORY as u8, 11, 0x0];
|
||||
module.code.bytes.extend(bytes);
|
||||
|
||||
let mut state = Instance::new(&arena, pages, pc, [], DefaultImportDispatcher::default());
|
||||
|
||||
state.value_store.push(Value::I32(destination));
|
||||
state.value_store.push(Value::I32(byte_value));
|
||||
state.value_store.push(Value::I32(SIZE));
|
||||
|
||||
// before the instruction, the memory is all zeros
|
||||
let slice = &state.memory[destination as usize..][..SIZE as usize];
|
||||
assert_eq!(slice, &[0; SIZE as usize]);
|
||||
|
||||
state.execute_next_instruction(&module).unwrap();
|
||||
|
||||
let slice = &state.memory[destination as usize..][..SIZE as usize];
|
||||
assert_eq!(slice, &[byte_value as u8; SIZE as usize])
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_memory_copy() {
|
||||
let arena = Bump::new();
|
||||
let mut module = WasmModule::new(&arena);
|
||||
|
||||
let pages = 3;
|
||||
let pc = 0;
|
||||
module.memory = MemorySection::new(&arena, pages * MemorySection::PAGE_SIZE);
|
||||
|
||||
const SIZE: i32 = 4;
|
||||
let source = 0x4;
|
||||
let destination = 0x8;
|
||||
|
||||
let bytes = [OpCode::MEMORY as u8, 10, 0x0, 0x0];
|
||||
module.code.bytes.extend(bytes);
|
||||
|
||||
let mut state = Instance::new(&arena, pages, pc, [], DefaultImportDispatcher::default());
|
||||
|
||||
state.value_store.push(Value::I32(destination));
|
||||
state.value_store.push(Value::I32(source));
|
||||
state.value_store.push(Value::I32(SIZE));
|
||||
|
||||
// before the instruction, the memory is all zeros
|
||||
let slice = &mut state.memory[source as usize..][..SIZE as usize];
|
||||
assert_eq!(slice, &[0; SIZE as usize]);
|
||||
|
||||
slice.fill(0xAA);
|
||||
|
||||
state.execute_next_instruction(&module).unwrap();
|
||||
|
||||
let slice = &state.memory[destination as usize..][..SIZE as usize];
|
||||
assert_eq!(slice, &[0xAA; SIZE as usize])
|
||||
}
|
||||
|
||||
fn test_load(load_op: OpCode, ty: ValueType, data: &[u8], addr: u32, offset: u32) -> Value {
|
||||
let arena = Bump::new();
|
||||
let mut module = WasmModule::new(&arena);
|
||||
|
@ -88,8 +88,25 @@ impl<'a> WasiDispatcher<'a> {
|
||||
|
||||
success_code
|
||||
}
|
||||
"environ_get" => todo!("WASI {}({:?})", function_name, arguments),
|
||||
"environ_sizes_get" => todo!("WASI {}({:?})", function_name, arguments),
|
||||
"environ_get" => {
|
||||
// `environ_sizes_get` always reports 0 environment variables
|
||||
// so we don't have to do anything here.
|
||||
|
||||
success_code
|
||||
}
|
||||
"environ_sizes_get" => {
|
||||
let num_env_ptr = arguments[0].expect_i32().unwrap() as usize;
|
||||
let size_env_ptr = arguments[1].expect_i32().unwrap() as usize;
|
||||
|
||||
// Calculate the total size required for environment variables
|
||||
let total_size = 0;
|
||||
let count = 0;
|
||||
|
||||
write_u32(memory, num_env_ptr, count);
|
||||
write_u32(memory, size_env_ptr, total_size as u32);
|
||||
|
||||
success_code
|
||||
}
|
||||
"clock_res_get" => success_code, // this dummy implementation seems to be good enough for some functions
|
||||
"clock_time_get" => success_code,
|
||||
"fd_advise" => todo!("WASI {}({:?})", function_name, arguments),
|
||||
|
@ -50,6 +50,7 @@ pub enum OpCode {
|
||||
I64STORE32 = 0x3e,
|
||||
CURRENTMEMORY = 0x3f,
|
||||
GROWMEMORY = 0x40,
|
||||
MEMORY = 0xFC,
|
||||
I32CONST = 0x41,
|
||||
I64CONST = 0x42,
|
||||
F32CONST = 0x43,
|
||||
@ -232,6 +233,7 @@ fn immediates_for(op: OpCode) -> Result<OpImmediates, String> {
|
||||
| I64STORE32 => Leb32x2,
|
||||
|
||||
CURRENTMEMORY | GROWMEMORY => Byte1,
|
||||
MEMORY => Leb32x2,
|
||||
|
||||
I32CONST => Leb32x1,
|
||||
I64CONST => Leb64x1,
|
||||
|
Loading…
Reference in New Issue
Block a user