WIP
This commit is contained in:
parent
aba44ca5ed
commit
e9f201bde9
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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),
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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)
|
||||||
|
|
Loading…
Reference in New Issue