From b7a94855d839b207652ec5d7bec6185531232891 Mon Sep 17 00:00:00 2001 From: Sergey Pepyakin Date: Mon, 8 Apr 2019 16:20:21 +0200 Subject: [PATCH] Move code under prepare --- src/lib.rs | 4 +- src/prepare/compile.rs | 1239 +++++++++++++++++++++++++++++++++++++++ src/prepare/mod.rs | 37 ++ src/validation/func.rs | 1264 +--------------------------------------- src/validation/mod.rs | 52 +- 5 files changed, 1306 insertions(+), 1290 deletions(-) create mode 100644 src/prepare/compile.rs create mode 100644 src/prepare/mod.rs diff --git a/src/lib.rs b/src/lib.rs index e2fb2f7..1411a17 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -389,6 +389,7 @@ mod isa; mod memory; mod module; pub mod nan_preserving_float; +mod prepare; mod runner; mod table; mod types; @@ -454,8 +455,7 @@ impl Module { /// } /// ``` pub fn from_parity_wasm_module(module: parity_wasm::elements::Module) -> Result { - use validation::{validate_module, ValidatedModule}; - let ValidatedModule { code_map, module } = validate_module(module)?; + let prepare::CompiledModule { code_map, module } = prepare::compile_module(module)?; Ok(Module { code_map, module }) } diff --git a/src/prepare/compile.rs b/src/prepare/compile.rs new file mode 100644 index 0000000..dc9ab88 --- /dev/null +++ b/src/prepare/compile.rs @@ -0,0 +1,1239 @@ +use parity_wasm::elements::{BlockType, Func, FuncBody, Instruction, TableElementType, ValueType}; +use validation::context::ModuleContext; + +use validation::func::{ + require_label, top_label, BlockFrame, FunctionValidationContext, StackValueType, StartedWith, +}; +use validation::util::Locals; +use validation::{Error, FunctionValidator}; + +use common::stack::StackWithLimit; +use isa; + +/// Type of block frame. +#[derive(Debug, Clone, Copy, PartialEq)] +enum BlockFrameType { + /// Usual block frame. + /// + /// Can be used for an implicit function block. + Block { end_label: LabelId }, + /// Loop frame (branching to the beginning of block). + Loop { header: LabelId }, + /// True-subblock of if expression. + IfTrue { + /// If jump happens inside the if-true block then control will + /// land on this label. + end_label: LabelId, + + /// If the condition of the `if` statement is unsatisfied, control + /// will land on this label. This label might point to `else` block if it + /// exists. Otherwise it equal to `end_label`. + if_not: LabelId, + }, + /// False-subblock of if expression. + IfFalse { end_label: LabelId }, +} + +impl BlockFrameType { + /// Returns a label which should be used as a branch destination. + fn br_destination(&self) -> LabelId { + match *self { + BlockFrameType::Block { end_label } => end_label, + BlockFrameType::Loop { header } => header, + BlockFrameType::IfTrue { end_label, .. } => end_label, + BlockFrameType::IfFalse { end_label } => end_label, + } + } + + /// Returns a label which should be resolved at the `End` opcode. + /// + /// All block types have it except loops. Loops doesn't use end as a branch + /// destination. + fn end_label(&self) -> LabelId { + match *self { + BlockFrameType::Block { end_label } => end_label, + BlockFrameType::IfTrue { end_label, .. } => end_label, + BlockFrameType::IfFalse { end_label } => end_label, + BlockFrameType::Loop { .. } => panic!("loop doesn't use end label"), + } + } +} + +// TODO: Move under prepare +pub struct Compiler { + /// A sink used to emit optimized code. + sink: Sink, + label_stack: Vec, +} + +impl FunctionValidator for Compiler { + type Output = isa::Instructions; + fn new(_module: &FunctionValidationContext) -> Self { + let mut compiler = Compiler { + sink: Sink::with_instruction_capacity(0), // TODO: Estimate instruction number. + label_stack: Vec::new(), + }; + + // Push implicit frame for the outer function block. + let end_label = compiler.sink.new_label(); + compiler + .label_stack + .push(BlockFrameType::Block { end_label }); + + compiler + } + fn next_instruction( + &mut self, + ctx: &mut FunctionValidationContext, + instruction: &Instruction, + ) -> Result<(), Error> { + self.compile_instruction(ctx, instruction) + } + fn finish(self) -> Self::Output { + self.sink.into_inner() + } +} + +impl Compiler { + fn compile_instruction( + &mut self, + context: &mut FunctionValidationContext, + instruction: &Instruction, + ) -> Result<(), Error> { + use self::Instruction::*; + + match *instruction { + Unreachable => { + self.sink.emit(isa::InstructionInternal::Unreachable); + context.step(instruction)?; + } + Block(_) => { + context.step(instruction)?; + + let end_label = self.sink.new_label(); + self.label_stack.push(BlockFrameType::Block { end_label }); + } + Loop(_) => { + context.step(instruction)?; + + // Resolve loop header right away. + let header = self.sink.new_label(); + self.sink.resolve_label(header); + self.label_stack.push(BlockFrameType::Loop { header }); + } + If(_) => { + context.step(instruction)?; + + // `if_not` will be resolved whenever `End` or `Else` operator will be met. + // `end_label` will always be resolved at `End`. + let if_not = self.sink.new_label(); + let end_label = self.sink.new_label(); + self.label_stack + .push(BlockFrameType::IfTrue { if_not, end_label }); + + self.sink.emit_br_eqz(Target { + label: if_not, + drop_keep: isa::DropKeep { + drop: 0, + keep: isa::Keep::None, + }, + }); + } + Else => { + context.step(instruction)?; + + let (if_not, end_label) = { + // TODO: We will have to place this before validation step to ensure that + // the block type is indeed if_true. + + let top_label = self.label_stack.last().unwrap(); + let (if_not, end_label) = match *top_label { + BlockFrameType::IfTrue { if_not, end_label } => (if_not, end_label), + _ => panic!("validation ensures that the top frame is actually if_true"), + }; + (if_not, end_label) + }; + + // First, we need to finish if-true block: add a jump from the end of the if-true block + // to the "end_label" (it will be resolved at End). + self.sink.emit_br(Target { + label: end_label, + drop_keep: isa::DropKeep { + drop: 0, + keep: isa::Keep::None, + }, + }); + + // Resolve `if_not` to here so when if condition is unsatisfied control flow + // will jump to this label. + self.sink.resolve_label(if_not); + + self.label_stack.pop().unwrap(); + self.label_stack.push(BlockFrameType::IfFalse { end_label }); + } + End => { + let started_with = top_label(&context.frame_stack).started_with; + let return_drop_keep = if context.frame_stack.len() == 1 { + // We are about to close the last frame. + Some(drop_keep_return( + &context.locals, + &context.value_stack, + &context.frame_stack, + )) + } else { + None + }; + + context.step(instruction)?; + + // let started_with = started_with.expect("validation ensures that it is ok"); + + // TODO: We will have to place this before validation step to ensure that + // the block type is indeed if_true. + let frame_type = self.label_stack.last().cloned().unwrap(); + + if let BlockFrameType::IfTrue { if_not, .. } = frame_type { + // Resolve `if_not` label. If the `if's` condition doesn't hold the control will jump + // to here. + self.sink.resolve_label(if_not); + } + + // Unless it's a loop, resolve the `end_label` position here. + if started_with != StartedWith::Loop { + let end_label = frame_type.end_label(); + self.sink.resolve_label(end_label); + } + + if let Some(drop_keep) = return_drop_keep { + // TODO: The last one. + // It was the last instruction. Emit the explicit return instruction. + let drop_keep = drop_keep.expect("validation should ensure this doesn't fail"); + self.sink.emit(isa::InstructionInternal::Return(drop_keep)); + } + + // Finally, pop the label. + self.label_stack.pop().unwrap(); + } + Br(depth) => { + let target = require_target( + depth, + context.value_stack.len(), + &context.frame_stack, + &self.label_stack, + ); + + context.step(instruction)?; + + let target = target.expect("validation step should ensure that this doesn't fail"); + self.sink.emit_br(target); + } + BrIf(depth) => { + context.step(instruction)?; + + let target = require_target( + depth, + context.value_stack.len(), + &context.frame_stack, + &self.label_stack, + ); + + let target = target.expect("validation step should ensure that this doesn't fail"); + self.sink.emit_br_nez(target); + } + BrTable(ref table, default) => { + // At this point, the condition value is at the top of the stack. + // But at the point of actual jump the condition will already be + // popped off. + let value_stack_height = context.value_stack.len().saturating_sub(1); + + let mut targets = table + .iter() + .map(|depth| { + require_target( + *depth, + value_stack_height, + &context.frame_stack, + &self.label_stack, + ) + }) + .collect::, _>>(); + let default_target = require_target( + default, + value_stack_height, + &context.frame_stack, + &self.label_stack, + ); + + context.step(instruction)?; + + // These two unwraps are guaranteed to succeed by validation. + let targets = targets.unwrap(); + let default_target = default_target.unwrap(); + + self.sink.emit_br_table(&targets, default_target); + } + Return => { + let drop_keep = + drop_keep_return(&context.locals, &context.value_stack, &context.frame_stack); + + context.step(instruction)?; + + let drop_keep = + drop_keep.expect("validation step should ensure that this doesn't fail"); + + self.sink.emit(isa::InstructionInternal::Return(drop_keep)); + } + Call(index) => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::Call(index)); + } + CallIndirect(index, _reserved) => { + context.step(instruction)?; + self.sink + .emit(isa::InstructionInternal::CallIndirect(index)); + } + + Drop => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::Drop); + } + Select => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::Select); + } + + GetLocal(index) => { + // We need to calculate relative depth before validation since + // it will change the value stack size. + let depth = relative_local_depth(index, &context.locals, &context.value_stack)?; + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::GetLocal(depth)); + } + SetLocal(index) => { + context.step(instruction)?; + let depth = relative_local_depth(index, &context.locals, &context.value_stack)?; + self.sink.emit(isa::InstructionInternal::SetLocal(depth)); + } + TeeLocal(index) => { + context.step(instruction)?; + let depth = relative_local_depth(index, &context.locals, &context.value_stack)?; + self.sink.emit(isa::InstructionInternal::TeeLocal(depth)); + } + GetGlobal(index) => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::GetGlobal(index)); + } + SetGlobal(index) => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::SetGlobal(index)); + } + + I32Load(_, offset) => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::I32Load(offset)); + } + I64Load(_, offset) => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::I64Load(offset)); + } + F32Load(_, offset) => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::F32Load(offset)); + } + F64Load(_, offset) => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::F64Load(offset)); + } + I32Load8S(_, offset) => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::I32Load8S(offset)); + } + I32Load8U(_, offset) => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::I32Load8U(offset)); + } + I32Load16S(_, offset) => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::I32Load16S(offset)); + } + I32Load16U(_, offset) => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::I32Load16U(offset)); + } + I64Load8S(_, offset) => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::I64Load8S(offset)); + } + I64Load8U(_, offset) => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::I64Load8U(offset)); + } + I64Load16S(_, offset) => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::I64Load16S(offset)); + } + I64Load16U(_, offset) => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::I64Load16U(offset)); + } + I64Load32S(_, offset) => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::I64Load32S(offset)); + } + I64Load32U(_, offset) => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::I64Load32U(offset)); + } + + I32Store(_, offset) => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::I32Store(offset)); + } + I64Store(_, offset) => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::I64Store(offset)); + } + F32Store(_, offset) => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::F32Store(offset)); + } + F64Store(_, offset) => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::F64Store(offset)); + } + I32Store8(_, offset) => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::I32Store8(offset)); + } + I32Store16(_, offset) => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::I32Store16(offset)); + } + I64Store8(_, offset) => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::I64Store8(offset)); + } + I64Store16(_, offset) => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::I64Store16(offset)); + } + I64Store32(_, offset) => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::I64Store32(offset)); + } + + CurrentMemory(_) => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::CurrentMemory); + } + GrowMemory(_) => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::GrowMemory); + } + + I32Const(v) => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::I32Const(v)); + } + I64Const(v) => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::I64Const(v)); + } + F32Const(v) => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::F32Const(v)); + } + F64Const(v) => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::F64Const(v)); + } + + I32Eqz => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::I32Eqz); + } + I32Eq => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::I32Eq); + } + I32Ne => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::I32Ne); + } + I32LtS => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::I32LtS); + } + I32LtU => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::I32LtU); + } + I32GtS => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::I32GtS); + } + I32GtU => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::I32GtU); + } + I32LeS => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::I32LeS); + } + I32LeU => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::I32LeU); + } + I32GeS => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::I32GeS); + } + I32GeU => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::I32GeU); + } + + I64Eqz => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::I64Eqz); + } + I64Eq => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::I64Eq); + } + I64Ne => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::I64Ne); + } + I64LtS => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::I64LtS); + } + I64LtU => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::I64LtU); + } + I64GtS => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::I64GtS); + } + I64GtU => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::I64GtU); + } + I64LeS => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::I64LeS); + } + I64LeU => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::I64LeU); + } + I64GeS => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::I64GeS); + } + I64GeU => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::I64GeU); + } + + F32Eq => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::F32Eq); + } + F32Ne => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::F32Ne); + } + F32Lt => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::F32Lt); + } + F32Gt => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::F32Gt); + } + F32Le => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::F32Le); + } + F32Ge => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::F32Ge); + } + + F64Eq => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::F64Eq); + } + F64Ne => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::F64Ne); + } + F64Lt => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::F64Lt); + } + F64Gt => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::F64Gt); + } + F64Le => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::F64Le); + } + F64Ge => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::F64Ge); + } + + I32Clz => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::I32Clz); + } + I32Ctz => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::I32Ctz); + } + I32Popcnt => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::I32Popcnt); + } + I32Add => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::I32Add); + } + I32Sub => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::I32Sub); + } + I32Mul => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::I32Mul); + } + I32DivS => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::I32DivS); + } + I32DivU => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::I32DivU); + } + I32RemS => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::I32RemS); + } + I32RemU => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::I32RemU); + } + I32And => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::I32And); + } + I32Or => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::I32Or); + } + I32Xor => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::I32Xor); + } + I32Shl => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::I32Shl); + } + I32ShrS => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::I32ShrS); + } + I32ShrU => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::I32ShrU); + } + I32Rotl => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::I32Rotl); + } + I32Rotr => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::I32Rotr); + } + + I64Clz => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::I64Clz); + } + I64Ctz => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::I64Ctz); + } + I64Popcnt => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::I64Popcnt); + } + I64Add => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::I64Add); + } + I64Sub => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::I64Sub); + } + I64Mul => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::I64Mul); + } + I64DivS => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::I64DivS); + } + I64DivU => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::I64DivU); + } + I64RemS => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::I64RemS); + } + I64RemU => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::I64RemU); + } + I64And => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::I64And); + } + I64Or => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::I64Or); + } + I64Xor => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::I64Xor); + } + I64Shl => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::I64Shl); + } + I64ShrS => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::I64ShrS); + } + I64ShrU => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::I64ShrU); + } + I64Rotl => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::I64Rotl); + } + I64Rotr => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::I64Rotr); + } + + F32Abs => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::F32Abs); + } + F32Neg => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::F32Neg); + } + F32Ceil => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::F32Ceil); + } + F32Floor => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::F32Floor); + } + F32Trunc => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::F32Trunc); + } + F32Nearest => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::F32Nearest); + } + F32Sqrt => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::F32Sqrt); + } + F32Add => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::F32Add); + } + F32Sub => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::F32Sub); + } + F32Mul => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::F32Mul); + } + F32Div => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::F32Div); + } + F32Min => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::F32Min); + } + F32Max => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::F32Max); + } + F32Copysign => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::F32Copysign); + } + + F64Abs => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::F64Abs); + } + F64Neg => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::F64Neg); + } + F64Ceil => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::F64Ceil); + } + F64Floor => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::F64Floor); + } + F64Trunc => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::F64Trunc); + } + F64Nearest => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::F64Nearest); + } + F64Sqrt => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::F64Sqrt); + } + F64Add => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::F64Add); + } + F64Sub => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::F64Sub); + } + F64Mul => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::F64Mul); + } + F64Div => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::F64Div); + } + F64Min => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::F64Min); + } + F64Max => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::F64Max); + } + F64Copysign => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::F64Copysign); + } + + I32WrapI64 => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::I32WrapI64); + } + I32TruncSF32 => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::I32TruncSF32); + } + I32TruncUF32 => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::I32TruncUF32); + } + I32TruncSF64 => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::I32TruncSF64); + } + I32TruncUF64 => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::I32TruncUF64); + } + I64ExtendSI32 => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::I64ExtendSI32); + } + I64ExtendUI32 => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::I64ExtendUI32); + } + I64TruncSF32 => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::I64TruncSF32); + } + I64TruncUF32 => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::I64TruncUF32); + } + I64TruncSF64 => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::I64TruncSF64); + } + I64TruncUF64 => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::I64TruncUF64); + } + F32ConvertSI32 => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::F32ConvertSI32); + } + F32ConvertUI32 => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::F32ConvertUI32); + } + F32ConvertSI64 => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::F32ConvertSI64); + } + F32ConvertUI64 => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::F32ConvertUI64); + } + F32DemoteF64 => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::F32DemoteF64); + } + F64ConvertSI32 => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::F64ConvertSI32); + } + F64ConvertUI32 => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::F64ConvertUI32); + } + F64ConvertSI64 => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::F64ConvertSI64); + } + F64ConvertUI64 => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::F64ConvertUI64); + } + F64PromoteF32 => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::F64PromoteF32); + } + + I32ReinterpretF32 => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::I32ReinterpretF32); + } + I64ReinterpretF64 => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::I64ReinterpretF64); + } + F32ReinterpretI32 => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::F32ReinterpretI32); + } + F64ReinterpretI64 => { + context.step(instruction)?; + self.sink.emit(isa::InstructionInternal::F64ReinterpretI64); + } + _ => { + context.step(instruction)?; + } + }; + + assert_eq!(self.label_stack.len(), context.frame_stack.len(),); + + Ok(()) + } +} + +fn compute_drop_keep( + in_stack_polymorphic_state: bool, + started_with: StartedWith, + block_type: BlockType, + actual_value_stack_height: usize, + start_value_stack_height: usize, +) -> Result { + // Find out how many values we need to keep (copy to the new stack location after the drop). + let keep: isa::Keep = match (started_with, block_type) { + // A loop doesn't take a value upon a branch. It can return value + // only via reaching it's closing `End` operator. + (StartedWith::Loop, _) => isa::Keep::None, + + (_, BlockType::Value(_)) => isa::Keep::Single, + (_, BlockType::NoResult) => isa::Keep::None, + }; + + // Find out how many values we need to discard. + let drop = if in_stack_polymorphic_state { + // Polymorphic stack is a weird state. Fortunately, it is always about the code that + // will not be executed, so we don't bother and return 0 here. + 0 + } else { + if actual_value_stack_height < start_value_stack_height { + return Err(Error(format!( + "Stack underflow detected: value stack height ({}) is lower than minimum stack len ({})", + actual_value_stack_height, + start_value_stack_height, + ))); + } + if (actual_value_stack_height as u32 - start_value_stack_height as u32) < keep as u32 { + return Err(Error(format!( + "Stack underflow detected: asked to keep {:?} values, but there are only {}", + keep, + actual_value_stack_height as u32 - start_value_stack_height as u32, + ))); + } + (actual_value_stack_height as u32 - start_value_stack_height as u32) - keep as u32 + }; + + Ok(isa::DropKeep { drop, keep }) +} + +fn require_target( + depth: u32, + value_stack_height: usize, + frame_stack: &StackWithLimit, + label_stack: &[BlockFrameType], +) -> Result { + let is_stack_polymorphic = top_label(frame_stack).polymorphic_stack; + let frame = require_label(depth, frame_stack)?; + + // TODO: Clean this mess. + let label = label_stack + .get(label_stack.len() - 1 - (depth as usize)) + .expect("this is ensured by `require_label` above"); + + let drop_keep = compute_drop_keep( + is_stack_polymorphic, + frame.started_with, + frame.block_type, + value_stack_height, + frame.value_stack_len, + )?; + + Ok(Target { + label: label.br_destination(), + drop_keep, + }) +} + +/// Compute drop/keep for the return statement. +/// +/// This function is a bit of unusual since it is called before validation and thus +/// should deal with invalid code. +fn drop_keep_return( + locals: &Locals, + value_stack: &StackWithLimit, + frame_stack: &StackWithLimit, +) -> Result { + if frame_stack.is_empty() { + return Err(Error( + "drop_keep_return can't be called with the frame stack empty".into(), + )); + } + + let is_stack_polymorphic = top_label(frame_stack).polymorphic_stack; + let deepest = (frame_stack.len() - 1) as u32; + let frame = require_label(deepest, frame_stack).expect("frame_stack is not empty"); + let mut drop_keep = compute_drop_keep( + is_stack_polymorphic, + frame.started_with, + frame.block_type, + value_stack.len(), + frame.value_stack_len, + )?; + + // Drop all local variables and parameters upon exit. + drop_keep.drop += locals.count(); + + Ok(drop_keep) +} + +/// Returns a relative depth on the stack of a local variable specified +/// by `idx`. +/// +/// See stack layout definition in mod isa. +fn relative_local_depth( + idx: u32, + locals: &Locals, + value_stack: &StackWithLimit, +) -> Result { + let value_stack_height = value_stack.len() as u32; + let locals_and_params_count = locals.count(); + + let depth = value_stack_height + .checked_add(locals_and_params_count) + .and_then(|x| x.checked_sub(idx)) + .ok_or_else(|| Error(String::from("Locals range not in 32-bit range")))?; + Ok(depth) +} + +/// The target of a branch instruction. +/// +/// It references a `LabelId` instead of exact instruction address. This is handy +/// for emitting code right away with labels resolved later. +#[derive(Clone)] +struct Target { + label: LabelId, + drop_keep: isa::DropKeep, +} + +/// Identifier of a label. +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +struct LabelId(usize); + +#[derive(Debug, PartialEq, Eq)] +enum Label { + Resolved(u32), + NotResolved, +} + +struct Sink { + ins: isa::Instructions, + labels: Vec<(Label, Vec)>, +} + +impl Sink { + fn with_instruction_capacity(capacity: usize) -> Sink { + Sink { + ins: isa::Instructions::with_capacity(capacity), + labels: Vec::new(), + } + } + + fn cur_pc(&self) -> u32 { + self.ins.current_pc() + } + + 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) => { + unresolved.push(reloc_creator()); + u32::max_value() + } + } + } + + fn emit(&mut self, instruction: isa::InstructionInternal) { + self.ins.push(instruction); + } + + fn emit_br(&mut self, target: Target) { + let Target { label, drop_keep } = target; + let pc = self.cur_pc(); + let dst_pc = self.pc_or_placeholder(label, || isa::Reloc::Br { pc }); + self.ins.push(isa::InstructionInternal::Br(isa::Target { + dst_pc, + drop_keep: drop_keep.into(), + })); + } + + fn emit_br_eqz(&mut self, target: Target) { + let Target { label, drop_keep } = target; + let pc = self.cur_pc(); + let dst_pc = self.pc_or_placeholder(label, || isa::Reloc::Br { pc }); + self.ins + .push(isa::InstructionInternal::BrIfEqz(isa::Target { + dst_pc, + drop_keep: drop_keep.into(), + })); + } + + fn emit_br_nez(&mut self, target: Target) { + let Target { label, drop_keep } = target; + let pc = self.cur_pc(); + let dst_pc = self.pc_or_placeholder(label, || isa::Reloc::Br { pc }); + self.ins + .push(isa::InstructionInternal::BrIfNez(isa::Target { + dst_pc, + drop_keep: drop_keep.into(), + })); + } + + fn emit_br_table(&mut self, targets: &[Target], default: Target) { + use core::iter; + + let pc = self.cur_pc(); + + self.ins.push(isa::InstructionInternal::BrTable { + count: targets.len() as u32 + 1, + }); + + for (idx, &Target { label, drop_keep }) in + targets.iter().chain(iter::once(&default)).enumerate() + { + let dst_pc = self.pc_or_placeholder(label, || isa::Reloc::BrTable { pc, idx }); + self.ins + .push(isa::InstructionInternal::BrTableTarget(isa::Target { + dst_pc, + drop_keep: drop_keep.into(), + })); + } + } + + /// Create a new unresolved label. + fn new_label(&mut self) -> LabelId { + let label_idx = self.labels.len(); + self.labels.push((Label::NotResolved, Vec::new())); + LabelId(label_idx) + } + + /// Resolve the label at the current position. + /// + /// Panics if the label is already resolved. + fn resolve_label(&mut self, label: LabelId) { + use core::mem; + + if let (Label::Resolved(_), _) = self.labels[label.0] { + panic!("Trying to resolve already resolved label"); + } + let dst_pc = self.cur_pc(); + + // Patch all relocations that was previously recorded for this + // particular label. + let unresolved_rels = mem::replace(&mut self.labels[label.0].1, Vec::new()); + for reloc in unresolved_rels { + 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::Instructions. + fn into_inner(self) -> isa::Instructions { + // At this moment all labels should be resolved. + assert!( + { + self.labels + .iter() + .all(|(state, unresolved)| match (state, unresolved) { + (Label::Resolved(_), unresolved) if unresolved.is_empty() => true, + _ => false, + }) + }, + "there are unresolved labels left: {:?}", + self.labels + ); + self.ins + } +} diff --git a/src/prepare/mod.rs b/src/prepare/mod.rs new file mode 100644 index 0000000..4fc9ca2 --- /dev/null +++ b/src/prepare/mod.rs @@ -0,0 +1,37 @@ +use crate::{isa, validation::{Error, validate_module2, Validation}}; +use parity_wasm::elements::Module; + +mod compile; + +#[derive(Clone)] +pub struct CompiledModule { + pub code_map: Vec, + pub module: Module, +} + +pub struct WasmiValidation { + code_map: Vec, +} + +impl Validation for WasmiValidation { + type Output = Vec; + type FunctionValidator = compile::Compiler; + fn new(_module: &Module) -> Self { + WasmiValidation { + // TODO: with capacity? + code_map: Vec::new(), + } + } + fn on_function_validated(&mut self, _index: u32, output: isa::Instructions) { + self.code_map.push(output); + } + fn finish(self) -> Vec { + self.code_map + } +} + +/// Validate a module and compile it to the internal representation. +pub fn compile_module(module: Module) -> Result { + let code_map = validate_module2::(&module)?; + Ok(CompiledModule { module, code_map }) +} diff --git a/src/validation/func.rs b/src/validation/func.rs index b5d2882..894ce1c 100644 --- a/src/validation/func.rs +++ b/src/validation/func.rs @@ -18,84 +18,35 @@ const DEFAULT_FRAME_STACK_LIMIT: usize = 16384; /// Control stack frame. #[derive(Debug, Clone)] -struct BlockFrame { +pub struct BlockFrame { /// The opcode that started this block frame. - started_with: StartedWith, + pub started_with: StartedWith, /// A signature, which is a block signature type indicating the number and types of result /// values of the region. - block_type: BlockType, + pub block_type: BlockType, /// A limit integer value, which is an index into the value stack indicating where to reset it /// to on a branch to that label. - value_stack_len: usize, + pub value_stack_len: usize, /// Boolean which signals whether value stack became polymorphic. Value stack starts in /// a non-polymorphic state and becomes polymorphic only after an instruction that never passes /// control further is executed, i.e. `unreachable`, `br` (but not `br_if`!), etc. - polymorphic_stack: bool, + pub polymorphic_stack: bool, } /// An opcode that opened the particular frame. /// /// We need that to ensure proper combinations with `End` instruction. #[derive(Debug, Copy, Clone, PartialEq, Eq)] -enum StartedWith { +pub enum StartedWith { Block, If, Else, Loop, } -/// Type of block frame. -#[derive(Debug, Clone, Copy, PartialEq)] -enum BlockFrameType { - /// Usual block frame. - /// - /// Can be used for an implicit function block. - Block { end_label: LabelId }, - /// Loop frame (branching to the beginning of block). - Loop { header: LabelId }, - /// True-subblock of if expression. - IfTrue { - /// If jump happens inside the if-true block then control will - /// land on this label. - end_label: LabelId, - - /// If the condition of the `if` statement is unsatisfied, control - /// will land on this label. This label might point to `else` block if it - /// exists. Otherwise it equal to `end_label`. - if_not: LabelId, - }, - /// False-subblock of if expression. - IfFalse { end_label: LabelId }, -} - -impl BlockFrameType { - /// Returns a label which should be used as a branch destination. - fn br_destination(&self) -> LabelId { - match *self { - BlockFrameType::Block { end_label } => end_label, - BlockFrameType::Loop { header } => header, - BlockFrameType::IfTrue { end_label, .. } => end_label, - BlockFrameType::IfFalse { end_label } => end_label, - } - } - - /// Returns a label which should be resolved at the `End` opcode. - /// - /// All block types have it except loops. Loops doesn't use end as a branch - /// destination. - fn end_label(&self) -> LabelId { - match *self { - BlockFrameType::Block { end_label } => end_label, - BlockFrameType::IfTrue { end_label, .. } => end_label, - BlockFrameType::IfFalse { end_label } => end_label, - BlockFrameType::Loop { .. } => panic!("loop doesn't use end label"), - } - } -} - /// Value type on the stack. #[derive(Debug, Clone, Copy)] -enum StackValueType { +pub enum StackValueType { /// Any value type. Any, /// Concrete value type. @@ -191,919 +142,18 @@ pub fn drive( Ok(validator.finish()) } -// TODO: Move under prepare -pub struct Compiler { - /// A sink used to emit optimized code. - sink: Sink, - label_stack: Vec, -} - -impl FunctionValidator for Compiler { - type Output = isa::Instructions; - fn new(_module: &FunctionValidationContext) -> Self { - let mut compiler = Compiler { - sink: Sink::with_instruction_capacity(0), // TODO: Estimate instruction number. - label_stack: Vec::new(), - }; - - // Push implicit frame for the outer function block. - let end_label = compiler.sink.new_label(); - compiler - .label_stack - .push(BlockFrameType::Block { end_label }); - - compiler - } - fn next_instruction( - &mut self, - ctx: &mut FunctionValidationContext, - instruction: &Instruction, - ) -> Result<(), Error> { - self.compile_instruction(ctx, instruction) - } - fn finish(self) -> Self::Output { - self.sink.into_inner() - } -} - -impl Compiler { - fn compile_instruction( - &mut self, - context: &mut FunctionValidationContext, - instruction: &Instruction, - ) -> Result<(), Error> { - use self::Instruction::*; - - match *instruction { - Unreachable => { - self.sink.emit(isa::InstructionInternal::Unreachable); - context.step(instruction)?; - } - Block(_) => { - context.step(instruction)?; - - let end_label = self.sink.new_label(); - self.label_stack.push(BlockFrameType::Block { end_label }); - } - Loop(_) => { - context.step(instruction)?; - - // Resolve loop header right away. - let header = self.sink.new_label(); - self.sink.resolve_label(header); - self.label_stack.push(BlockFrameType::Loop { header }); - } - If(_) => { - context.step(instruction)?; - - // `if_not` will be resolved whenever `End` or `Else` operator will be met. - // `end_label` will always be resolved at `End`. - let if_not = self.sink.new_label(); - let end_label = self.sink.new_label(); - self.label_stack - .push(BlockFrameType::IfTrue { if_not, end_label }); - - self.sink.emit_br_eqz(Target { - label: if_not, - drop_keep: isa::DropKeep { - drop: 0, - keep: isa::Keep::None, - }, - }); - } - Else => { - context.step(instruction)?; - - let (if_not, end_label) = { - // TODO: We will have to place this before validation step to ensure that - // the block type is indeed if_true. - - let top_label = self.label_stack.last().unwrap(); - let (if_not, end_label) = match *top_label { - BlockFrameType::IfTrue { if_not, end_label } => (if_not, end_label), - _ => panic!("validation ensures that the top frame is actually if_true"), - }; - (if_not, end_label) - }; - - // First, we need to finish if-true block: add a jump from the end of the if-true block - // to the "end_label" (it will be resolved at End). - self.sink.emit_br(Target { - label: end_label, - drop_keep: isa::DropKeep { - drop: 0, - keep: isa::Keep::None, - }, - }); - - // Resolve `if_not` to here so when if condition is unsatisfied control flow - // will jump to this label. - self.sink.resolve_label(if_not); - - self.label_stack.pop().unwrap(); - self.label_stack.push(BlockFrameType::IfFalse { end_label }); - } - End => { - let started_with = top_label(&context.frame_stack).started_with; - let return_drop_keep = if context.frame_stack.len() == 1 { - // We are about to close the last frame. - Some(drop_keep_return( - &context.locals, - &context.value_stack, - &context.frame_stack, - )) - } else { - None - }; - - context.step(instruction)?; - - // let started_with = started_with.expect("validation ensures that it is ok"); - - // TODO: We will have to place this before validation step to ensure that - // the block type is indeed if_true. - let frame_type = self.label_stack.last().cloned().unwrap(); - - if let BlockFrameType::IfTrue { if_not, .. } = frame_type { - // Resolve `if_not` label. If the `if's` condition doesn't hold the control will jump - // to here. - self.sink.resolve_label(if_not); - } - - // Unless it's a loop, resolve the `end_label` position here. - if started_with != StartedWith::Loop { - let end_label = frame_type.end_label(); - self.sink.resolve_label(end_label); - } - - if let Some(drop_keep) = return_drop_keep { - // TODO: The last one. - // It was the last instruction. Emit the explicit return instruction. - let drop_keep = drop_keep.expect("validation should ensure this doesn't fail"); - self.sink.emit(isa::InstructionInternal::Return(drop_keep)); - } - - // Finally, pop the label. - self.label_stack.pop().unwrap(); - } - Br(depth) => { - let target = require_target( - depth, - context.value_stack.len(), - &context.frame_stack, - &self.label_stack, - ); - - context.step(instruction)?; - - let target = target.expect("validation step should ensure that this doesn't fail"); - self.sink.emit_br(target); - } - BrIf(depth) => { - context.step(instruction)?; - - let target = require_target( - depth, - context.value_stack.len(), - &context.frame_stack, - &self.label_stack, - ); - - let target = target.expect("validation step should ensure that this doesn't fail"); - self.sink.emit_br_nez(target); - } - BrTable(ref table, default) => { - // At this point, the condition value is at the top of the stack. - // But at the point of actual jump the condition will already be - // popped off. - let value_stack_height = context.value_stack.len().saturating_sub(1); - - let mut targets = table - .iter() - .map(|depth| { - require_target( - *depth, - value_stack_height, - &context.frame_stack, - &self.label_stack, - ) - }) - .collect::, _>>(); - let default_target = require_target( - default, - value_stack_height, - &context.frame_stack, - &self.label_stack, - ); - - context.step(instruction)?; - - // These two unwraps are guaranteed to succeed by validation. - let targets = targets.unwrap(); - let default_target = default_target.unwrap(); - - self.sink.emit_br_table(&targets, default_target); - } - Return => { - let drop_keep = - drop_keep_return(&context.locals, &context.value_stack, &context.frame_stack); - - context.step(instruction)?; - - let drop_keep = - drop_keep.expect("validation step should ensure that this doesn't fail"); - - self.sink.emit(isa::InstructionInternal::Return(drop_keep)); - } - Call(index) => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::Call(index)); - } - CallIndirect(index, _reserved) => { - context.step(instruction)?; - self.sink - .emit(isa::InstructionInternal::CallIndirect(index)); - } - - Drop => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::Drop); - } - Select => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::Select); - } - - GetLocal(index) => { - // We need to calculate relative depth before validation since - // it will change the value stack size. - let depth = relative_local_depth(index, &context.locals, &context.value_stack)?; - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::GetLocal(depth)); - } - SetLocal(index) => { - context.step(instruction)?; - let depth = relative_local_depth(index, &context.locals, &context.value_stack)?; - self.sink.emit(isa::InstructionInternal::SetLocal(depth)); - } - TeeLocal(index) => { - context.step(instruction)?; - let depth = relative_local_depth(index, &context.locals, &context.value_stack)?; - self.sink.emit(isa::InstructionInternal::TeeLocal(depth)); - } - GetGlobal(index) => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::GetGlobal(index)); - } - SetGlobal(index) => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::SetGlobal(index)); - } - - I32Load(_, offset) => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::I32Load(offset)); - } - I64Load(_, offset) => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::I64Load(offset)); - } - F32Load(_, offset) => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::F32Load(offset)); - } - F64Load(_, offset) => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::F64Load(offset)); - } - I32Load8S(_, offset) => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::I32Load8S(offset)); - } - I32Load8U(_, offset) => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::I32Load8U(offset)); - } - I32Load16S(_, offset) => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::I32Load16S(offset)); - } - I32Load16U(_, offset) => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::I32Load16U(offset)); - } - I64Load8S(_, offset) => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::I64Load8S(offset)); - } - I64Load8U(_, offset) => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::I64Load8U(offset)); - } - I64Load16S(_, offset) => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::I64Load16S(offset)); - } - I64Load16U(_, offset) => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::I64Load16U(offset)); - } - I64Load32S(_, offset) => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::I64Load32S(offset)); - } - I64Load32U(_, offset) => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::I64Load32U(offset)); - } - - I32Store(_, offset) => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::I32Store(offset)); - } - I64Store(_, offset) => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::I64Store(offset)); - } - F32Store(_, offset) => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::F32Store(offset)); - } - F64Store(_, offset) => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::F64Store(offset)); - } - I32Store8(_, offset) => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::I32Store8(offset)); - } - I32Store16(_, offset) => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::I32Store16(offset)); - } - I64Store8(_, offset) => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::I64Store8(offset)); - } - I64Store16(_, offset) => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::I64Store16(offset)); - } - I64Store32(_, offset) => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::I64Store32(offset)); - } - - CurrentMemory(_) => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::CurrentMemory); - } - GrowMemory(_) => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::GrowMemory); - } - - I32Const(v) => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::I32Const(v)); - } - I64Const(v) => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::I64Const(v)); - } - F32Const(v) => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::F32Const(v)); - } - F64Const(v) => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::F64Const(v)); - } - - I32Eqz => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::I32Eqz); - } - I32Eq => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::I32Eq); - } - I32Ne => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::I32Ne); - } - I32LtS => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::I32LtS); - } - I32LtU => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::I32LtU); - } - I32GtS => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::I32GtS); - } - I32GtU => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::I32GtU); - } - I32LeS => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::I32LeS); - } - I32LeU => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::I32LeU); - } - I32GeS => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::I32GeS); - } - I32GeU => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::I32GeU); - } - - I64Eqz => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::I64Eqz); - } - I64Eq => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::I64Eq); - } - I64Ne => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::I64Ne); - } - I64LtS => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::I64LtS); - } - I64LtU => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::I64LtU); - } - I64GtS => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::I64GtS); - } - I64GtU => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::I64GtU); - } - I64LeS => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::I64LeS); - } - I64LeU => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::I64LeU); - } - I64GeS => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::I64GeS); - } - I64GeU => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::I64GeU); - } - - F32Eq => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::F32Eq); - } - F32Ne => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::F32Ne); - } - F32Lt => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::F32Lt); - } - F32Gt => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::F32Gt); - } - F32Le => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::F32Le); - } - F32Ge => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::F32Ge); - } - - F64Eq => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::F64Eq); - } - F64Ne => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::F64Ne); - } - F64Lt => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::F64Lt); - } - F64Gt => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::F64Gt); - } - F64Le => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::F64Le); - } - F64Ge => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::F64Ge); - } - - I32Clz => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::I32Clz); - } - I32Ctz => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::I32Ctz); - } - I32Popcnt => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::I32Popcnt); - } - I32Add => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::I32Add); - } - I32Sub => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::I32Sub); - } - I32Mul => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::I32Mul); - } - I32DivS => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::I32DivS); - } - I32DivU => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::I32DivU); - } - I32RemS => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::I32RemS); - } - I32RemU => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::I32RemU); - } - I32And => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::I32And); - } - I32Or => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::I32Or); - } - I32Xor => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::I32Xor); - } - I32Shl => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::I32Shl); - } - I32ShrS => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::I32ShrS); - } - I32ShrU => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::I32ShrU); - } - I32Rotl => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::I32Rotl); - } - I32Rotr => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::I32Rotr); - } - - I64Clz => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::I64Clz); - } - I64Ctz => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::I64Ctz); - } - I64Popcnt => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::I64Popcnt); - } - I64Add => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::I64Add); - } - I64Sub => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::I64Sub); - } - I64Mul => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::I64Mul); - } - I64DivS => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::I64DivS); - } - I64DivU => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::I64DivU); - } - I64RemS => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::I64RemS); - } - I64RemU => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::I64RemU); - } - I64And => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::I64And); - } - I64Or => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::I64Or); - } - I64Xor => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::I64Xor); - } - I64Shl => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::I64Shl); - } - I64ShrS => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::I64ShrS); - } - I64ShrU => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::I64ShrU); - } - I64Rotl => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::I64Rotl); - } - I64Rotr => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::I64Rotr); - } - - F32Abs => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::F32Abs); - } - F32Neg => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::F32Neg); - } - F32Ceil => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::F32Ceil); - } - F32Floor => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::F32Floor); - } - F32Trunc => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::F32Trunc); - } - F32Nearest => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::F32Nearest); - } - F32Sqrt => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::F32Sqrt); - } - F32Add => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::F32Add); - } - F32Sub => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::F32Sub); - } - F32Mul => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::F32Mul); - } - F32Div => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::F32Div); - } - F32Min => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::F32Min); - } - F32Max => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::F32Max); - } - F32Copysign => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::F32Copysign); - } - - F64Abs => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::F64Abs); - } - F64Neg => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::F64Neg); - } - F64Ceil => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::F64Ceil); - } - F64Floor => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::F64Floor); - } - F64Trunc => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::F64Trunc); - } - F64Nearest => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::F64Nearest); - } - F64Sqrt => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::F64Sqrt); - } - F64Add => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::F64Add); - } - F64Sub => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::F64Sub); - } - F64Mul => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::F64Mul); - } - F64Div => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::F64Div); - } - F64Min => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::F64Min); - } - F64Max => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::F64Max); - } - F64Copysign => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::F64Copysign); - } - - I32WrapI64 => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::I32WrapI64); - } - I32TruncSF32 => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::I32TruncSF32); - } - I32TruncUF32 => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::I32TruncUF32); - } - I32TruncSF64 => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::I32TruncSF64); - } - I32TruncUF64 => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::I32TruncUF64); - } - I64ExtendSI32 => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::I64ExtendSI32); - } - I64ExtendUI32 => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::I64ExtendUI32); - } - I64TruncSF32 => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::I64TruncSF32); - } - I64TruncUF32 => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::I64TruncUF32); - } - I64TruncSF64 => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::I64TruncSF64); - } - I64TruncUF64 => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::I64TruncUF64); - } - F32ConvertSI32 => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::F32ConvertSI32); - } - F32ConvertUI32 => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::F32ConvertUI32); - } - F32ConvertSI64 => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::F32ConvertSI64); - } - F32ConvertUI64 => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::F32ConvertUI64); - } - F32DemoteF64 => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::F32DemoteF64); - } - F64ConvertSI32 => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::F64ConvertSI32); - } - F64ConvertUI32 => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::F64ConvertUI32); - } - F64ConvertSI64 => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::F64ConvertSI64); - } - F64ConvertUI64 => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::F64ConvertUI64); - } - F64PromoteF32 => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::F64PromoteF32); - } - - I32ReinterpretF32 => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::I32ReinterpretF32); - } - I64ReinterpretF64 => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::I64ReinterpretF64); - } - F32ReinterpretI32 => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::F32ReinterpretI32); - } - F64ReinterpretI64 => { - context.step(instruction)?; - self.sink.emit(isa::InstructionInternal::F64ReinterpretI64); - } - _ => { - context.step(instruction)?; - } - }; - - assert_eq!(self.label_stack.len(), context.frame_stack.len(),); - - Ok(()) - } -} - /// Function validation context. pub struct FunctionValidationContext<'a> { /// Wasm module - module: &'a ModuleContext, + pub module: &'a ModuleContext, /// Local variables. - locals: Locals<'a>, + pub locals: Locals<'a>, /// Value stack. - value_stack: StackWithLimit, + pub value_stack: StackWithLimit, /// Frame stack. - frame_stack: StackWithLimit, + pub frame_stack: StackWithLimit, /// Function return type. - return_type: BlockType, + pub return_type: BlockType, } impl<'a> FunctionValidationContext<'a> { @@ -1133,10 +183,6 @@ impl<'a> FunctionValidationContext<'a> { ctx } - fn return_type(&self) -> BlockType { - self.return_type - } - pub fn step(&mut self, instruction: &Instruction) -> Result<(), Error> { use self::Instruction::*; @@ -1217,7 +263,7 @@ impl<'a> FunctionValidationContext<'a> { // an explicit return. // Check the return type. - if let BlockType::Value(value_type) = self.return_type() { + if let BlockType::Value(value_type) = self.return_type { tee_value(&mut self.value_stack, &self.frame_stack, value_type.into())?; } } @@ -1241,7 +287,7 @@ impl<'a> FunctionValidationContext<'a> { make_top_frame_polymorphic(&mut self.value_stack, &mut self.frame_stack); } Return => { - if let BlockType::Value(value_type) = self.return_type() { + if let BlockType::Value(value_type) = self.return_type { tee_value(&mut self.value_stack, &self.frame_stack, value_type.into())?; } make_top_frame_polymorphic(&mut self.value_stack, &mut self.frame_stack); @@ -2162,298 +1208,20 @@ fn pop_label( Ok(()) } -fn top_label(frame_stack: &StackWithLimit) -> &BlockFrame { +pub fn top_label(frame_stack: &StackWithLimit) -> &BlockFrame { // TODO: This actually isn't safe. frame_stack .top() .expect("this function can't be called with empty frame stack") } -fn require_label( +pub fn require_label( depth: u32, frame_stack: &StackWithLimit, ) -> Result<&BlockFrame, Error> { Ok(frame_stack.get(depth as usize)?) } -fn compute_drop_keep( - in_stack_polymorphic_state: bool, - started_with: StartedWith, - block_type: BlockType, - actual_value_stack_height: usize, - start_value_stack_height: usize, -) -> Result { - // Find out how many values we need to keep (copy to the new stack location after the drop). - let keep: isa::Keep = match (started_with, block_type) { - // A loop doesn't take a value upon a branch. It can return value - // only via reaching it's closing `End` operator. - (StartedWith::Loop, _) => isa::Keep::None, - - (_, BlockType::Value(_)) => isa::Keep::Single, - (_, BlockType::NoResult) => isa::Keep::None, - }; - - // Find out how many values we need to discard. - let drop = if in_stack_polymorphic_state { - // Polymorphic stack is a weird state. Fortunately, it is always about the code that - // will not be executed, so we don't bother and return 0 here. - 0 - } else { - if actual_value_stack_height < start_value_stack_height { - return Err(Error(format!( - "Stack underflow detected: value stack height ({}) is lower than minimum stack len ({})", - actual_value_stack_height, - start_value_stack_height, - ))); - } - if (actual_value_stack_height as u32 - start_value_stack_height as u32) < keep as u32 { - return Err(Error(format!( - "Stack underflow detected: asked to keep {:?} values, but there are only {}", - keep, - actual_value_stack_height as u32 - start_value_stack_height as u32, - ))); - } - (actual_value_stack_height as u32 - start_value_stack_height as u32) - keep as u32 - }; - - Ok(isa::DropKeep { drop, keep }) -} - -fn require_target( - depth: u32, - value_stack_height: usize, - frame_stack: &StackWithLimit, - label_stack: &[BlockFrameType], -) -> Result { - let is_stack_polymorphic = top_label(frame_stack).polymorphic_stack; - let frame = require_label(depth, frame_stack)?; - - // TODO: Clean this mess. - let label = label_stack - .get(label_stack.len() - 1 - (depth as usize)) - .expect("this is ensured by `require_label` above"); - - let drop_keep = compute_drop_keep( - is_stack_polymorphic, - frame.started_with, - frame.block_type, - value_stack_height, - frame.value_stack_len, - )?; - - Ok(Target { - label: label.br_destination(), - drop_keep, - }) -} - -/// Compute drop/keep for the return statement. -/// -/// This function is a bit of unusual since it is called before validation and thus -/// should deal with invalid code. -fn drop_keep_return( - locals: &Locals, - value_stack: &StackWithLimit, - frame_stack: &StackWithLimit, -) -> Result { - if frame_stack.is_empty() { - return Err(Error( - "drop_keep_return can't be called with the frame stack empty".into(), - )); - } - - let is_stack_polymorphic = top_label(frame_stack).polymorphic_stack; - let deepest = (frame_stack.len() - 1) as u32; - let frame = require_label(deepest, frame_stack).expect("frame_stack is not empty"); - let mut drop_keep = compute_drop_keep( - is_stack_polymorphic, - frame.started_with, - frame.block_type, - value_stack.len(), - frame.value_stack_len, - )?; - - // Drop all local variables and parameters upon exit. - drop_keep.drop += locals.count(); - - Ok(drop_keep) -} - fn require_local(locals: &Locals, idx: u32) -> Result { Ok(locals.type_of_local(idx)?) } - -/// Returns a relative depth on the stack of a local variable specified -/// by `idx`. -/// -/// See stack layout definition in mod isa. -fn relative_local_depth( - idx: u32, - locals: &Locals, - value_stack: &StackWithLimit, -) -> Result { - let value_stack_height = value_stack.len() as u32; - let locals_and_params_count = locals.count(); - - let depth = value_stack_height - .checked_add(locals_and_params_count) - .and_then(|x| x.checked_sub(idx)) - .ok_or_else(|| Error(String::from("Locals range not in 32-bit range")))?; - Ok(depth) -} - -/// The target of a branch instruction. -/// -/// It references a `LabelId` instead of exact instruction address. This is handy -/// for emitting code right away with labels resolved later. -#[derive(Clone)] -struct Target { - label: LabelId, - drop_keep: isa::DropKeep, -} - -/// Identifier of a label. -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] -struct LabelId(usize); - -#[derive(Debug, PartialEq, Eq)] -enum Label { - Resolved(u32), - NotResolved, -} - -struct Sink { - ins: isa::Instructions, - labels: Vec<(Label, Vec)>, -} - -impl Sink { - fn with_instruction_capacity(capacity: usize) -> Sink { - Sink { - ins: isa::Instructions::with_capacity(capacity), - labels: Vec::new(), - } - } - - fn cur_pc(&self) -> u32 { - self.ins.current_pc() - } - - 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) => { - unresolved.push(reloc_creator()); - u32::max_value() - } - } - } - - fn emit(&mut self, instruction: isa::InstructionInternal) { - self.ins.push(instruction); - } - - fn emit_br(&mut self, target: Target) { - let Target { label, drop_keep } = target; - let pc = self.cur_pc(); - let dst_pc = self.pc_or_placeholder(label, || isa::Reloc::Br { pc }); - self.ins.push(isa::InstructionInternal::Br(isa::Target { - dst_pc, - drop_keep: drop_keep.into(), - })); - } - - fn emit_br_eqz(&mut self, target: Target) { - let Target { label, drop_keep } = target; - let pc = self.cur_pc(); - let dst_pc = self.pc_or_placeholder(label, || isa::Reloc::Br { pc }); - self.ins - .push(isa::InstructionInternal::BrIfEqz(isa::Target { - dst_pc, - drop_keep: drop_keep.into(), - })); - } - - fn emit_br_nez(&mut self, target: Target) { - let Target { label, drop_keep } = target; - let pc = self.cur_pc(); - let dst_pc = self.pc_or_placeholder(label, || isa::Reloc::Br { pc }); - self.ins - .push(isa::InstructionInternal::BrIfNez(isa::Target { - dst_pc, - drop_keep: drop_keep.into(), - })); - } - - fn emit_br_table(&mut self, targets: &[Target], default: Target) { - use core::iter; - - let pc = self.cur_pc(); - - self.ins.push(isa::InstructionInternal::BrTable { - count: targets.len() as u32 + 1, - }); - - for (idx, &Target { label, drop_keep }) in - targets.iter().chain(iter::once(&default)).enumerate() - { - let dst_pc = self.pc_or_placeholder(label, || isa::Reloc::BrTable { pc, idx }); - self.ins - .push(isa::InstructionInternal::BrTableTarget(isa::Target { - dst_pc, - drop_keep: drop_keep.into(), - })); - } - } - - /// Create a new unresolved label. - fn new_label(&mut self) -> LabelId { - let label_idx = self.labels.len(); - self.labels.push((Label::NotResolved, Vec::new())); - LabelId(label_idx) - } - - /// Resolve the label at the current position. - /// - /// Panics if the label is already resolved. - fn resolve_label(&mut self, label: LabelId) { - use core::mem; - - if let (Label::Resolved(_), _) = self.labels[label.0] { - panic!("Trying to resolve already resolved label"); - } - let dst_pc = self.cur_pc(); - - // Patch all relocations that was previously recorded for this - // particular label. - let unresolved_rels = mem::replace(&mut self.labels[label.0].1, Vec::new()); - for reloc in unresolved_rels { - 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::Instructions. - fn into_inner(self) -> isa::Instructions { - // At this moment all labels should be resolved. - assert!( - { - self.labels - .iter() - .all(|(state, unresolved)| match (state, unresolved) { - (Label::Resolved(_), unresolved) if unresolved.is_empty() => true, - _ => false, - }) - }, - "there are unresolved labels left: {:?}", - self.labels - ); - self.ins - } -} diff --git a/src/validation/mod.rs b/src/validation/mod.rs index ba2429c..fbe85e4 100644 --- a/src/validation/mod.rs +++ b/src/validation/mod.rs @@ -18,15 +18,18 @@ use parity_wasm::elements::{ Module, ResizableLimits, TableType, Type, ValueType, }; -mod context; -mod func; -mod util; +pub mod context; +pub mod func; +pub mod util; -#[cfg(test)] -mod tests; +// TODO: Uncomment +// #[cfg(test)] +// mod tests; +// TODO: Consider using a type other than String, because +// of formatting machinary is not welcomed in substrate runtimes. #[derive(Debug)] -pub struct Error(String); +pub struct Error(pub String); impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { @@ -197,31 +200,6 @@ pub trait FunctionValidator { fn finish(self) -> Self::Output; } -pub struct WasmiValidation { - code_map: Vec, -} - -impl Validation for WasmiValidation { - type Output = Vec; - type FunctionValidator = func::Compiler; - fn new(_module: &Module) -> Self { - WasmiValidation { - // TODO: with capacity? - code_map: Vec::new(), - } - } - fn on_function_validated( - &mut self, - _index: u32, - output: isa::Instructions, - ) { - self.code_map.push(output); - } - fn finish(self) -> Vec { - self.code_map - } -} - // TODO: Rename to validate_module pub fn validate_module2(module: &Module) -> Result { let mut context_builder = ModuleContextBuilder::new(); @@ -314,14 +292,13 @@ pub fn validate_module2(module: &Module) -> Result(&context, function, function_body).map_err( - |Error(ref msg)| { + let output = func::drive::(&context, function, function_body) + .map_err(|Error(ref msg)| { Error(format!( "Function #{} reading/validation error: {}", index, msg )) - }, - )?; + })?; validation.on_function_validated(index as u32, output); } } @@ -433,11 +410,6 @@ pub fn validate_module2(module: &Module) -> Result Result { - let code_map = validate_module2::(&module)?; - Ok(ValidatedModule { module, code_map }) -} - fn validate_limits(limits: &ResizableLimits) -> Result<(), Error> { if let Some(maximum) = limits.maximum() { if limits.initial() > maximum {