diff --git a/benches/Cargo.toml b/benches/Cargo.toml index 3626446..f3d3fe8 100644 --- a/benches/Cargo.toml +++ b/benches/Cargo.toml @@ -7,3 +7,6 @@ authors = ["Sergey Pepyakin "] wasmi = { path = ".." } assert_matches = "1.2" wabt = "0.3" + +[profile.bench] +debug = true diff --git a/src/isa.rs b/src/isa.rs index 7d82767..84c8b37 100644 --- a/src/isa.rs +++ b/src/isa.rs @@ -17,7 +17,7 @@ //! that it is less convenient to interpret. For example, let's look at //! the following example in hypothetical structured stack machine: //! -//! ``` +//! ```plain //! loop //! ... //! if_true_jump_to_end diff --git a/src/runner.rs b/src/runner.rs index 48beaaf..ae8a5d1 100644 --- a/src/runner.rs +++ b/src/runner.rs @@ -20,11 +20,11 @@ use nan_preserving_float::{F32, F64}; use isa; /// Maximum number of entries in value stack. -pub const DEFAULT_VALUE_STACK_LIMIT: usize = 16384; +pub const DEFAULT_VALUE_STACK_LIMIT: usize = 16 * 1024; -struct EvalContext { - call_stack: VecDeque, -} +// TODO: Do the initial calibration. +// TODO: Make these parameters changeble. +pub const DEFAULT_CALL_STACK_LIMIT: usize = 1024; /// Interpreter action to execute after executing instruction. pub enum InstructionOutcome { @@ -75,9 +75,9 @@ impl<'a, E: Externals> Interpreter<'a, E> { self.run_interpreter_loop(&mut function_stack)?; - println!("return stack: {:?}", self.value_stack); + // println!("return stack: {:?}", self.value_stack); - Ok(func.signature().return_type().map(|vt| { + Ok(func.signature().return_type().map(|_vt| { let return_value = self.value_stack .pop(); @@ -116,6 +116,10 @@ impl<'a, E: Externals> Interpreter<'a, E> { } }, RunResult::NestedCall(nested_func) => { + if function_stack.len() + 1 >= DEFAULT_CALL_STACK_LIMIT { + return Err(TrapKind::StackOverflow.into()); + } + match *nested_func.as_internal() { FuncInstanceInternal::Internal { .. } => { let nested_context = function_context.nested(nested_func.clone()).map_err(Trap::new)?; @@ -146,16 +150,16 @@ impl<'a, E: Externals> Interpreter<'a, E> { fn drop_keep(&mut self, drop: u32, keep: u8) -> Result<(), TrapKind> { assert!(keep <= 1); - println!("drop: {}, keep: {}, stack: {:?}", drop, keep, self.value_stack); + // println!("drop: {}, keep: {}, stack: {:?}", drop, keep, self.value_stack); if keep == 1 { let top = *self.value_stack.top(); *self.value_stack.pick_mut(drop as usize) = top; } - println!("drop: {}, keep: {}, stack: {:?}", drop, keep, self.value_stack); + // println!("drop: {}, keep: {}, stack: {:?}", drop, keep, self.value_stack); let cur_stack_len = self.value_stack.len(); self.value_stack.resize(cur_stack_len - drop as usize); - println!("drop: {}, keep: {}, stack: {:?}", drop, keep, self.value_stack); + // println!("drop: {}, keep: {}, stack: {:?}", drop, keep, self.value_stack); Ok(()) } @@ -163,8 +167,8 @@ impl<'a, E: Externals> Interpreter<'a, E> { loop { let instruction = &instructions[function_context.position]; - println!("{:?}", instruction); - println!(" before = {:?}", self.value_stack); + // println!("{:?}", instruction); + // println!(" before = {:?}", self.value_stack); match self.run_instruction(function_context, instruction)? { InstructionOutcome::RunNextInstruction => function_context.position += 1, @@ -182,12 +186,13 @@ impl<'a, E: Externals> Interpreter<'a, E> { }, } - println!(" after = {:?}", self.value_stack); + // println!(" after = {:?}", self.value_stack); } Ok(RunResult::Return) } + #[inline(always)] fn run_instruction(&mut self, context: &mut FunctionContext, instruction: &isa::Instruction) -> Result { match instruction { &isa::Instruction::Unreachable => self.run_unreachable(context), diff --git a/src/validation/func.rs b/src/validation/func.rs index ca44926..cbad520 100644 --- a/src/validation/func.rs +++ b/src/validation/func.rs @@ -155,7 +155,12 @@ impl Validator { loop { let opcode = &body[context.position]; - match Validator::validate_instruction(context, opcode)? { + + let outcome = Validator::validate_instruction(context, opcode) + .map_err(|err| Error(format!("At instruction {:?}(@{}): {}", opcode, context.position, err)))?; + + println!("opcode: {:?}, outcome={:?}", opcode, outcome); + match outcome { InstructionOutcome::ValidateNextInstruction => (), InstructionOutcome::Unreachable => context.unreachable()?, } @@ -225,8 +230,7 @@ impl Validator { context.sink.emit_br_eqz(Target { label: if_not, - drop: 0, - keep: 0, + drop_keep: DropKeep { drop: 0, keep: 0 }, }); }, Else => { @@ -244,8 +248,7 @@ impl Validator { // to the "end_label" (it will be resolved at End). context.sink.emit_br(Target { label: end_label, - drop: 0, - keep: 0, + drop_keep: DropKeep { drop: 0, keep: 0 }, }); // Resolve `if_not` to here so when if condition is unsatisfied control flow @@ -297,7 +300,7 @@ impl Validator { if context.frame_stack.len() == 1 { // We are about to close the last frame. Insert // an explicit return. - let (drop, keep) = context.drop_keep_return()?; + let DropKeep { drop, keep } = context.drop_keep_return()?; context.sink.emit(isa::Instruction::Return { drop, keep, @@ -334,13 +337,15 @@ impl Validator { return Ok(InstructionOutcome::Unreachable); }, Return => { - let (drop, keep) = context.drop_keep_return()?; + let DropKeep { drop, keep } = context.drop_keep_return()?; context.sink.emit(isa::Instruction::Return { drop, keep, }); - Validator::validate_return(context)?; + if let BlockType::Value(value_type) = context.return_type()? { + context.tee_value(value_type.into())?; + } return Ok(InstructionOutcome::Unreachable); }, @@ -1358,13 +1363,6 @@ impl Validator { Ok(InstructionOutcome::Unreachable) } - fn validate_return(context: &mut FunctionValidationContext) -> Result { - if let BlockType::Value(value_type) = context.return_type()? { - context.tee_value(value_type.into())?; - } - Ok(InstructionOutcome::Unreachable) - } - fn validate_call(context: &mut FunctionValidationContext, idx: u32) -> Result { let (argument_types, return_type) = context.module.require_function(idx)?; for argument_type in argument_types.iter().rev() { @@ -1529,11 +1527,6 @@ impl<'a> FunctionValidationContext<'a> { let frame = self.frame_stack.pop()?; if self.value_stack.len() != frame.value_stack_len { - if true { - panic!("Unexpected stack height {}, expected {}", - self.value_stack.len(), - frame.value_stack_len) - } return Err(Error(format!( "Unexpected stack height {}, expected {}", self.value_stack.len(), @@ -1561,6 +1554,7 @@ impl<'a> FunctionValidationContext<'a> { } fn require_target(&self, depth: u32) -> Result { + let is_stack_polymorphic = self.top_label()?.polymorphic_stack; let frame = self.require_label(depth)?; let keep: u8 = match (frame.frame_type, frame.block_type) { @@ -1569,27 +1563,63 @@ impl<'a> FunctionValidationContext<'a> { (_, BlockType::Value(_)) => 1, }; - let value_stack_height = self.value_stack.len() as u32; - let drop = if frame.polymorphic_stack { 0 } else { - (value_stack_height - frame.value_stack_len as u32) - keep as u32 + let value_stack_height = self.value_stack.len(); + let drop = if is_stack_polymorphic { 0 } else { + // TODO: Remove this. + // println!("value_stack_height = {}", value_stack_height); + // println!("frame.value_stack_len = {}", frame.value_stack_len); + // println!("keep = {}", keep); + + if value_stack_height < frame.value_stack_len { + // TODO: Better error message. + return Err( + Error( + format!( + "Stack underflow detected: value stack height ({}) is lower than minimum stack len ({})", + value_stack_height, + frame.value_stack_len, + ) + ) + ); + } + if (value_stack_height as u32 - frame.value_stack_len as u32) < keep as u32 { + // TODO: Better error message. + return Err( + Error( + format!( + "Stack underflow detected: asked to keep {} values, but there are only {}", + keep, + (value_stack_height as u32 - frame.value_stack_len as u32), + ) + ) + ); + } + + (value_stack_height as u32 - frame.value_stack_len as u32) - keep as u32 }; Ok(Target { label: frame.frame_type.br_destination(), - keep, - drop, + drop_keep: DropKeep { + drop, + keep, + }, }) } - fn drop_keep_return(&self) -> Result<(u32, u8), Error> { - // TODO: Refactor + fn drop_keep_return(&self) -> Result { + assert!( + !self.frame_stack.is_empty(), + "drop_keep_return can't be called with the frame stack empty" + ); + let deepest = (self.frame_stack.len() - 1) as u32; - let mut target = self.require_target(deepest)?; + let mut drop_keep = self.require_target(deepest)?.drop_keep; // Drop all local variables and parameters upon exit. - target.drop += self.locals.count()?; + drop_keep.drop += self.locals.count()?; - Ok((target.drop, target.keep)) + Ok(drop_keep) } fn relative_local_depth(&mut self, idx: u32) -> Result { @@ -1662,12 +1692,17 @@ impl PartialEq for ValueType { } #[derive(Clone)] -struct Target { - label: LabelId, +struct DropKeep { drop: u32, keep: u8, } +#[derive(Clone)] +struct Target { + label: LabelId, + drop_keep: DropKeep, +} + enum Reloc { Br { pc: u32, @@ -1725,8 +1760,10 @@ impl Sink { fn emit_br(&mut self, target: Target) { let Target { label, - drop, - keep, + drop_keep: DropKeep { + drop, + keep, + }, } = target; let pc = self.cur_pc(); let dst_pc = self.pc_or_placeholder(label, || Reloc::Br { pc }); @@ -1740,8 +1777,10 @@ impl Sink { fn emit_br_eqz(&mut self, target: Target) { let Target { label, - drop, - keep, + drop_keep: DropKeep { + drop, + keep, + }, } = target; let pc = self.cur_pc(); let dst_pc = self.pc_or_placeholder(label, || Reloc::Br { pc }); @@ -1755,8 +1794,10 @@ impl Sink { fn emit_br_nez(&mut self, target: Target) { let Target { label, - drop, - keep, + drop_keep: DropKeep { + drop, + keep, + }, } = target; let pc = self.cur_pc(); let dst_pc = self.pc_or_placeholder(label, || Reloc::Br { pc }); @@ -1772,7 +1813,10 @@ 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() { + for (idx, &Target { label, drop_keep: DropKeep { + drop, + keep, + }}) in targets.iter().chain(iter::once(&default)).enumerate() { let dst_pc = self.pc_or_placeholder(label, || Reloc::BrTable { pc, idx }); isa_targets.push( isa::Target { diff --git a/tests/spec/run.rs b/tests/spec/run.rs index 1fe12c6..80d312f 100644 --- a/tests/spec/run.rs +++ b/tests/spec/run.rs @@ -360,6 +360,8 @@ fn try_spec(name: &str) -> Result<(), Error> { }}; } + println!("Running spec cmd {}: {:?}", line, kind); + match kind { CommandKind::Module { name, module, .. } => { load_module(&module.into_vec()?, &name, &mut spec_driver)