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]
wasmi = { path = ".." }
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
//! the following example in hypothetical structured stack machine:
//!
//! ```
//! ```plain
//! loop
//! ...
//! if_true_jump_to_end

View File

@ -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<FunctionContext>,
}
// 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<InstructionOutcome, TrapKind> {
match instruction {
&isa::Instruction::Unreachable => self.run_unreachable(context),

View File

@ -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<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> {
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<Target, Error> {
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<DropKeep, Error> {
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<u32, Error> {
@ -1662,12 +1692,17 @@ impl PartialEq<StackValueType> 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 {

View File

@ -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)