From 167e4845ef38902327a5cdc770a736cf5061e983 Mon Sep 17 00:00:00 2001 From: Tobias Bucher Date: Mon, 27 Aug 2018 10:10:45 +0200 Subject: [PATCH 1/8] Proofread the library-level docs (#123) * Proofread the library-level docs * Fix extra word, remove mention of linear memory "space" --- src/lib.rs | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index bffaee0..104b7e0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,48 +1,48 @@ //! # wasmi //! -//! This library allows to load WebAssembly modules in binary format and invoke functions on them. +//! This library allows to load WebAssembly modules in binary format and invoke their functions. //! //! # Introduction //! -//! WebAssembly (wasm) is a safe, portable, compact format that designed for efficient execution. +//! WebAssembly (wasm) is a safe, portable and compact format that designed for efficient execution. //! -//! Wasm code is distributed in a form of modules, that contains definitions of: +//! Wasm code is distributed in the form of modules that contains definitions of: //! //! - functions, //! - global variables, -//! - linear memories, +//! - linear memory instances and //! - tables. //! -//! and this definitions can be imported. Also, each definition can be exported. +//! Each of these definitions can be imported and exported. //! -//! In addition to definitions, modules can define initialization data for their memories or tables that takes the -//! form of segments copied to given offsets. They can also define a `start` function that is automatically executed. +//! In addition to these definitions, modules can define initialization data for their memory or tables. This initialization data can take the +//! form of segments, copied to given offsets. They can also define a `start` function that is automatically executed when the module is loaded. //! //! ## Loading and Validation //! -//! Before execution a module should be validated. This process checks that module is well-formed +//! Before execution, a module must be validated. This process checks that module is well-formed //! and makes only allowed operations. //! -//! Valid modules can't access memory out of its sandbox, can't cause stack underflow -//! and can call functions only with correct signatures. +//! A valid module can't access memory out of its sandbox, can't cause stack underflows +//! and can only call functions with correct signatures. //! //! ## Instantiation //! -//! In order to execute code in wasm module it should be instatiated. +//! In order to execute code from a wasm module, it must be instatiated. //! Instantiation includes the following steps: //! -//! 1. Create an empty module instance, -//! 2. Resolve definition instances for each declared import in the module, -//! 3. Instantiate definitions declared in the module (e.g. allocate global variables, allocate linear memory, etc), -//! 4. Initialize memory and table contents by copiying segments into them, -//! 5. Execute `start` function, if any. +//! 1. Creating an empty module instance. +//! 2. Resolving the definition instances for each declared import in the module. +//! 3. Instantiating definitions declared in the module (e.g. allocate global variables, allocate linear memory, etc.). +//! 4. Initializing memory and table contents by copying segments into them. +//! 5. Executing the `start` function, if any. //! -//! After these steps, module instance are ready to execute functions. +//! After these steps, the module instance is ready to execute functions. //! //! ## Execution //! -//! It is allowed to only execute functions which are exported by a module. -//! Functions can either return a result or trap (e.g. there can't be linking-error at the middle of execution). +//! It only is allowed to call functions which are exported by the module. +//! Functions can either return a result or trap (e.g. there can't be linking error in the middle of the function execution). //! This property is ensured by the validation process. //! //! # Examples @@ -72,7 +72,7 @@ //! .expect("failed to load wasm"); //! //! // Instantiate a module with empty imports and -//! // asserting that there is no `start` function. +//! // assert that there is no `start` function. //! let instance = //! ModuleInstance::new( //! &module, @@ -81,7 +81,7 @@ //! .expect("failed to instantiate wasm module") //! .assert_no_start(); //! -//! // Finally, invoke exported function "test" with no parameters +//! // Finally, invoke the exported function "test" with no parameters //! // and empty external function executor. //! assert_eq!( //! instance.invoke_export( From 438eab9ada4e6c137405ca587c4c0a7b3dd43ed9 Mon Sep 17 00:00:00 2001 From: Sergey Pepyakin Date: Mon, 27 Aug 2018 11:11:21 +0300 Subject: [PATCH 2/8] Don't run cargo deadlinks for nightly (#121) --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 68a5cda..46c9de1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -19,7 +19,7 @@ env: install: # Install `cargo-deadlinks` unless it is currently installed. -- command -v cargo-deadlinks &> /dev/null || cargo install cargo-deadlinks +- command -v cargo-deadlinks &> /dev/null || cargo install --git https://github.com/deadlinks/cargo-deadlinks/ - if [ "$TRAVIS_RUST_VERSION" == "nightly" ]; then rustup target add wasm32-unknown-unknown; fi script: # Make sure nightly targets are not broken. From 36582c32b6c1bf6caa534ae5c880e1f9de513acb Mon Sep 17 00:00:00 2001 From: Sergey Pepyakin Date: Tue, 2 Oct 2018 11:01:18 +0100 Subject: [PATCH 3/8] Transfer function (#128) --- src/memory.rs | 67 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 66 insertions(+), 1 deletion(-) diff --git a/src/memory.rs b/src/memory.rs index 0854d2a..4b859d6 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -352,6 +352,29 @@ impl MemoryInstance { Ok(()) } + /// Copy memory between two (possibly distinct) memory instances. + /// + /// If the same memory instance passed as `src` and `dst` then usual `copy` will be used. + pub fn transfer(src: &MemoryRef, src_offset: usize, dst: &MemoryRef, dst_offset: usize, len: usize) -> Result<(), Error> { + if Rc::ptr_eq(&src.0, &dst.0) { + // `transfer` is invoked with with same source and destination. Let's assume that regions may + // overlap and use `copy`. + return src.copy(src_offset, dst_offset, len); + } + + // Because memory references point to different memory instances, it is safe to `borrow_mut` + // both buffers at once (modulo `with_direct_access_mut`). + let mut src_buffer = src.buffer.borrow_mut(); + let mut dst_buffer = dst.buffer.borrow_mut(); + + let src_range = src.checked_region(&mut src_buffer, src_offset, len)?.range(); + let dst_range = dst.checked_region(&mut dst_buffer, dst_offset, len)?.range(); + + dst_buffer[dst_range].copy_from_slice(&src_buffer[src_range]); + + Ok(()) + } + /// Fill the memory region with the specified value. /// /// Semantically equivalent to `memset`. @@ -430,7 +453,8 @@ pub fn validate_memory(initial: Pages, maximum: Option) -> Result<(), Str #[cfg(test)] mod tests { - use super::{MemoryInstance, LINEAR_MEMORY_PAGE_SIZE}; + use super::{MemoryRef, MemoryInstance, LINEAR_MEMORY_PAGE_SIZE}; + use std::rc::Rc; use Error; use memory_units::Pages; @@ -533,6 +557,47 @@ mod tests { } } + #[test] + fn transfer_works() { + let src = MemoryRef(Rc::new(create_memory(&[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]))); + let dst = MemoryRef(Rc::new(create_memory(&[10, 11, 12, 13, 14, 15, 16, 17, 18, 19]))); + + MemoryInstance::transfer(&src, 4, &dst, 0, 3).unwrap(); + + assert_eq!(src.get(0, 10).unwrap(), &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); + assert_eq!(dst.get(0, 10).unwrap(), &[4, 5, 6, 13, 14, 15, 16, 17, 18, 19]); + } + + #[test] + fn transfer_still_works_with_same_memory() { + let src = MemoryRef(Rc::new(create_memory(&[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]))); + + MemoryInstance::transfer(&src, 4, &src, 0, 3).unwrap(); + + assert_eq!(src.get(0, 10).unwrap(), &[4, 5, 6, 3, 4, 5, 6, 7, 8, 9]); + } + + #[test] + fn transfer_oob_with_same_memory_errors() { + let src = MemoryRef(Rc::new(create_memory(&[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]))); + assert!(MemoryInstance::transfer(&src, 65535, &src, 0, 3).is_err()); + + // Check that memories content left untouched + assert_eq!(src.get(0, 10).unwrap(), &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); + } + + #[test] + fn transfer_oob_errors() { + let src = MemoryRef(Rc::new(create_memory(&[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]))); + let dst = MemoryRef(Rc::new(create_memory(&[10, 11, 12, 13, 14, 15, 16, 17, 18, 19]))); + + assert!(MemoryInstance::transfer(&src, 65535, &dst, 0, 3).is_err()); + + // Check that memories content left untouched + assert_eq!(src.get(0, 10).unwrap(), &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); + assert_eq!(dst.get(0, 10).unwrap(), &[10, 11, 12, 13, 14, 15, 16, 17, 18, 19]); + } + #[test] fn clear() { let mem = create_memory(&[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); From 9170303aad83cd2f4d58b5f84b20a34c0122e7c1 Mon Sep 17 00:00:00 2001 From: Sergey Pepyakin Date: Tue, 2 Oct 2018 11:36:23 +0100 Subject: [PATCH 4/8] Bump wasmi to 0.4.1 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 3938432..2c05e50 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmi" -version = "0.4.0" +version = "0.4.1" authors = ["Nikolay Volf ", "Svyatoslav Nikolsky ", "Sergey Pepyakin "] license = "MIT/Apache-2.0" readme = "README.md" From 7509477a61c0cc5a6983a22e013b2404eeae7f15 Mon Sep 17 00:00:00 2001 From: Will Glynn Date: Wed, 10 Oct 2018 12:02:27 -0500 Subject: [PATCH 5/8] Hide instruction storage details (#129) * Hide Instructions implementation behind an iterator * Hide instruction encoding behind isa::Instructions::push() * Consistently use u32 for program counter storage * Refer to instructions by position rather than index --- src/isa.rs | 80 +++++++++++++++++++++++++++++++++++++++- src/runner.rs | 20 ++++++---- src/validation/func.rs | 55 +++++++-------------------- src/validation/tests.rs | 82 ++++++++++++++++++++++++----------------- 4 files changed, 153 insertions(+), 84 deletions(-) diff --git a/src/isa.rs b/src/isa.rs index 39aaa9e..646db4a 100644 --- a/src/isa.rs +++ b/src/isa.rs @@ -93,6 +93,22 @@ pub struct Target { pub drop_keep: DropKeep, } +/// A relocation entry that specifies. +#[derive(Debug)] +pub enum Reloc { + /// Patch the destination of the branch instruction (br, br_eqz, br_nez) + /// at the specified pc. + Br { + pc: u32, + }, + /// Patch the specified destination index inside of br_table instruction at + /// the specified pc. + BrTable { + pc: u32, + idx: usize, + }, +} + #[derive(Debug, Clone, PartialEq)] pub enum Instruction { /// Push a local variable or an argument from the specified depth. @@ -299,5 +315,67 @@ pub enum Instruction { #[derive(Debug, Clone)] pub struct Instructions { - pub code: Vec, + vec: Vec, +} + +impl Instructions { + pub fn with_capacity(capacity: usize) -> Self { + Instructions { + vec: Vec::with_capacity(capacity), + } + } + + pub fn current_pc(&self) -> u32 { + self.vec.len() as u32 + } + + pub fn push(&mut self, instruction: Instruction) { + self.vec.push(instruction); + } + + pub fn patch_relocation(&mut self, reloc: Reloc, dst_pc: u32) { + match reloc { + Reloc::Br { pc } => match self.vec[pc as usize] { + Instruction::Br(ref mut target) + | Instruction::BrIfEqz(ref mut target) + | Instruction::BrIfNez(ref mut target) => target.dst_pc = dst_pc, + _ => panic!("branch relocation points to a non-branch instruction"), + }, + Reloc::BrTable { pc, idx } => match self.vec[pc as usize] { + Instruction::BrTable(ref mut targets) => targets[idx].dst_pc = dst_pc, + _ => panic!("brtable relocation points to not brtable instruction"), + } + } + } + + pub fn iterate_from(&self, position: u32) -> InstructionIter { + InstructionIter{ + instructions: &self.vec, + position, + } + } +} + +pub struct InstructionIter<'a> { + instructions: &'a [Instruction], + position: u32, +} + +impl<'a> InstructionIter<'a> { + #[inline] + pub fn position(&self) -> u32 { + self.position + } +} + +impl<'a> Iterator for InstructionIter<'a> { + type Item = &'a Instruction; + + #[inline] + fn next(&mut self) -> Option<::Item> { + self.instructions.get(self.position as usize).map(|instruction| { + self.position += 1; + instruction + }) + } } diff --git a/src/runner.rs b/src/runner.rs index 5a9ec84..8fa1a6d 100644 --- a/src/runner.rs +++ b/src/runner.rs @@ -175,7 +175,7 @@ impl Interpreter { let function_return = self.do_run_function( &mut function_context, - &function_body.code.code, + &function_body.code, ).map_err(Trap::new)?; match function_return { @@ -229,18 +229,24 @@ impl Interpreter { } } - fn do_run_function(&mut self, function_context: &mut FunctionContext, instructions: &[isa::Instruction]) -> Result { + fn do_run_function(&mut self, function_context: &mut FunctionContext, instructions: &isa::Instructions) + -> Result + { + let mut iter = instructions.iterate_from(function_context.position); loop { - let instruction = &instructions[function_context.position]; + let instruction = iter.next().expect("instruction"); match self.run_instruction(function_context, instruction)? { - InstructionOutcome::RunNextInstruction => function_context.position += 1, + InstructionOutcome::RunNextInstruction => { + function_context.position = iter.position(); + }, InstructionOutcome::Branch(target) => { - function_context.position = target.dst_pc as usize; + function_context.position = target.dst_pc; + iter = instructions.iterate_from(function_context.position); self.value_stack.drop_keep(target.drop_keep); }, InstructionOutcome::ExecuteCall(func_ref) => { - function_context.position += 1; + function_context.position = iter.position(); return Ok(RunResult::NestedCall(func_ref)); }, InstructionOutcome::Return(drop_keep) => { @@ -1077,7 +1083,7 @@ struct FunctionContext { pub module: ModuleRef, pub memory: Option, /// Current instruction position. - pub position: usize, + pub position: u32, } impl FunctionContext { diff --git a/src/validation/func.rs b/src/validation/func.rs index 7107e05..f7a975e 100644 --- a/src/validation/func.rs +++ b/src/validation/func.rs @@ -1406,9 +1406,7 @@ impl<'a> FunctionValidationContext<'a> { } fn into_code(self) -> isa::Instructions { - isa::Instructions { - code: self.sink.into_inner(), - } + self.sink.into_inner() } } @@ -1628,22 +1626,6 @@ struct Target { drop_keep: isa::DropKeep, } -/// A relocation entry that specifies. -#[derive(Debug)] -enum Reloc { - /// Patch the destination of the branch instruction (br, br_eqz, br_nez) - /// at the specified pc. - Br { - pc: u32, - }, - /// Patch the specified destination index inside of br_table instruction at - /// the specified pc. - BrTable { - pc: u32, - idx: usize, - }, -} - /// Identifier of a label. #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] struct LabelId(usize); @@ -1655,23 +1637,23 @@ enum Label { } struct Sink { - ins: Vec, - labels: Vec<(Label, Vec)>, + ins: isa::Instructions, + labels: Vec<(Label, Vec)>, } impl Sink { fn with_instruction_capacity(capacity: usize) -> Sink { Sink { - ins: Vec::with_capacity(capacity), + ins: isa::Instructions::with_capacity(capacity), labels: Vec::new(), } } fn cur_pc(&self) -> u32 { - self.ins.len() as u32 + self.ins.current_pc() } - fn pc_or_placeholder Reloc>(&mut self, label: LabelId, reloc_creator: F) -> u32 { + fn pc_or_placeholder isa::Reloc>(&mut self, label: LabelId, reloc_creator: F) -> u32 { match self.labels[label.0] { (Label::Resolved(dst_pc), _) => dst_pc, (Label::NotResolved, ref mut unresolved) => { @@ -1692,7 +1674,7 @@ impl Sink { drop_keep, } = target; let pc = self.cur_pc(); - let dst_pc = self.pc_or_placeholder(label, || Reloc::Br { pc }); + let dst_pc = self.pc_or_placeholder(label, || isa::Reloc::Br { pc }); self.ins.push(isa::Instruction::Br(isa::Target { dst_pc, drop_keep: drop_keep.into(), @@ -1705,7 +1687,7 @@ impl Sink { drop_keep, } = target; let pc = self.cur_pc(); - let dst_pc = self.pc_or_placeholder(label, || Reloc::Br { pc }); + let dst_pc = self.pc_or_placeholder(label, || isa::Reloc::Br { pc }); self.ins.push(isa::Instruction::BrIfEqz(isa::Target { dst_pc, drop_keep: drop_keep.into(), @@ -1718,7 +1700,7 @@ impl Sink { drop_keep, } = target; let pc = self.cur_pc(); - let dst_pc = self.pc_or_placeholder(label, || Reloc::Br { pc }); + let dst_pc = self.pc_or_placeholder(label, || isa::Reloc::Br { pc }); self.ins.push(isa::Instruction::BrIfNez(isa::Target { dst_pc, drop_keep: drop_keep.into(), @@ -1731,7 +1713,7 @@ impl Sink { let pc = self.cur_pc(); let mut isa_targets = Vec::new(); for (idx, &Target { label, drop_keep }) in targets.iter().chain(iter::once(&default)).enumerate() { - let dst_pc = self.pc_or_placeholder(label, || Reloc::BrTable { pc, idx }); + let dst_pc = self.pc_or_placeholder(label, || isa::Reloc::BrTable { pc, idx }); isa_targets.push( isa::Target { dst_pc, @@ -1768,26 +1750,15 @@ impl Sink { // particular label. let unresolved_rels = mem::replace(&mut self.labels[label.0].1, Vec::new()); for reloc in unresolved_rels { - match reloc { - Reloc::Br { pc } => match self.ins[pc as usize] { - isa::Instruction::Br(ref mut target) - | isa::Instruction::BrIfEqz(ref mut target) - | isa::Instruction::BrIfNez(ref mut target) => target.dst_pc = dst_pc, - _ => panic!("branch relocation points to a non-branch instruction"), - }, - Reloc::BrTable { pc, idx } => match self.ins[pc as usize] { - isa::Instruction::BrTable(ref mut targets) => targets[idx].dst_pc = dst_pc, - _ => panic!("brtable relocation points to not brtable instruction"), - } - } + self.ins.patch_relocation(reloc, dst_pc); } // Mark this label as resolved. self.labels[label.0] = (Label::Resolved(dst_pc), Vec::new()); } - /// Consume this Sink and returns isa::Instruction. - fn into_inner(self) -> Vec { + /// Consume this Sink and returns isa::Instructions. + fn into_inner(self) -> isa::Instructions { // At this moment all labels should be resolved. assert!({ self.labels.iter().all(|(state, unresolved)| diff --git a/src/validation/tests.rs b/src/validation/tests.rs index 7feb726..9a8829e 100644 --- a/src/validation/tests.rs +++ b/src/validation/tests.rs @@ -310,15 +310,29 @@ fn validate(wat: &str) -> ValidatedModule { validated_module } -fn compile(wat: &str) -> Vec { +fn compile(wat: &str) -> (Vec, Vec) { let validated_module = validate(wat); let code = &validated_module.code_map[0]; - code.code.clone() + + let mut instructions = Vec::new(); + let mut pcs = Vec::new(); + let mut iter = code.iterate_from(0); + loop { + let pc = iter.position(); + if let Some(instruction) = iter.next() { + instructions.push(instruction.clone()); + pcs.push(pc); + } else { + break + } + } + + (instructions, pcs) } #[test] fn implicit_return_no_value() { - let code = compile(r#" + let (code, _) = compile(r#" (module (func (export "call") ) @@ -337,7 +351,7 @@ fn implicit_return_no_value() { #[test] fn implicit_return_with_value() { - let code = compile(r#" + let (code, _) = compile(r#" (module (func (export "call") (result i32) i32.const 0 @@ -358,7 +372,7 @@ fn implicit_return_with_value() { #[test] fn implicit_return_param() { - let code = compile(r#" + let (code, _) = compile(r#" (module (func (export "call") (param i32) ) @@ -377,7 +391,7 @@ fn implicit_return_param() { #[test] fn get_local() { - let code = compile(r#" + let (code, _) = compile(r#" (module (func (export "call") (param i32) (result i32) get_local 0 @@ -398,7 +412,7 @@ fn get_local() { #[test] fn explicit_return() { - let code = compile(r#" + let (code, _) = compile(r#" (module (func (export "call") (param i32) (result i32) get_local 0 @@ -424,7 +438,7 @@ fn explicit_return() { #[test] fn add_params() { - let code = compile(r#" + let (code, _) = compile(r#" (module (func (export "call") (param i32) (param i32) (result i32) get_local 0 @@ -454,7 +468,7 @@ fn add_params() { #[test] fn drop_locals() { - let code = compile(r#" + let (code, _) = compile(r#" (module (func (export "call") (param i32) (local i32) @@ -478,7 +492,7 @@ fn drop_locals() { #[test] fn if_without_else() { - let code = compile(r#" + let (code, pcs) = compile(r#" (module (func (export "call") (param i32) (result i32) i32.const 1 @@ -495,7 +509,7 @@ fn if_without_else() { vec![ isa::Instruction::I32Const(1), isa::Instruction::BrIfEqz(isa::Target { - dst_pc: 4, + dst_pc: pcs[4], drop_keep: isa::DropKeep { drop: 0, keep: isa::Keep::None, @@ -517,7 +531,7 @@ fn if_without_else() { #[test] fn if_else() { - let code = compile(r#" + let (code, pcs) = compile(r#" (module (func (export "call") (local i32) @@ -537,7 +551,7 @@ fn if_else() { vec![ isa::Instruction::I32Const(1), isa::Instruction::BrIfEqz(isa::Target { - dst_pc: 5, + dst_pc: pcs[5], drop_keep: isa::DropKeep { drop: 0, keep: isa::Keep::None, @@ -546,7 +560,7 @@ fn if_else() { isa::Instruction::I32Const(2), isa::Instruction::SetLocal(1), isa::Instruction::Br(isa::Target { - dst_pc: 7, + dst_pc: pcs[7], drop_keep: isa::DropKeep { drop: 0, keep: isa::Keep::None, @@ -564,7 +578,7 @@ fn if_else() { #[test] fn if_else_returns_result() { - let code = compile(r#" + let (code, pcs) = compile(r#" (module (func (export "call") i32.const 1 @@ -582,7 +596,7 @@ fn if_else_returns_result() { vec![ isa::Instruction::I32Const(1), isa::Instruction::BrIfEqz(isa::Target { - dst_pc: 4, + dst_pc: pcs[4], drop_keep: isa::DropKeep { drop: 0, keep: isa::Keep::None, @@ -590,7 +604,7 @@ fn if_else_returns_result() { }), isa::Instruction::I32Const(2), isa::Instruction::Br(isa::Target { - dst_pc: 5, + dst_pc: pcs[5], drop_keep: isa::DropKeep { drop: 0, keep: isa::Keep::None, @@ -608,7 +622,7 @@ fn if_else_returns_result() { #[test] fn if_else_branch_from_true_branch() { - let code = compile(r#" + let (code, pcs) = compile(r#" (module (func (export "call") i32.const 1 @@ -630,7 +644,7 @@ fn if_else_branch_from_true_branch() { vec![ isa::Instruction::I32Const(1), isa::Instruction::BrIfEqz(isa::Target { - dst_pc: 8, + dst_pc: pcs[8], drop_keep: isa::DropKeep { drop: 0, keep: isa::Keep::None, @@ -639,7 +653,7 @@ fn if_else_branch_from_true_branch() { isa::Instruction::I32Const(1), isa::Instruction::I32Const(1), isa::Instruction::BrIfNez(isa::Target { - dst_pc: 9, + dst_pc: pcs[9], drop_keep: isa::DropKeep { drop: 0, keep: isa::Keep::Single, @@ -648,7 +662,7 @@ fn if_else_branch_from_true_branch() { isa::Instruction::Drop, isa::Instruction::I32Const(2), isa::Instruction::Br(isa::Target { - dst_pc: 9, + dst_pc: pcs[9], drop_keep: isa::DropKeep { drop: 0, keep: isa::Keep::None, @@ -666,7 +680,7 @@ fn if_else_branch_from_true_branch() { #[test] fn if_else_branch_from_false_branch() { - let code = compile(r#" + let (code, pcs) = compile(r#" (module (func (export "call") i32.const 1 @@ -688,7 +702,7 @@ fn if_else_branch_from_false_branch() { vec![ isa::Instruction::I32Const(1), isa::Instruction::BrIfEqz(isa::Target { - dst_pc: 4, + dst_pc: pcs[4], drop_keep: isa::DropKeep { drop: 0, keep: isa::Keep::None, @@ -696,7 +710,7 @@ fn if_else_branch_from_false_branch() { }), isa::Instruction::I32Const(1), isa::Instruction::Br(isa::Target { - dst_pc: 9, + dst_pc: pcs[9], drop_keep: isa::DropKeep { drop: 0, keep: isa::Keep::None, @@ -705,7 +719,7 @@ fn if_else_branch_from_false_branch() { isa::Instruction::I32Const(2), isa::Instruction::I32Const(1), isa::Instruction::BrIfNez(isa::Target { - dst_pc: 9, + dst_pc: pcs[9], drop_keep: isa::DropKeep { drop: 0, keep: isa::Keep::Single, @@ -724,7 +738,7 @@ fn if_else_branch_from_false_branch() { #[test] fn loop_() { - let code = compile(r#" + let (code, _) = compile(r#" (module (func (export "call") loop (result i32) @@ -759,7 +773,7 @@ fn loop_() { #[test] fn loop_empty() { - let code = compile(r#" + let (code, _) = compile(r#" (module (func (export "call") loop @@ -780,7 +794,7 @@ fn loop_empty() { #[test] fn brtable() { - let code = compile(r#" + let (code, pcs) = compile(r#" (module (func (export "call") block $1 @@ -806,7 +820,7 @@ fn brtable() { }, }, isa::Target { - dst_pc: 2, + dst_pc: pcs[2], drop_keep: isa::DropKeep { drop: 0, keep: isa::Keep::None, @@ -824,7 +838,7 @@ fn brtable() { #[test] fn brtable_returns_result() { - let code = compile(r#" + let (code, pcs) = compile(r#" (module (func (export "call") block $1 (result i32) @@ -847,14 +861,14 @@ fn brtable_returns_result() { isa::Instruction::BrTable( vec![ isa::Target { - dst_pc: 3, + dst_pc: pcs[3], drop_keep: isa::DropKeep { drop: 0, keep: isa::Keep::Single, }, }, isa::Target { - dst_pc: 4, + dst_pc: pcs[4], drop_keep: isa::DropKeep { keep: isa::Keep::Single, drop: 0, @@ -874,7 +888,7 @@ fn brtable_returns_result() { #[test] fn wabt_example() { - let code = compile(r#" + let (code, pcs) = compile(r#" (module (func (export "call") (param i32) (result i32) block $exit @@ -893,7 +907,7 @@ fn wabt_example() { vec![ isa::Instruction::GetLocal(1), isa::Instruction::BrIfNez(isa::Target { - dst_pc: 4, + dst_pc: pcs[4], drop_keep: isa::DropKeep { drop: 0, keep: isa::Keep::None, From ad4236263a0b8d89e0a0f7d9adef1901487f0c01 Mon Sep 17 00:00:00 2001 From: Sergey Pepyakin Date: Fri, 19 Oct 2018 16:55:07 +0200 Subject: [PATCH 6/8] Don't update PC on every instruction (#130) --- src/runner.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/runner.rs b/src/runner.rs index 8fa1a6d..d2b1ce4 100644 --- a/src/runner.rs +++ b/src/runner.rs @@ -237,12 +237,9 @@ impl Interpreter { let instruction = iter.next().expect("instruction"); match self.run_instruction(function_context, instruction)? { - InstructionOutcome::RunNextInstruction => { - function_context.position = iter.position(); - }, + InstructionOutcome::RunNextInstruction => {}, InstructionOutcome::Branch(target) => { - function_context.position = target.dst_pc; - iter = instructions.iterate_from(function_context.position); + iter = instructions.iterate_from(target.dst_pc); self.value_stack.drop_keep(target.drop_keep); }, InstructionOutcome::ExecuteCall(func_ref) => { From 3854ecdad6b25d7ef0552e282260415e82955d6e Mon Sep 17 00:00:00 2001 From: Jef Date: Thu, 25 Oct 2018 14:17:25 +0200 Subject: [PATCH 7/8] Remove some unsafety (#131) * Remove unsafety * Remove transmutes and pointer casts --- src/memory.rs | 4 ++-- src/value.rs | 5 ++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/memory.rs b/src/memory.rs index 4b859d6..730139c 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -316,7 +316,7 @@ impl MemoryInstance { unsafe { ::std::ptr::copy( buffer[read_region.range()].as_ptr(), - buffer[write_region.range()].as_ptr() as *mut _, + buffer[write_region.range()].as_mut_ptr(), len, )} @@ -345,7 +345,7 @@ impl MemoryInstance { unsafe { ::std::ptr::copy_nonoverlapping( buffer[read_region.range()].as_ptr(), - buffer[write_region.range()].as_ptr() as *mut _, + buffer[write_region.range()].as_mut_ptr(), len, )} diff --git a/src/value.rs b/src/value.rs index 9be2d41..88b424d 100644 --- a/src/value.rs +++ b/src/value.rs @@ -1,7 +1,6 @@ use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; use nan_preserving_float::{F32, F64}; use std::io; -use std::mem::transmute; use std::{f32, i32, i64, u32, u64}; use TrapKind; @@ -547,11 +546,11 @@ impl TransmuteInto for i64 { } impl TransmuteInto for u32 { - fn transmute_into(self) -> i32 { unsafe { transmute(self) } } + fn transmute_into(self) -> i32 { self as _ } } impl TransmuteInto for u64 { - fn transmute_into(self) -> i64 { unsafe { transmute(self) } } + fn transmute_into(self) -> i64 { self as _ } } impl LittleEndianConvert for i8 { From 2f7505d120894d57501ba4b321f5f608e7711934 Mon Sep 17 00:00:00 2001 From: Sergey Pepyakin Date: Fri, 26 Oct 2018 15:03:01 +0200 Subject: [PATCH 8/8] Travis maintenance (#132) * Use gcc-8 for builds * Don't run cargo-deadlinks * Update wabt to 0.6. --- .travis.yml | 8 +++----- Cargo.toml | 2 +- benches/Cargo.toml | 2 +- fuzz/Cargo.toml | 2 +- hfuzz/Cargo.toml | 2 +- 5 files changed, 7 insertions(+), 9 deletions(-) diff --git a/.travis.yml b/.travis.yml index 46c9de1..ab78915 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,15 +11,13 @@ addons: sources: - ubuntu-toolchain-r-test packages: - - gcc-6 - - g++-6 + - gcc-8 + - g++-8 - cmake env: -- CC=/usr/bin/gcc-6 CXX=/usr/bin/g++-6 +- CC=/usr/bin/gcc-8 CXX=/usr/bin/g++-8 install: -# Install `cargo-deadlinks` unless it is currently installed. -- command -v cargo-deadlinks &> /dev/null || cargo install --git https://github.com/deadlinks/cargo-deadlinks/ - if [ "$TRAVIS_RUST_VERSION" == "nightly" ]; then rustup target add wasm32-unknown-unknown; fi script: # Make sure nightly targets are not broken. diff --git a/Cargo.toml b/Cargo.toml index 2c05e50..e95ad17 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,4 +18,4 @@ nan-preserving-float = "0.1.0" [dev-dependencies] assert_matches = "1.1" -wabt = "0.4" +wabt = "0.6" diff --git a/benches/Cargo.toml b/benches/Cargo.toml index f3d3fe8..de60346 100644 --- a/benches/Cargo.toml +++ b/benches/Cargo.toml @@ -6,7 +6,7 @@ authors = ["Sergey Pepyakin "] [dependencies] wasmi = { path = ".." } assert_matches = "1.2" -wabt = "0.3" +wabt = "0.6" [profile.bench] debug = true diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml index 1dd173d..4431140 100644 --- a/fuzz/Cargo.toml +++ b/fuzz/Cargo.toml @@ -10,7 +10,7 @@ cargo-fuzz = true [dependencies] wasmi = { path = ".." } -wabt = "0.2.0" +wabt = "0.6.0" wasmparser = "0.14.1" tempdir = "0.3.6" diff --git a/hfuzz/Cargo.toml b/hfuzz/Cargo.toml index 473364f..266b767 100644 --- a/hfuzz/Cargo.toml +++ b/hfuzz/Cargo.toml @@ -7,4 +7,4 @@ authors = ["Sergey Pepyakin "] honggfuzz = "=0.5.9" # Strict equal since hfuzz requires dep and cmd versions to match. wasmi = { path = ".." } tempdir = "0.3.6" -wabt = "0.2.0" +wabt = "0.6.0"