diff --git a/examples/test.rs b/examples/test.rs index 163dda0142..3fcbf7c573 100644 --- a/examples/test.rs +++ b/examples/test.rs @@ -22,7 +22,7 @@ fn read_to_end>(path: P) -> io::Result> { fn maybe_main() -> Result<(), String> { let data = read_to_end("test.wasm").map_err(|e| e.to_string())?; let translated = translate(&data).map_err(|e| e.to_string())?; - let result: u32 = unsafe { translated.execute_func(0, (5u32, 3u32)) }; + let result: u32 = translated.execute_func(0, (5u32, 3u32)).unwrap(); println!("f(5, 3) = {}", result); Ok(()) diff --git a/src/function_body.rs b/src/function_body.rs index b5022506f3..20873b94e6 100644 --- a/src/function_body.rs +++ b/src/function_body.rs @@ -1,6 +1,6 @@ use backend::*; use error::Error; -use module::TranslationContext; +use module::FuncTyStore; use wasmparser::{FunctionBody, Operator, Type}; // TODO: Use own declared `Type` enum. @@ -92,7 +92,7 @@ impl ControlFrame { pub fn translate( session: &mut CodeGenSession, - translation_ctx: &TranslationContext, + translation_ctx: &FuncTyStore, func_idx: u32, body: &FunctionBody, ) -> Result<(), Error> diff --git a/src/module.rs b/src/module.rs index 888893bfef..0ac5c89a40 100644 --- a/src/module.rs +++ b/src/module.rs @@ -1,8 +1,55 @@ use backend::TranslatedCodeSection; use error::Error; +use std::borrow::Cow; use std::mem; use translate_sections; -use wasmparser::{FuncType, ModuleReader, SectionCode}; +use wasmparser::{FuncType, ModuleReader, SectionCode, Type}; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Signature { + params: Cow<'static, [Type]>, + returns: Cow<'static, [Type]>, +} + +impl PartialEq for Signature { + fn eq(&self, other: &FuncType) -> bool { + &self.params[..] == &other.params[..] && &self.returns[..] == &other.returns[..] + } +} + +pub trait AsValueType { + const TYPE: Type; +} + +pub trait TypeList { + const TYPE_LIST: &'static [Type]; +} + +impl TypeList for T +where + T: AsValueType, +{ + const TYPE_LIST: &'static [Type] = &[T::TYPE]; +} + +impl AsValueType for i32 { + const TYPE: Type = Type::I32; +} +impl AsValueType for i64 { + const TYPE: Type = Type::I64; +} +impl AsValueType for u32 { + const TYPE: Type = Type::I32; +} +impl AsValueType for u64 { + const TYPE: Type = Type::I64; +} +impl AsValueType for f32 { + const TYPE: Type = Type::F32; +} +impl AsValueType for f64 { + const TYPE: Type = Type::F64; +} pub trait FunctionArgs { unsafe fn call(self, start: *const u8) -> T; @@ -21,6 +68,10 @@ macro_rules! impl_function_args { } } + impl<$first: AsValueType, $($rest: AsValueType),*> TypeList for ($first, $($rest),*) { + const TYPE_LIST: &'static [Type] = &[$first::TYPE, $($rest::TYPE),*]; + } + impl_function_args!($($rest),*); }; () => { @@ -30,28 +81,57 @@ macro_rules! impl_function_args { func() } } + + impl TypeList for () { + const TYPE_LIST: &'static [Type] = &[]; + } }; } impl_function_args!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S); -#[derive(Default)] +#[derive(Default, Debug)] pub struct TranslatedModule { translated_code_section: Option, + types: FuncTyStore, + // Note: This vector should never be deallocated or reallocated or the pointer + // to its contents otherwise invalidated while the JIT'd code is still + // callable. memory: Option>, } +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum ExecutionError { + FuncIndexOutOfBounds, + TypeMismatch, +} + impl TranslatedModule { // For testing only. // TODO: Handle generic signatures. - pub unsafe fn execute_func(&self, func_idx: u32, args: Args) -> T { + pub fn execute_func( + &self, + func_idx: u32, + args: Args, + ) -> Result { let code_section = self .translated_code_section .as_ref() .expect("no code section"); + + if func_idx as usize >= self.types.func_ty_indicies.len() { + return Err(ExecutionError::FuncIndexOutOfBounds); + } + + let type_ = self.types.func_type(func_idx); + + if (&type_.params[..], &type_.returns[..]) != (Args::TYPE_LIST, T::TYPE_LIST) { + return Err(ExecutionError::TypeMismatch); + } + let start_buf = code_section.func_start(func_idx as usize); - args.call(start_buf) + Ok(unsafe { args.call(start_buf) }) } pub fn disassemble(&self) { @@ -62,13 +142,13 @@ impl TranslatedModule { } } -#[derive(Default)] -pub struct TranslationContext { +#[derive(Default, Debug)] +pub struct FuncTyStore { types: Vec, func_ty_indicies: Vec, } -impl TranslationContext { +impl FuncTyStore { pub fn func_type(&self, func_idx: u32) -> &FuncType { // TODO: This assumes that there is no imported functions. let func_ty_idx = self.func_ty_indicies[func_idx as usize]; @@ -89,11 +169,9 @@ pub fn translate(data: &[u8]) -> Result { } let mut section = reader.read()?; - let mut ctx = TranslationContext::default(); - if let SectionCode::Type = section.code { let types_reader = section.get_type_section_reader()?; - ctx.types = translate_sections::type_(types_reader)?; + output.types.types = translate_sections::type_(types_reader)?; reader.skip_custom_sections()?; if reader.eof() { @@ -115,7 +193,7 @@ pub fn translate(data: &[u8]) -> Result { if let SectionCode::Function = section.code { let functions = section.get_function_section_reader()?; - ctx.func_ty_indicies = translate_sections::function(functions)?; + output.types.func_ty_indicies = translate_sections::function(functions)?; reader.skip_custom_sections()?; if reader.eof() { @@ -205,7 +283,7 @@ pub fn translate(data: &[u8]) -> Result { let code = section.get_code_section_reader()?; output.translated_code_section = Some(translate_sections::code( code, - &ctx, + &output.types, output.memory.as_mut().map(|m| &mut m[..]), )?); @@ -221,5 +299,7 @@ pub fn translate(data: &[u8]) -> Result { translate_sections::data(data)?; } + assert!(reader.eof()); + Ok(output) } diff --git a/src/tests.rs b/src/tests.rs index 03a460e8f6..3a8717d72d 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -1,4 +1,4 @@ -use super::{translate, TranslatedModule}; +use super::{module::ExecutionError, translate, TranslatedModule}; use wabt; fn translate_wat(wat: &str) -> TranslatedModule { @@ -10,7 +10,7 @@ fn translate_wat(wat: &str) -> TranslatedModule { /// Execute the first function in the module. fn execute_wat(wat: &str, a: u32, b: u32) -> u32 { let translated = translate_wat(wat); - unsafe { translated.execute_func(0, (a, b)) } + translated.execute_func(0, (a, b)).unwrap() } #[test] @@ -30,51 +30,51 @@ mod op32 { const OP: &str = stringify!($op); lazy_static! { - static ref AS_PARAMS: TranslatedModule = translate_wat(&format!(" + static ref AS_PARAMS: TranslatedModule = translate_wat(&format!( + " (module (func (param i32) (param i32) (result i32) (i32.{op} (get_local 0) (get_local 1)))) - ", op = OP)); + ", + op = OP + )); } quickcheck! { fn as_params(a: i32, b: i32) -> bool { - unsafe { AS_PARAMS.execute_func::<(i32, i32), i32>(0, (a, b)) == $func(a, b) } + AS_PARAMS.execute_func::<(i32, i32), i32>(0, (a, b)) == Ok($func(a, b)) } fn lit_lit(a: i32, b: i32) -> bool { - let translated = translate_wat(&format!(" + let translated = translate_wat(&format!(" (module (func (result i32) (i32.{op} (i32.const {left}) (i32.const {right})))) ", op = OP, left = a, right = b)); static ONCE: Once = Once::new(); ONCE.call_once(|| translated.disassemble()); - unsafe { - translated.execute_func::<(), i32>(0, ()) == $func(a, b) - } + + translated.execute_func::<(), i32>(0, ()) == Ok($func(a, b)) } fn lit_reg(a: i32, b: i32) -> bool { - let translated = translate_wat(&format!(" + let translated = translate_wat(&format!(" (module (func (param i32) (result i32) (i32.{op} (i32.const {left}) (get_local 0)))) ", op = OP, left = a)); static ONCE: Once = Once::new(); ONCE.call_once(|| translated.disassemble()); - unsafe { - translated.execute_func::<(i32,), i32>(0, (b,)) == $func(a, b) - } + + translated.execute_func::<(i32,), i32>(0, (b,)) == Ok($func(a, b)) } fn reg_lit(a: i32, b: i32) -> bool { - let translated = translate_wat(&format!(" + let translated = translate_wat(&format!(" (module (func (param i32) (result i32) (i32.{op} (get_local 0) (i32.const {right})))) ", op = OP, right = b)); static ONCE: Once = Once::new(); ONCE.call_once(|| translated.disassemble()); - unsafe { - translated.execute_func::<(i32,), i32>(0, (a,)) == $func(a, b) - } + + translated.execute_func::<(i32,), i32>(0, (a,)) == Ok($func(a, b)) } } } @@ -122,16 +122,14 @@ mod op64 { quickcheck! { fn as_params(a: i64, b: i64) -> bool { - unsafe { AS_PARAMS.execute_func::<(i64, i64), $retty>(0, (a, b)) == ($func(a, b) as $retty) } + AS_PARAMS.execute_func::<(i64, i64), $retty>(0, (a, b)) == Ok($func(a, b) as $retty) } fn lit_lit(a: i64, b: i64) -> bool { - unsafe { - translate_wat(&format!(" - (module (func (result {retty}) - (i64.{op} (i64.const {left}) (i64.const {right})))) - ", retty = RETTY, op = OP, left = a, right = b)).execute_func::<(), $retty>(0, ()) == ($func(a, b) as $retty) - } + translate_wat(&format!(" + (module (func (result {retty}) + (i64.{op} (i64.const {left}) (i64.const {right})))) + ", retty = RETTY, op = OP, left = a, right = b)).execute_func::<(), $retty>(0, ()) == Ok($func(a, b) as $retty) } fn lit_reg(a: i64, b: i64) -> bool { @@ -143,18 +141,15 @@ mod op64 { ", retty = RETTY, op = OP, left = a)); static ONCE: Once = Once::new(); ONCE.call_once(|| translated.disassemble()); - unsafe { - translated.execute_func::<(i64,), $retty>(0, (b,)) == ($func(a, b) as $retty) - } + + translated.execute_func::<(i64,), $retty>(0, (b,)) == Ok($func(a, b) as $retty) } fn reg_lit(a: i64, b: i64) -> bool { - unsafe { - translate_wat(&format!(" - (module (func (param i64) (result {retty}) - (i64.{op} (get_local 0) (i64.const {right})))) - ", retty = RETTY, op = OP, right = b)).execute_func::<(i64,), $retty>(0, (a,)) == ($func(a, b) as $retty) - } + translate_wat(&format!(" + (module (func (param i64) (result {retty}) + (i64.{op} (get_local 0) (i64.const {right})))) + ", retty = RETTY, op = OP, right = b)).execute_func::<(i64,), $retty>(0, (a,)) == Ok($func(a, b) as $retty) } } } @@ -207,7 +202,7 @@ quickcheck! { static ref TRANSLATED: TranslatedModule = translate_wat(CODE); } - let out = unsafe { TRANSLATED.execute_func::<(u32, u32), u32>(0, (a, b)) }; + let out = TRANSLATED.execute_func::<(u32, u32), u32>(0, (a, b)).unwrap(); (a == b) == (out == 1) } @@ -234,9 +229,9 @@ quickcheck! { static ref TRANSLATED: TranslatedModule = translate_wat(CODE); } - let out = unsafe { TRANSLATED.execute_func::<(u32, u32), u32>(0, (a, b)) }; + let out = TRANSLATED.execute_func::<(u32, u32), u32>(0, (a, b)); - out == (if a == b { a } else { b }) + out == Ok(if a == b { a } else { b }) } } #[test] @@ -310,10 +305,10 @@ fn large_function() { { let translated = translate_wat(code); translated.disassemble(); - let out: u32 = unsafe { translated.execute_func(0, (5, 4, 3, 2, 1, 0)) }; + let out: Result = translated.execute_func(0, (5, 4, 3, 2, 1, 0)); out }, - 5 + Ok(5) ); } @@ -344,12 +339,9 @@ fn function_read_args_spill_to_stack() { { let translated = translate_wat(code); translated.disassemble(); - let out: u32 = unsafe { - translated.execute_func(0, (7u32, 6u32, 5u32, 4u32, 3u32, 2u32, 1u32, 0u32)) - }; - out + translated.execute_func(0, (7u32, 6u32, 5u32, 4u32, 3u32, 2u32, 1u32, 0u32)) }, - 7 + Ok(7u32) ); } @@ -408,18 +400,16 @@ macro_rules! mk_function_write_args_spill_to_stack { { let translated = translate_wat(&code); translated.disassemble(); - let out: $typ = unsafe { - translated.execute_func( - 0, - ( - 11 as $typ, 10 as $typ, 9 as $typ, 8 as $typ, 7 as $typ, 6 as $typ, - 5 as $typ, 4 as $typ, 3 as $typ, 2 as $typ, 1 as $typ, 0 as $typ, - ), - ) - }; + let out: Result<$typ, _> = translated.execute_func( + 0, + ( + 11 as $typ, 10 as $typ, 9 as $typ, 8 as $typ, 7 as $typ, 6 as $typ, + 5 as $typ, 4 as $typ, 3 as $typ, 2 as $typ, 1 as $typ, 0 as $typ, + ), + ); out }, - 11 + Ok(11) ); } }; @@ -461,7 +451,10 @@ fn br_block() { let translated = translate_wat(code); translated.disassemble(); - assert_eq!(unsafe { translated.execute_func::<(i32, i32), i32>(0, (5, 7)) }, 12); + assert_eq!( + translated.execute_func::<(i32, i32), i32>(0, (5, 7)), + Ok(12) + ); } // Tests discarding values on the value stack, while @@ -543,7 +536,7 @@ fn spec_loop() { let translated = translate_wat(code); translated.disassemble(); - unsafe { translated.execute_func::<(), ()>(0, ()) } + translated.execute_func::<(), ()>(0, ()).unwrap(); } quickcheck! { @@ -620,9 +613,8 @@ quickcheck! { } let n = n as i32; - unsafe { - TRANSLATED.execute_func::<(i32,), i32>(0, (n,)) == fac(n) - } + + TRANSLATED.execute_func::<(i32,), i32>(0, (n,)) == Ok(fac(n)) } } @@ -659,6 +651,44 @@ fn literals() { assert_eq!(execute_wat(code, 0, 0), 228); } +#[test] +fn wrong_type() { + let code = r#" +(module + (func (param i32) (param i64) (result i32) + (i32.const 228) + ) +) + "#; + + let translated = translate_wat(code); + assert_eq!( + translated + .execute_func::<_, ()>(0, (0u32, 0u32)) + .unwrap_err(), + ExecutionError::TypeMismatch + ); +} + +#[test] +fn wrong_index() { + let code = r#" +(module + (func (param i32) (param i64) (result i32) + (i32.const 228) + ) +) + "#; + + let translated = translate_wat(code); + assert_eq!( + translated + .execute_func::<_, ()>(10, (0u32, 0u32)) + .unwrap_err(), + ExecutionError::FuncIndexOutOfBounds + ); +} + const FIBONACCI: &str = r#" (module (func $fib (param $n i32) (result i32) @@ -722,14 +752,12 @@ fn fib() { translated.disassemble(); for x in 0..30 { - unsafe { - assert_eq!( - translated.execute_func::<_, u32>(0, (x,)), - fib(x), - "Failed for x={}", - x - ); - } + assert_eq!( + translated.execute_func::<_, u32>(0, (x,)), + Ok(fib(x)), + "Failed for x={}", + x + ); } } @@ -766,7 +794,7 @@ fn storage() { let translated = translate_wat(CODE); translated.disassemble(); - assert_eq!(unsafe { translated.execute_func::<(), i32>(0, ()) }, 1); + assert_eq!(translated.execute_func::<(), i32>(0, ()), Ok(1)); } #[bench] @@ -781,7 +809,7 @@ fn bench_fibonacci_run(b: &mut test::Bencher) { let wasm = wabt::wat2wasm(FIBONACCI).unwrap(); let module = translate(&wasm).unwrap(); - b.iter(|| unsafe { module.execute_func::<_, u32>(0, (20,)) }); + b.iter(|| module.execute_func::<_, u32>(0, (20,))); } #[bench] diff --git a/src/translate_sections.rs b/src/translate_sections.rs index 3ff236fbd4..5e95d5b338 100644 --- a/src/translate_sections.rs +++ b/src/translate_sections.rs @@ -1,7 +1,7 @@ use backend::{CodeGenSession, TranslatedCodeSection}; use error::Error; use function_body; -use module::TranslationContext; +use module::FuncTyStore; #[allow(unused_imports)] // for now use wasmparser::{ CodeSectionReader, Data, DataSectionReader, Element, ElementSectionReader, Export, @@ -12,11 +12,10 @@ use wasmparser::{ /// Parses the Type section of the wasm module. pub fn type_(types_reader: TypeSectionReader) -> Result, Error> { - let mut types = vec![]; - for entry in types_reader { - types.push(entry?); - } - Ok(types) + types_reader + .into_iter() + .map(|r| r.map_err(Into::into)) + .collect() } /// Parses the Import section of the wasm module. @@ -29,11 +28,10 @@ pub fn import(imports: ImportSectionReader) -> Result<(), Error> { /// Parses the Function section of the wasm module. pub fn function(functions: FunctionSectionReader) -> Result, Error> { - let mut func_ty_indicies = vec![]; - for entry in functions { - func_ty_indicies.push(entry?); - } - Ok(func_ty_indicies) + functions + .into_iter() + .map(|r| r.map_err(Into::into)) + .collect() } /// Parses the Table section of the wasm module. @@ -85,7 +83,7 @@ pub fn element(elements: ElementSectionReader) -> Result<(), Error> { /// Parses the Code section of the wasm module. pub fn code( code: CodeSectionReader, - translation_ctx: &TranslationContext, + translation_ctx: &FuncTyStore, memory: Option<&mut [u8]>, ) -> Result { let func_count = code.get_count();