This commit is contained in:
Sergey Pepyakin 2018-06-14 16:06:45 +03:00
parent aba44ca5ed
commit e9f201bde9
5 changed files with 106 additions and 52 deletions

View File

@ -6,3 +6,6 @@ authors = ["Sergey Pepyakin <s.pepyakin@gmail.com>"]
[dependencies] [dependencies]
wasmi = { path = ".." } wasmi = { path = ".." }
assert_matches = "1.2" assert_matches = "1.2"
[profile.bench]
debug = true

View File

@ -17,7 +17,7 @@
//! that it is less convenient to interpret. For example, let's look at //! that it is less convenient to interpret. For example, let's look at
//! the following example in hypothetical structured stack machine: //! the following example in hypothetical structured stack machine:
//! //!
//! ``` //! ```plain
//! loop //! loop
//! ... //! ...
//! if_true_jump_to_end //! if_true_jump_to_end

View File

@ -20,11 +20,11 @@ use nan_preserving_float::{F32, F64};
use isa; use isa;
/// Maximum number of entries in value stack. /// 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 { // TODO: Do the initial calibration.
call_stack: VecDeque<FunctionContext>, // TODO: Make these parameters changeble.
} pub const DEFAULT_CALL_STACK_LIMIT: usize = 1024;
/// Interpreter action to execute after executing instruction. /// Interpreter action to execute after executing instruction.
pub enum InstructionOutcome { pub enum InstructionOutcome {
@ -75,9 +75,9 @@ impl<'a, E: Externals> Interpreter<'a, E> {
self.run_interpreter_loop(&mut function_stack)?; 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 let return_value = self.value_stack
.pop(); .pop();
@ -116,6 +116,10 @@ impl<'a, E: Externals> Interpreter<'a, E> {
} }
}, },
RunResult::NestedCall(nested_func) => { RunResult::NestedCall(nested_func) => {
if function_stack.len() + 1 >= DEFAULT_CALL_STACK_LIMIT {
return Err(TrapKind::StackOverflow.into());
}
match *nested_func.as_internal() { match *nested_func.as_internal() {
FuncInstanceInternal::Internal { .. } => { FuncInstanceInternal::Internal { .. } => {
let nested_context = function_context.nested(nested_func.clone()).map_err(Trap::new)?; 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> { fn drop_keep(&mut self, drop: u32, keep: u8) -> Result<(), TrapKind> {
assert!(keep <= 1); assert!(keep <= 1);
println!("drop: {}, keep: {}, stack: {:?}", drop, keep, self.value_stack); // println!("drop: {}, keep: {}, stack: {:?}", drop, keep, self.value_stack);
if keep == 1 { if keep == 1 {
let top = *self.value_stack.top(); let top = *self.value_stack.top();
*self.value_stack.pick_mut(drop as usize) = 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(); let cur_stack_len = self.value_stack.len();
self.value_stack.resize(cur_stack_len - drop as usize); 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(()) Ok(())
} }
@ -163,8 +167,8 @@ impl<'a, E: Externals> Interpreter<'a, E> {
loop { loop {
let instruction = &instructions[function_context.position]; let instruction = &instructions[function_context.position];
println!("{:?}", instruction); // println!("{:?}", instruction);
println!(" before = {:?}", self.value_stack); // println!(" before = {:?}", self.value_stack);
match self.run_instruction(function_context, instruction)? { match self.run_instruction(function_context, instruction)? {
InstructionOutcome::RunNextInstruction => function_context.position += 1, 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) Ok(RunResult::Return)
} }
#[inline(always)]
fn run_instruction(&mut self, context: &mut FunctionContext, instruction: &isa::Instruction) -> Result<InstructionOutcome, TrapKind> { fn run_instruction(&mut self, context: &mut FunctionContext, instruction: &isa::Instruction) -> Result<InstructionOutcome, TrapKind> {
match instruction { match instruction {
&isa::Instruction::Unreachable => self.run_unreachable(context), &isa::Instruction::Unreachable => self.run_unreachable(context),

View File

@ -155,7 +155,12 @@ impl Validator {
loop { loop {
let opcode = &body[context.position]; 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::ValidateNextInstruction => (),
InstructionOutcome::Unreachable => context.unreachable()?, InstructionOutcome::Unreachable => context.unreachable()?,
} }
@ -225,8 +230,7 @@ impl Validator {
context.sink.emit_br_eqz(Target { context.sink.emit_br_eqz(Target {
label: if_not, label: if_not,
drop: 0, drop_keep: DropKeep { drop: 0, keep: 0 },
keep: 0,
}); });
}, },
Else => { Else => {
@ -244,8 +248,7 @@ impl Validator {
// to the "end_label" (it will be resolved at End). // to the "end_label" (it will be resolved at End).
context.sink.emit_br(Target { context.sink.emit_br(Target {
label: end_label, label: end_label,
drop: 0, drop_keep: DropKeep { drop: 0, keep: 0 },
keep: 0,
}); });
// Resolve `if_not` to here so when if condition is unsatisfied control flow // 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 { if context.frame_stack.len() == 1 {
// We are about to close the last frame. Insert // We are about to close the last frame. Insert
// an explicit return. // an explicit return.
let (drop, keep) = context.drop_keep_return()?; let DropKeep { drop, keep } = context.drop_keep_return()?;
context.sink.emit(isa::Instruction::Return { context.sink.emit(isa::Instruction::Return {
drop, drop,
keep, keep,
@ -334,13 +337,15 @@ impl Validator {
return Ok(InstructionOutcome::Unreachable); return Ok(InstructionOutcome::Unreachable);
}, },
Return => { Return => {
let (drop, keep) = context.drop_keep_return()?; let DropKeep { drop, keep } = context.drop_keep_return()?;
context.sink.emit(isa::Instruction::Return { context.sink.emit(isa::Instruction::Return {
drop, drop,
keep, keep,
}); });
Validator::validate_return(context)?; if let BlockType::Value(value_type) = context.return_type()? {
context.tee_value(value_type.into())?;
}
return Ok(InstructionOutcome::Unreachable); return Ok(InstructionOutcome::Unreachable);
}, },
@ -1358,13 +1363,6 @@ impl Validator {
Ok(InstructionOutcome::Unreachable) Ok(InstructionOutcome::Unreachable)
} }
fn validate_return(context: &mut FunctionValidationContext) -> Result<InstructionOutcome, Error> {
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<InstructionOutcome, Error> { fn validate_call(context: &mut FunctionValidationContext, idx: u32) -> Result<InstructionOutcome, Error> {
let (argument_types, return_type) = context.module.require_function(idx)?; let (argument_types, return_type) = context.module.require_function(idx)?;
for argument_type in argument_types.iter().rev() { for argument_type in argument_types.iter().rev() {
@ -1529,11 +1527,6 @@ impl<'a> FunctionValidationContext<'a> {
let frame = self.frame_stack.pop()?; let frame = self.frame_stack.pop()?;
if self.value_stack.len() != frame.value_stack_len { 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!( return Err(Error(format!(
"Unexpected stack height {}, expected {}", "Unexpected stack height {}, expected {}",
self.value_stack.len(), self.value_stack.len(),
@ -1561,6 +1554,7 @@ impl<'a> FunctionValidationContext<'a> {
} }
fn require_target(&self, depth: u32) -> Result<Target, Error> { fn require_target(&self, depth: u32) -> Result<Target, Error> {
let is_stack_polymorphic = self.top_label()?.polymorphic_stack;
let frame = self.require_label(depth)?; let frame = self.require_label(depth)?;
let keep: u8 = match (frame.frame_type, frame.block_type) { let keep: u8 = match (frame.frame_type, frame.block_type) {
@ -1569,27 +1563,63 @@ impl<'a> FunctionValidationContext<'a> {
(_, BlockType::Value(_)) => 1, (_, BlockType::Value(_)) => 1,
}; };
let value_stack_height = self.value_stack.len() as u32; let value_stack_height = self.value_stack.len();
let drop = if frame.polymorphic_stack { 0 } else { let drop = if is_stack_polymorphic { 0 } else {
(value_stack_height - frame.value_stack_len as u32) - keep as u32 // 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 { Ok(Target {
label: frame.frame_type.br_destination(), label: frame.frame_type.br_destination(),
keep, drop_keep: DropKeep {
drop, drop,
keep,
},
}) })
} }
fn drop_keep_return(&self) -> Result<(u32, u8), Error> { fn drop_keep_return(&self) -> Result<DropKeep, Error> {
// TODO: Refactor 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 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. // 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<u32, Error> { fn relative_local_depth(&mut self, idx: u32) -> Result<u32, Error> {
@ -1662,12 +1692,17 @@ impl PartialEq<StackValueType> for ValueType {
} }
#[derive(Clone)] #[derive(Clone)]
struct Target { struct DropKeep {
label: LabelId,
drop: u32, drop: u32,
keep: u8, keep: u8,
} }
#[derive(Clone)]
struct Target {
label: LabelId,
drop_keep: DropKeep,
}
enum Reloc { enum Reloc {
Br { Br {
pc: u32, pc: u32,
@ -1725,8 +1760,10 @@ impl Sink {
fn emit_br(&mut self, target: Target) { fn emit_br(&mut self, target: Target) {
let Target { let Target {
label, label,
drop_keep: DropKeep {
drop, drop,
keep, keep,
},
} = target; } = target;
let pc = self.cur_pc(); let pc = self.cur_pc();
let dst_pc = self.pc_or_placeholder(label, || Reloc::Br { 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) { fn emit_br_eqz(&mut self, target: Target) {
let Target { let Target {
label, label,
drop_keep: DropKeep {
drop, drop,
keep, keep,
},
} = target; } = target;
let pc = self.cur_pc(); let pc = self.cur_pc();
let dst_pc = self.pc_or_placeholder(label, || Reloc::Br { 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) { fn emit_br_nez(&mut self, target: Target) {
let Target { let Target {
label, label,
drop_keep: DropKeep {
drop, drop,
keep, keep,
},
} = target; } = target;
let pc = self.cur_pc(); let pc = self.cur_pc();
let dst_pc = self.pc_or_placeholder(label, || Reloc::Br { pc }); let dst_pc = self.pc_or_placeholder(label, || Reloc::Br { pc });
@ -1772,7 +1813,10 @@ impl Sink {
let pc = self.cur_pc(); let pc = self.cur_pc();
let mut isa_targets = Vec::new(); 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 }); let dst_pc = self.pc_or_placeholder(label, || Reloc::BrTable { pc, idx });
isa_targets.push( isa_targets.push(
isa::Target { isa::Target {

View File

@ -360,6 +360,8 @@ fn try_spec(name: &str) -> Result<(), Error> {
}}; }};
} }
println!("Running spec cmd {}: {:?}", line, kind);
match kind { match kind {
CommandKind::Module { name, module, .. } => { CommandKind::Module { name, module, .. } => {
load_module(&module.into_vec()?, &name, &mut spec_driver) load_module(&module.into_vec()?, &name, &mut spec_driver)