wasm_interp: implement WASI random_get & refactor default imports

This commit is contained in:
Brian Carroll 2022-12-14 23:35:57 +00:00
parent eaa3f14fb0
commit b7fef386ee
No known key found for this signature in database
GPG key ID: 5C7B2EC4101703C0
8 changed files with 112 additions and 31 deletions

1
Cargo.lock generated
View file

@ -4525,6 +4525,7 @@ dependencies = [
"bitvec 1.0.1",
"bumpalo",
"clap 3.2.20",
"rand",
"roc_wasm_module",
]

View file

@ -1,7 +1,5 @@
use bumpalo::Bump;
use roc_wasm_interp::{
wasi, DefaultImportDispatcher, ImportDispatcher, Instance, WasiDispatcher, DEFAULT_IMPORTS,
};
use roc_wasm_interp::{wasi, DefaultImportDispatcher, ImportDispatcher, Instance, WasiDispatcher};
use roc_wasm_module::{Value, WasmModule};
const COMPILER_BYTES: &[u8] =
@ -24,6 +22,8 @@ impl<'a> ImportDispatcher for CompilerDispatcher<'a> {
arguments: &[Value],
compiler_memory: &mut [u8],
) -> Option<Value> {
dbg!(module_name, function_name);
let unknown = || {
panic!(
"TestDispatcher does not implement {}.{}",
@ -48,9 +48,13 @@ impl<'a> ImportDispatcher for CompilerDispatcher<'a> {
let module = WasmModule::preload(self.arena, app_bytes, require_reloc).unwrap();
let is_debug_mode = false;
let instance =
Instance::for_module(self.arena, &module, DEFAULT_IMPORTS, is_debug_mode)
.unwrap();
let instance = Instance::for_module(
self.arena,
&module,
DefaultImportDispatcher::default(),
is_debug_mode,
)
.unwrap();
self.app = Some((module, instance));
let ok = Value::I32(true as i32);
@ -133,17 +137,18 @@ fn run(src: &'static str) -> Result<String, String> {
arena: &arena,
src,
answer: String::new(),
wasi: WasiDispatcher { args: &[] },
wasi: WasiDispatcher::default(),
app: None,
result_addr: None,
};
let is_debug_mode = false;
let is_debug_mode = false; // logs every instruction!
Instance::for_module(&arena, &module, dispatcher, is_debug_mode).unwrap()
};
let len = Value::I32(src.len() as i32);
let wasm_ok: i32 = instance
.call_export(&module, "entrypoint_from_test", [])
.call_export(&module, "entrypoint_from_test", [len])
.unwrap()
.unwrap()
.expect_i32()

View file

@ -12,7 +12,7 @@ path = "src/main.rs"
[dependencies]
roc_wasm_module = { path = "../wasm_module" }
rand = "0.8.4"
bitvec.workspace = true
bumpalo.workspace = true
clap.workspace = true

View file

@ -8,6 +8,7 @@ pub mod wasi;
pub use instance::Instance;
pub use wasi::WasiDispatcher;
use rand::prelude::*;
use roc_wasm_module::{Value, ValueType, WasmModule};
use value_stack::ValueStack;
@ -22,9 +23,16 @@ pub trait ImportDispatcher {
) -> Option<Value>;
}
pub const DEFAULT_IMPORTS: DefaultImportDispatcher = DefaultImportDispatcher {
wasi: WasiDispatcher { args: &[] },
};
impl Default for DefaultImportDispatcher<'_> {
fn default() -> Self {
DefaultImportDispatcher {
wasi: WasiDispatcher {
args: &[],
rng: thread_rng(),
},
}
}
}
pub struct DefaultImportDispatcher<'a> {
wasi: WasiDispatcher<'a>,
@ -33,7 +41,10 @@ pub struct DefaultImportDispatcher<'a> {
impl<'a> DefaultImportDispatcher<'a> {
pub fn new(args: &'a [&'a String]) -> Self {
DefaultImportDispatcher {
wasi: WasiDispatcher { args },
wasi: WasiDispatcher {
args,
rng: thread_rng(),
},
}
}
}

View file

@ -8,7 +8,7 @@ mod test_i32;
mod test_i64;
mod test_mem;
use crate::{DefaultImportDispatcher, Instance, DEFAULT_IMPORTS};
use crate::{DefaultImportDispatcher, Instance};
use bumpalo::{collections::Vec, Bump};
use roc_wasm_module::{
opcodes::OpCode, Export, ExportType, SerialBuffer, Signature, Value, ValueType, WasmModule,
@ -18,7 +18,13 @@ pub fn default_state(arena: &Bump) -> Instance<DefaultImportDispatcher> {
let pages = 1;
let program_counter = 0;
let globals = [];
Instance::new(arena, pages, program_counter, globals, DEFAULT_IMPORTS)
Instance::new(
arena,
pages,
program_counter,
globals,
DefaultImportDispatcher::default(),
)
}
pub fn const_value(buf: &mut Vec<'_, u8>, value: Value) {
@ -85,7 +91,8 @@ where
std::fs::write(&filename, outfile_buf).unwrap();
}
let mut inst = Instance::for_module(&arena, &module, DEFAULT_IMPORTS, true).unwrap();
let mut inst =
Instance::for_module(&arena, &module, DefaultImportDispatcher::default(), false).unwrap();
let return_val = inst.call_export(&module, "test", []).unwrap().unwrap();

View file

@ -1,7 +1,7 @@
#![cfg(test)]
use super::{const_value, create_exported_function_no_locals, default_state};
use crate::{instance::Action, ImportDispatcher, Instance, ValueStack, DEFAULT_IMPORTS};
use crate::{instance::Action, DefaultImportDispatcher, ImportDispatcher, Instance, ValueStack};
use bumpalo::{collections::Vec, Bump};
use roc_wasm_module::sections::{Import, ImportDesc};
use roc_wasm_module::{
@ -623,7 +623,8 @@ fn test_call_return_no_args() {
println!("Wrote to {}", filename);
}
let mut inst = Instance::for_module(&arena, &module, DEFAULT_IMPORTS, true).unwrap();
let mut inst =
Instance::for_module(&arena, &module, DefaultImportDispatcher::default(), true).unwrap();
let return_val = inst
.call_export(&module, start_fn_name, [])
@ -762,7 +763,13 @@ fn test_call_indirect_help(table_index: u32, elem_index: u32) -> Value {
.unwrap();
}
let mut inst = Instance::for_module(&arena, &module, DEFAULT_IMPORTS, is_debug_mode).unwrap();
let mut inst = Instance::for_module(
&arena,
&module,
DefaultImportDispatcher::default(),
is_debug_mode,
)
.unwrap();
inst.call_export(&module, start_fn_name, [])
.unwrap()
.unwrap()

View file

@ -1,5 +1,5 @@
use super::create_exported_function_no_locals;
use crate::{Instance, DEFAULT_IMPORTS};
use crate::{DefaultImportDispatcher, Instance};
use bumpalo::{collections::Vec, Bump};
use roc_wasm_module::{
opcodes::OpCode,
@ -18,7 +18,7 @@ fn test_currentmemory() {
module.code.bytes.push(OpCode::CURRENTMEMORY as u8);
module.code.bytes.encode_i32(0);
let mut state = Instance::new(&arena, pages, pc, [], DEFAULT_IMPORTS);
let mut state = Instance::new(&arena, pages, pc, [], DefaultImportDispatcher::default());
state.execute_next_instruction(&module).unwrap();
assert_eq!(state.value_stack.pop(), Value::I32(3))
}
@ -37,7 +37,13 @@ fn test_growmemory() {
module.code.bytes.push(OpCode::GROWMEMORY as u8);
module.code.bytes.encode_i32(0);
let mut state = Instance::new(&arena, existing_pages, pc, [], DEFAULT_IMPORTS);
let mut state = Instance::new(
&arena,
existing_pages,
pc,
[],
DefaultImportDispatcher::default(),
);
state.execute_next_instruction(&module).unwrap();
state.execute_next_instruction(&module).unwrap();
assert_eq!(state.memory.len(), 5 * MemorySection::PAGE_SIZE as usize);
@ -79,7 +85,13 @@ fn test_load(load_op: OpCode, ty: ValueType, data: &[u8], addr: u32, offset: u32
std::fs::write("/tmp/roc/interp_load_test.wasm", outfile_buf).unwrap();
}
let mut inst = Instance::for_module(&arena, &module, DEFAULT_IMPORTS, is_debug_mode).unwrap();
let mut inst = Instance::for_module(
&arena,
&module,
DefaultImportDispatcher::default(),
is_debug_mode,
)
.unwrap();
inst.call_export(&module, start_fn_name, [])
.unwrap()
.unwrap()
@ -276,7 +288,13 @@ fn test_store<'a>(
buf.append_u8(OpCode::END as u8);
});
let mut inst = Instance::for_module(arena, module, DEFAULT_IMPORTS, is_debug_mode).unwrap();
let mut inst = Instance::for_module(
arena,
module,
DefaultImportDispatcher::default(),
is_debug_mode,
)
.unwrap();
inst.call_export(module, start_fn_name, []).unwrap();
inst.memory

View file

@ -1,3 +1,4 @@
use rand::prelude::*;
use roc_wasm_module::Value;
use std::io::{self, Write};
use std::process::exit;
@ -6,6 +7,13 @@ pub const MODULE_NAME: &str = "wasi_snapshot_preview1";
pub struct WasiDispatcher<'a> {
pub args: &'a [&'a String],
pub rng: ThreadRng,
}
impl Default for WasiDispatcher<'_> {
fn default() -> Self {
WasiDispatcher::new(&[])
}
}
/// Implementation of WASI syscalls
@ -14,7 +22,10 @@ pub struct WasiDispatcher<'a> {
/// https://github.com/wasm3/wasm3/blob/045040a97345e636b8be4f3086e6db59cdcc785f/source/extra/wasi_core.h
impl<'a> WasiDispatcher<'a> {
pub fn new(args: &'a [&'a String]) -> Self {
WasiDispatcher { args }
WasiDispatcher {
args,
rng: thread_rng(),
}
}
pub fn dispatch(
@ -61,8 +72,8 @@ impl<'a> WasiDispatcher<'a> {
}
"environ_get" => todo!("WASI {}({:?})", function_name, arguments),
"environ_sizes_get" => todo!("WASI {}({:?})", function_name, arguments),
"clock_res_get" => success_code, // this dummy implementation seems to be good enough
"clock_time_get" => success_code, // this dummy implementation seems to be good enough
"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),
"fd_allocate" => todo!("WASI {}({:?})", function_name, arguments),
"fd_close" => todo!("WASI {}({:?})", function_name, arguments),
@ -74,8 +85,20 @@ impl<'a> WasiDispatcher<'a> {
"fd_filestat_set_size" => todo!("WASI {}({:?})", function_name, arguments),
"fd_filestat_set_times" => todo!("WASI {}({:?})", function_name, arguments),
"fd_pread" => todo!("WASI {}({:?})", function_name, arguments),
"fd_prestat_get" => todo!("WASI {}({:?})", function_name, arguments),
"fd_prestat_dir_name" => todo!("WASI {}({:?})", function_name, arguments),
"fd_prestat_get" => {
// The preopened file descriptor to query
let fd = arguments[0].expect_i32().unwrap();
// Where the metadata will be written
let _ptr_buf = arguments[1].expect_i32().unwrap() as usize;
match fd {
0 | 1 | 2 => success_code,
_ => {
println!("WASI warning: file descriptor {} does not exist", fd);
Some(Value::I32(Errno::Badf as i32))
}
}
}
"fd_prestat_dir_name" => success_code,
"fd_pwrite" => todo!("WASI {}({:?})", function_name, arguments),
"fd_read" => todo!("WASI {}({:?})", function_name, arguments),
"fd_readdir" => todo!("WASI {}({:?})", function_name, arguments),
@ -156,7 +179,16 @@ impl<'a> WasiDispatcher<'a> {
}
"proc_raise" => todo!("WASI {}({:?})", function_name, arguments),
"sched_yield" => todo!("WASI {}({:?})", function_name, arguments),
"random_get" => todo!("WASI {}({:?})", function_name, arguments),
"random_get" => {
// A pointer to a buffer where the random bytes will be written
let ptr_buf = arguments[1].expect_i32().unwrap() as usize;
// The number of bytes that will be written
let buf_len = arguments[1].expect_i32().unwrap() as usize;
for i in 0..buf_len {
memory[ptr_buf + i] = self.rng.gen();
}
success_code
}
"sock_recv" => todo!("WASI {}({:?})", function_name, arguments),
"sock_send" => todo!("WASI {}({:?})", function_name, arguments),
"sock_shutdown" => todo!("WASI {}({:?})", function_name, arguments),