diff --git a/src/common/mod.rs b/src/common/mod.rs index 0801984..4b90dc9 100644 --- a/src/common/mod.rs +++ b/src/common/mod.rs @@ -7,6 +7,8 @@ pub const DEFAULT_MEMORY_INDEX: u32 = 0; /// Index of default table. pub const DEFAULT_TABLE_INDEX: u32 = 0; +// TODO: Move BlockFrame under validation. + /// Control stack frame. #[derive(Debug, Clone)] pub struct BlockFrame { diff --git a/src/func.rs b/src/func.rs index b72107d..beeacea 100644 --- a/src/func.rs +++ b/src/func.rs @@ -1,12 +1,12 @@ use std::rc::{Rc, Weak}; use std::fmt; -use std::collections::HashMap; -use parity_wasm::elements::{Local, Opcodes}; +use parity_wasm::elements::Local; use {Trap, TrapKind, Signature}; use host::Externals; use runner::{check_function_args, Interpreter}; use value::RuntimeValue; use module::ModuleInstance; +use isa; /// Reference to a function (See [`FuncInstance`] for details). /// @@ -158,6 +158,5 @@ impl FuncInstance { #[derive(Clone, Debug)] pub struct FuncBody { pub locals: Vec, - pub opcodes: Opcodes, - pub labels: HashMap, + pub code: isa::Instructions, } diff --git a/src/instructions.rs b/src/instructions.rs deleted file mode 100644 index 8af67a9..0000000 --- a/src/instructions.rs +++ /dev/null @@ -1,241 +0,0 @@ -/// Enumeration of instruction set used by wasmi. -/// -/// The instruction set is mostly derived from Wasm. However, -/// there is a substantial difference. -/// -/// # Structured Stack Machine vs Traditional One -/// -/// Wasm is a structured stack machine. Wasm encodes control flow in structures -/// similar to that commonly found in a programming languages -/// such as if, while. That contrasts to a traditional stack machine which -/// encodes all control flow with goto-like instructions. -/// -/// Structured stack machine code aligns well with goals of Wasm, -/// namely providing fast validation of Wasm code and compilation to native code. -/// -/// Unfortunately, the downside of structured stack machine code is -/// that it is less convenient to interpret. For example, let's look at -/// the following example in hypothetical structured stack machine: -/// -/// ``` -/// loop -/// ... -/// if_true_jump_to_end -/// ... -/// end -/// ``` -/// -/// To execute `if_true_jump_to_end` , the interpreter needs to skip all instructions -/// until it reaches the *matching* `end`. That's quite inefficient compared -/// to a plain goto to the specific position. -/// -/// # Differences from Wasm -/// -/// - There is no `nop` instruction. -/// - All control flow strucutres are flattened to plain gotos. -/// - Implicit returns via reaching function scope `End` are replaced with an explicit `return` instruction. -/// - Locals live on the value stack now. -enum Instruction { - /// Keep the specified amount of operands and then - /// drop the rest. - DropKeep { - drop: u32, - keep: u32, - }, - - /// Push a local variable or an argument from the specified depth. - GetLocal { - depth: u32 - }, - - /// Pop a value and put it in at the specified depth. - SetLocal { - depth: u32 - }, - - /// Copy a value to the specified depth. - TeeLocal(u32), - - /// Similar to the Wasm ones, but instead of a label depth - /// they specify direct PC. - Br(u32), - BrIf(u32), - BrTable(Box<[u32]>, u32), - - Unreachable, - Return, - - Call(u32), - CallIndirect(u32, u8), - - Drop, - Select, - - GetGlobal(u32), - SetGlobal(u32), - - // All store/load instructions operate with 'memory immediates' - // which represented here as (flag, offset) tuple - I32Load(u32, u32), - I64Load(u32, u32), - F32Load(u32, u32), - F64Load(u32, u32), - I32Load8S(u32, u32), - I32Load8U(u32, u32), - I32Load16S(u32, u32), - I32Load16U(u32, u32), - I64Load8S(u32, u32), - I64Load8U(u32, u32), - I64Load16S(u32, u32), - I64Load16U(u32, u32), - I64Load32S(u32, u32), - I64Load32U(u32, u32), - I32Store(u32, u32), - I64Store(u32, u32), - F32Store(u32, u32), - F64Store(u32, u32), - I32Store8(u32, u32), - I32Store16(u32, u32), - I64Store8(u32, u32), - I64Store16(u32, u32), - I64Store32(u32, u32), - - CurrentMemory(u8), - GrowMemory(u8), - - I32Const(i32), - I64Const(i64), - F32Const(u32), - F64Const(u64), - - I32Eqz, - I32Eq, - I32Ne, - I32LtS, - I32LtU, - I32GtS, - I32GtU, - I32LeS, - I32LeU, - I32GeS, - I32GeU, - - I64Eqz, - I64Eq, - I64Ne, - I64LtS, - I64LtU, - I64GtS, - I64GtU, - I64LeS, - I64LeU, - I64GeS, - I64GeU, - - F32Eq, - F32Ne, - F32Lt, - F32Gt, - F32Le, - F32Ge, - - F64Eq, - F64Ne, - F64Lt, - F64Gt, - F64Le, - F64Ge, - - I32Clz, - I32Ctz, - I32Popcnt, - I32Add, - I32Sub, - I32Mul, - I32DivS, - I32DivU, - I32RemS, - I32RemU, - I32And, - I32Or, - I32Xor, - I32Shl, - I32ShrS, - I32ShrU, - I32Rotl, - I32Rotr, - - I64Clz, - I64Ctz, - I64Popcnt, - I64Add, - I64Sub, - I64Mul, - I64DivS, - I64DivU, - I64RemS, - I64RemU, - I64And, - I64Or, - I64Xor, - I64Shl, - I64ShrS, - I64ShrU, - I64Rotl, - I64Rotr, - F32Abs, - F32Neg, - F32Ceil, - F32Floor, - F32Trunc, - F32Nearest, - F32Sqrt, - F32Add, - F32Sub, - F32Mul, - F32Div, - F32Min, - F32Max, - F32Copysign, - F64Abs, - F64Neg, - F64Ceil, - F64Floor, - F64Trunc, - F64Nearest, - F64Sqrt, - F64Add, - F64Sub, - F64Mul, - F64Div, - F64Min, - F64Max, - F64Copysign, - - I32WrapI64, - I32TruncSF32, - I32TruncUF32, - I32TruncSF64, - I32TruncUF64, - I64ExtendSI32, - I64ExtendUI32, - I64TruncSF32, - I64TruncUF32, - I64TruncSF64, - I64TruncUF64, - F32ConvertSI32, - F32ConvertUI32, - F32ConvertSI64, - F32ConvertUI64, - F32DemoteF64, - F64ConvertSI32, - F64ConvertUI32, - F64ConvertSI64, - F64ConvertUI64, - F64PromoteF32, - - I32ReinterpretF32, - I64ReinterpretF64, - F32ReinterpretI32, - F64ReinterpretI64, -} diff --git a/src/isa.rs b/src/isa.rs new file mode 100644 index 0000000..c01dc2b --- /dev/null +++ b/src/isa.rs @@ -0,0 +1,256 @@ +//! An instruction set used by wasmi. +//! +//! The instruction set is mostly derived from Wasm. However, +//! there is a substantial difference. +//! +//! # Structured Stack Machine vs Traditional One +//! +//! Wasm is a structured stack machine. Wasm encodes control flow in structures +//! similar to that commonly found in a programming languages +//! such as if, while. That contrasts to a traditional stack machine which +//! encodes all control flow with goto-like instructions. +//! +//! Structured stack machine code aligns well with goals of Wasm, +//! namely providing fast validation of Wasm code and compilation to native code. +//! +//! Unfortunately, the downside of structured stack machine code is +//! that it is less convenient to interpret. For example, let's look at +//! the following example in hypothetical structured stack machine: +//! +//! ``` +//! loop +//! ... +//! if_true_jump_to_end +//! ... +//! end +//! ``` +//! +//! To execute `if_true_jump_to_end` , the interpreter needs to skip all instructions +//! until it reaches the *matching* `end`. That's quite inefficient compared +//! to a plain goto to the specific position. +//! +//! # Differences from Wasm +//! +//! - There is no `nop` instruction. +//! - All control flow strucutres are flattened to plain gotos. +//! - Implicit returns via reaching function scope `End` are replaced with an explicit `return` instruction. +//! - Locals live on the value stack now. +//! - Load/store instructions doesn't take `align` parameter. +//! - *.const store value in straight encoding. +//! - Reserved immediates are ignored for `call_indirect`, `current_memory`, `grow_memory`. +//! + +#[derive(Debug, Clone)] +pub struct Target { + pub dst_pc: u32, + pub drop: u32, + pub keep: u8, +} + +#[allow(unused)] // TODO: Remove +#[derive(Debug, Clone)] +pub enum Instruction { + /// Push a local variable or an argument from the specified depth. + GetLocal { + depth: u32 + }, + + /// Pop a value and put it in at the specified depth. + SetLocal { + depth: u32 + }, + + /// Copy a value to the specified depth. + TeeLocal { depth: u32 }, + + /// Similar to the Wasm ones, but instead of a label depth + /// they specify direct PC. + Br(Target), + BrIfEqz(Target), + BrIfNez(Target), + + /// Last one is the default. + /// + /// Can be less than zero. + BrTable(Box<[Target]>), + + Unreachable, + Return, + + Call(u32), + CallIndirect(u32), + + Drop, + Select, + + GetGlobal(u32), + SetGlobal(u32), + + I32Load(u32), + I64Load(u32), + F32Load(u32), + F64Load(u32), + I32Load8S(u32), + I32Load8U(u32), + I32Load16S(u32), + I32Load16U(u32), + I64Load8S(u32), + I64Load8U(u32), + I64Load16S(u32), + I64Load16U(u32), + I64Load32S(u32), + I64Load32U(u32), + I32Store(u32), + I64Store(u32), + F32Store(u32), + F64Store(u32), + I32Store8(u32), + I32Store16(u32), + I64Store8(u32), + I64Store16(u32), + I64Store32(u32), + + CurrentMemory, + GrowMemory, + + I32Const(i32), + I64Const(i64), + F32Const(u32), + F64Const(u64), + + I32Eqz, + I32Eq, + I32Ne, + I32LtS, + I32LtU, + I32GtS, + I32GtU, + I32LeS, + I32LeU, + I32GeS, + I32GeU, + + I64Eqz, + I64Eq, + I64Ne, + I64LtS, + I64LtU, + I64GtS, + I64GtU, + I64LeS, + I64LeU, + I64GeS, + I64GeU, + + F32Eq, + F32Ne, + F32Lt, + F32Gt, + F32Le, + F32Ge, + + F64Eq, + F64Ne, + F64Lt, + F64Gt, + F64Le, + F64Ge, + + I32Clz, + I32Ctz, + I32Popcnt, + I32Add, + I32Sub, + I32Mul, + I32DivS, + I32DivU, + I32RemS, + I32RemU, + I32And, + I32Or, + I32Xor, + I32Shl, + I32ShrS, + I32ShrU, + I32Rotl, + I32Rotr, + + I64Clz, + I64Ctz, + I64Popcnt, + I64Add, + I64Sub, + I64Mul, + I64DivS, + I64DivU, + I64RemS, + I64RemU, + I64And, + I64Or, + I64Xor, + I64Shl, + I64ShrS, + I64ShrU, + I64Rotl, + I64Rotr, + F32Abs, + F32Neg, + F32Ceil, + F32Floor, + F32Trunc, + F32Nearest, + F32Sqrt, + F32Add, + F32Sub, + F32Mul, + F32Div, + F32Min, + F32Max, + F32Copysign, + F64Abs, + F64Neg, + F64Ceil, + F64Floor, + F64Trunc, + F64Nearest, + F64Sqrt, + F64Add, + F64Sub, + F64Mul, + F64Div, + F64Min, + F64Max, + F64Copysign, + + I32WrapI64, + I32TruncSF32, + I32TruncUF32, + I32TruncSF64, + I32TruncUF64, + I64ExtendSI32, + I64ExtendUI32, + I64TruncSF32, + I64TruncUF32, + I64TruncSF64, + I64TruncUF64, + F32ConvertSI32, + F32ConvertUI32, + F32ConvertSI64, + F32ConvertUI64, + F32DemoteF64, + F64ConvertSI32, + F64ConvertUI32, + F64ConvertSI64, + F64ConvertUI64, + F64PromoteF32, + + I32ReinterpretF32, + I64ReinterpretF64, + F32ReinterpretI32, + F64ReinterpretI64, +} + +#[derive(Debug, Clone)] +pub struct Instructions { + pub code: Vec, +} diff --git a/src/lib.rs b/src/lib.rs index c99d1db..c46eff5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -356,7 +356,7 @@ mod imports; mod global; mod func; mod types; -mod instructions; +mod isa; #[cfg(test)] mod tests; @@ -379,7 +379,7 @@ pub mod memory_units { /// Deserialized module prepared for instantiation. pub struct Module { - labels: HashMap>, + code_map: Vec, module: parity_wasm::elements::Module, } @@ -419,12 +419,12 @@ impl Module { pub fn from_parity_wasm_module(module: parity_wasm::elements::Module) -> Result { use validation::{validate_module, ValidatedModule}; let ValidatedModule { - labels, + code_map, module, } = validate_module(module)?; Ok(Module { - labels, + code_map, module, }) } @@ -525,7 +525,7 @@ impl Module { &self.module } - pub(crate) fn labels(&self) -> &HashMap> { - &self.labels + pub(crate) fn code(&self) -> &Vec { + &self.code_map } } diff --git a/src/module.rs b/src/module.rs index 820e66a..a3a9fa0 100644 --- a/src/module.rs +++ b/src/module.rs @@ -291,7 +291,7 @@ impl ModuleInstance { } } - let labels = loaded_module.labels(); + let code = loaded_module.code(); { let funcs = module.function_section().map(|fs| fs.entries()).unwrap_or( &[], @@ -308,13 +308,12 @@ impl ModuleInstance { let signature = instance.signature_by_index(ty.type_ref()).expect( "Due to validation type should exists", ); - let labels = labels.get(&index).expect( + let code = code.get(index).expect( "At func validation time labels are collected; Collected labels are added by index; qed", ).clone(); let func_body = FuncBody { locals: body.locals().to_vec(), - opcodes: body.code().clone(), - labels: labels, + code: code, }; let func_instance = FuncInstance::alloc_internal(Rc::downgrade(&instance.0), signature, func_body); diff --git a/src/runner.rs b/src/runner.rs index 51c9661..83c7e16 100644 --- a/src/runner.rs +++ b/src/runner.rs @@ -84,7 +84,7 @@ impl<'a, E: Externals> Interpreter<'a, E> { if !function_context.is_initialized() { let return_type = function_context.return_type; function_context.initialize(&function_body.locals); - function_context.push_frame(&function_body.labels, BlockFrameType::Function, return_type).map_err(Trap::new)?; + function_context.push_frame(BlockFrameType::Function, return_type).map_err(Trap::new)?; } let function_return = self.do_run_function(&mut function_context, function_body.opcodes.elements(), &function_body.labels).map_err(Trap::new)?; @@ -127,7 +127,7 @@ impl<'a, E: Externals> Interpreter<'a, E> { } } - fn do_run_function(&mut self, function_context: &mut FunctionContext, function_body: &[Opcode], function_labels: &HashMap) -> Result { + fn do_run_function(&mut self, function_context: &mut FunctionContext, function_body: &[Opcode]) -> Result { loop { let instruction = &function_body[function_context.position]; @@ -400,7 +400,7 @@ impl<'a, E: Externals> Interpreter<'a, E> { Ok(InstructionOutcome::RunNextInstruction) } - fn run_else(&mut self, context: &mut FunctionContext, labels: &HashMap) -> Result { + fn run_else(&mut self, context: &mut FunctionContext) -> Result { let end_pos = labels[&context.position]; context.pop_frame(false)?; context.position = end_pos; @@ -1144,7 +1144,7 @@ impl FunctionContext { &self.frame_stack } - pub fn push_frame(&mut self, labels: &HashMap, frame_type: BlockFrameType, block_type: BlockType) -> Result<(), TrapKind> { + pub fn push_frame(&mut self, frame_type: BlockFrameType, block_type: BlockType) -> Result<(), TrapKind> { let begin_position = self.position; let branch_position = match frame_type { BlockFrameType::Function => usize::MAX, diff --git a/src/validation/func.rs b/src/validation/func.rs index b15acc1..700d732 100644 --- a/src/validation/func.rs +++ b/src/validation/func.rs @@ -8,13 +8,47 @@ use validation::Error; use validation::util::Locals; use common::stack::StackWithLimit; -use common::{BlockFrame, BlockFrameType}; +use isa; /// Maximum number of entries in value stack per function. const DEFAULT_VALUE_STACK_LIMIT: usize = 16384; /// Maximum number of entries in frame stack per function. const DEFAULT_FRAME_STACK_LIMIT: usize = 16384; +/// Control stack frame. +#[derive(Debug, Clone)] +struct BlockFrame { + /// Frame type. + frame_type: BlockFrameType, + /// A signature, which is a block signature type indicating the number and types of result values of the region. + block_type: BlockType, + /// A label for reference to block instruction. + begin_position: usize, + // TODO: + branch_label: LabelId, + /// 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, + /// Boolean which signals whether value stack became polymorphic. Value stack starts in 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, +} + +/// Type of block frame. +#[derive(Debug, Clone, Copy, PartialEq)] +enum BlockFrameType { + /// Function frame. + Function, + /// Usual block frame. + Block, + /// Loop frame (branching to the beginning of block). + Loop, + /// True-subblock of if expression. + IfTrue, + /// False-subblock of if expression. + IfFalse, +} + /// Function validation context. struct FunctionValidationContext<'a> { /// Wasm module @@ -29,8 +63,9 @@ struct FunctionValidationContext<'a> { frame_stack: StackWithLimit, /// Function return type. None if validating expression. return_type: Option, - /// Labels positions. - labels: HashMap, + + // TODO: comment + sink: Sink, } /// Value type on the stack. @@ -59,7 +94,7 @@ impl Validator { module: &ModuleContext, func: &Func, body: &FuncBody, - ) -> Result, Error> { + ) -> Result { let (params, result_ty) = module.require_function_type(func.type_ref())?; let mut context = FunctionValidationContext::new( @@ -70,13 +105,15 @@ impl Validator { result_ty, ); - context.push_label(BlockFrameType::Function, result_ty)?; + let func_label = context.sink.new_label(); + context.push_label(BlockFrameType::Function, result_ty, func_label)?; Validator::validate_function_block(&mut context, body.code().elements())?; while !context.frame_stack.is_empty() { + // TODO: resolve labels? context.pop_label()?; } - Ok(context.into_labels()) + Ok(context.into_code()) } fn validate_function_block(context: &mut FunctionValidationContext, body: &[Opcode]) -> Result<(), Error> { @@ -100,198 +137,943 @@ impl Validator { } fn validate_instruction(context: &mut FunctionValidationContext, opcode: &Opcode) -> Result { + // TODO: use InstructionOutcome::*; + use self::Opcode::*; match *opcode { - Unreachable => Ok(InstructionOutcome::Unreachable), - Nop => Ok(InstructionOutcome::ValidateNextInstruction), - Block(block_type) => Validator::validate_block(context, block_type), - Loop(block_type) => Validator::validate_loop(context, block_type), - If(block_type) => Validator::validate_if(context, block_type), - Else => Validator::validate_else(context), - End => Validator::validate_end(context), - Br(idx) => Validator::validate_br(context, idx), - BrIf(idx) => Validator::validate_br_if(context, idx), - BrTable(ref table, default) => Validator::validate_br_table(context, table, default), - Return => Validator::validate_return(context), + // Nop instruction doesn't do anything. It is safe to just skip it. + Nop => {}, - Call(index) => Validator::validate_call(context, index), - CallIndirect(index, _reserved) => Validator::validate_call_indirect(context, index), + Unreachable => { + return Ok(InstructionOutcome::Unreachable); + }, - Drop => Validator::validate_drop(context), - Select => Validator::validate_select(context), + Block(block_type) => { + let label = context.sink.new_label(); + context.push_label(BlockFrameType::Block, block_type, label)?; + }, + Loop(block_type) => { + // Resolve loop header right away. + let loop_header = context.top_label()?.branch_label; + context.sink.resolve_label(loop_header); - GetLocal(index) => Validator::validate_get_local(context, index), - SetLocal(index) => Validator::validate_set_local(context, index), - TeeLocal(index) => Validator::validate_tee_local(context, index), - GetGlobal(index) => Validator::validate_get_global(context, index), - SetGlobal(index) => Validator::validate_set_global(context, index), + context.push_label(BlockFrameType::Loop, block_type, loop_header)?; + }, + If(block_type) => { + // if + // .. + // end + // + // translates to -> + // + // br_if_not $end + // .. + // $end + let end_label = context.sink.new_label(); + context.pop_value(ValueType::I32.into())?; + context.push_label(BlockFrameType::IfTrue, block_type, end_label)?; + }, + Else => { + // 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). + let end_target = context.require_target(0)?; + let end_label = end_target.label; + context.sink.emit_br(end_target); - I32Load(align, _) => Validator::validate_load(context, align, 4, ValueType::I32), - I64Load(align, _) => Validator::validate_load(context, align, 8, ValueType::I64), - F32Load(align, _) => Validator::validate_load(context, align, 4, ValueType::F32), - F64Load(align, _) => Validator::validate_load(context, align, 8, ValueType::F64), - I32Load8S(align, _) => Validator::validate_load(context, align, 1, ValueType::I32), - I32Load8U(align, _) => Validator::validate_load(context, align, 1, ValueType::I32), - I32Load16S(align, _) => Validator::validate_load(context, align, 2, ValueType::I32), - I32Load16U(align, _) => Validator::validate_load(context, align, 2, ValueType::I32), - I64Load8S(align, _) => Validator::validate_load(context, align, 1, ValueType::I64), - I64Load8U(align, _) => Validator::validate_load(context, align, 1, ValueType::I64), - I64Load16S(align, _) => Validator::validate_load(context, align, 2, ValueType::I64), - I64Load16U(align, _) => Validator::validate_load(context, align, 2, ValueType::I64), - I64Load32S(align, _) => Validator::validate_load(context, align, 4, ValueType::I64), - I64Load32U(align, _) => Validator::validate_load(context, align, 4, ValueType::I64), + // Then, we validate. Validator will pop the if..else block and the push else..end block. + let block_type = { + let top_frame = context.top_label()?; + if top_frame.frame_type != BlockFrameType::IfTrue { + return Err(Error("Misplaced else instruction".into())); + } + top_frame.block_type + }; + context.pop_label()?; - I32Store(align, _) => Validator::validate_store(context, align, 4, ValueType::I32), - I64Store(align, _) => Validator::validate_store(context, align, 8, ValueType::I64), - F32Store(align, _) => Validator::validate_store(context, align, 4, ValueType::F32), - F64Store(align, _) => Validator::validate_store(context, align, 8, ValueType::F64), - I32Store8(align, _) => Validator::validate_store(context, align, 1, ValueType::I32), - I32Store16(align, _) => Validator::validate_store(context, align, 2, ValueType::I32), - I64Store8(align, _) => Validator::validate_store(context, align, 1, ValueType::I64), - I64Store16(align, _) => Validator::validate_store(context, align, 2, ValueType::I64), - I64Store32(align, _) => Validator::validate_store(context, align, 4, ValueType::I64), + if let BlockType::Value(value_type) = block_type { + context.pop_value(value_type.into())?; + } + context.push_label(BlockFrameType::IfFalse, block_type, end_label)?; + }, + End => { + { + let frame_type = context.top_label()?.frame_type; - CurrentMemory(_) => Validator::validate_current_memory(context), - GrowMemory(_) => Validator::validate_grow_memory(context), + // If this end for a non-loop frame then we resolve it's label location to here. + if frame_type != BlockFrameType::Loop { + let loop_header = context.top_label()?.branch_label; + context.sink.resolve_label(loop_header); + } + } - I32Const(_) => Validator::validate_const(context, ValueType::I32), - I64Const(_) => Validator::validate_const(context, ValueType::I64), - F32Const(_) => Validator::validate_const(context, ValueType::F32), - F64Const(_) => Validator::validate_const(context, ValueType::F64), + { + let top_frame = context.top_label()?; + if top_frame.frame_type == BlockFrameType::IfTrue { + if top_frame.block_type != BlockType::NoResult { + return Err(Error(format!("If block without else required to have NoResult block type. But it have {:?} type", top_frame.block_type))); + } + } + } - I32Eqz => Validator::validate_testop(context, ValueType::I32), - I32Eq => Validator::validate_relop(context, ValueType::I32), - I32Ne => Validator::validate_relop(context, ValueType::I32), - I32LtS => Validator::validate_relop(context, ValueType::I32), - I32LtU => Validator::validate_relop(context, ValueType::I32), - I32GtS => Validator::validate_relop(context, ValueType::I32), - I32GtU => Validator::validate_relop(context, ValueType::I32), - I32LeS => Validator::validate_relop(context, ValueType::I32), - I32LeU => Validator::validate_relop(context, ValueType::I32), - I32GeS => Validator::validate_relop(context, ValueType::I32), - I32GeU => Validator::validate_relop(context, ValueType::I32), + context.pop_label()?; + }, + Br(depth) => { + Validator::validate_br(context, depth)?; + let target = context.require_target(depth)?; + context.sink.emit_br(target); + }, + BrIf(depth) => { + Validator::validate_br_if(context, depth)?; + let target = context.require_target(depth)?; + context.sink.emit_br_nez(target); + }, + BrTable(ref table, default) => { + Validator::validate_br_table(context, table, default)?; + let mut targets = Vec::new(); + for depth in table.iter() { + let target = context.require_target(*depth)?; + targets.push(target); + } + let default = context.require_target(default)?; + context.sink.emit_br_table(&targets, default); + }, + Return => { + Validator::validate_return(context)?; + context.sink.emit(isa::Instruction::Return); + }, - I64Eqz => Validator::validate_testop(context, ValueType::I64), - I64Eq => Validator::validate_relop(context, ValueType::I64), - I64Ne => Validator::validate_relop(context, ValueType::I64), - I64LtS => Validator::validate_relop(context, ValueType::I64), - I64LtU => Validator::validate_relop(context, ValueType::I64), - I64GtS => Validator::validate_relop(context, ValueType::I64), - I64GtU => Validator::validate_relop(context, ValueType::I64), - I64LeS => Validator::validate_relop(context, ValueType::I64), - I64LeU => Validator::validate_relop(context, ValueType::I64), - I64GeS => Validator::validate_relop(context, ValueType::I64), - I64GeU => Validator::validate_relop(context, ValueType::I64), + Call(index) => { + Validator::validate_call(context, index)?; + context.sink.emit(isa::Instruction::Call(index)); + }, + CallIndirect(index, _reserved) => { + Validator::validate_call_indirect(context, index)?; + context.sink.emit(isa::Instruction::CallIndirect(index)); + }, - F32Eq => Validator::validate_relop(context, ValueType::F32), - F32Ne => Validator::validate_relop(context, ValueType::F32), - F32Lt => Validator::validate_relop(context, ValueType::F32), - F32Gt => Validator::validate_relop(context, ValueType::F32), - F32Le => Validator::validate_relop(context, ValueType::F32), - F32Ge => Validator::validate_relop(context, ValueType::F32), + Drop => { + Validator::validate_drop(context)?; + context.sink.emit(isa::Instruction::Drop); + }, + Select => { + Validator::validate_select(context)?; + context.sink.emit(isa::Instruction::Select); + }, - F64Eq => Validator::validate_relop(context, ValueType::F64), - F64Ne => Validator::validate_relop(context, ValueType::F64), - F64Lt => Validator::validate_relop(context, ValueType::F64), - F64Gt => Validator::validate_relop(context, ValueType::F64), - F64Le => Validator::validate_relop(context, ValueType::F64), - F64Ge => Validator::validate_relop(context, ValueType::F64), + GetLocal(index) => { + // We need to calculate relative depth before validation since + // it will change value stack size. + let depth = context.relative_local_depth(index)?; + Validator::validate_get_local(context, index)?; + context.sink.emit( + isa::Instruction::GetLocal { depth }, + ); + }, + SetLocal(index) => { + // We need to calculate relative depth before validation since + // it will change value stack size. + let depth = context.relative_local_depth(index)?; + Validator::validate_set_local(context, index)?; + context.sink.emit( + isa::Instruction::SetLocal { depth }, + ); + }, + TeeLocal(index) => { + // We need to calculate relative depth before validation since + // it will change value stack size. + let depth = context.relative_local_depth(index)?; + Validator::validate_tee_local(context, index)?; + context.sink.emit( + isa::Instruction::TeeLocal { depth }, + ); + }, + GetGlobal(index) => { + Validator::validate_get_global(context, index)?; + context.sink.emit(isa::Instruction::GetGlobal(index)); - I32Clz => Validator::validate_unop(context, ValueType::I32), - I32Ctz => Validator::validate_unop(context, ValueType::I32), - I32Popcnt => Validator::validate_unop(context, ValueType::I32), - I32Add => Validator::validate_binop(context, ValueType::I32), - I32Sub => Validator::validate_binop(context, ValueType::I32), - I32Mul => Validator::validate_binop(context, ValueType::I32), - I32DivS => Validator::validate_binop(context, ValueType::I32), - I32DivU => Validator::validate_binop(context, ValueType::I32), - I32RemS => Validator::validate_binop(context, ValueType::I32), - I32RemU => Validator::validate_binop(context, ValueType::I32), - I32And => Validator::validate_binop(context, ValueType::I32), - I32Or => Validator::validate_binop(context, ValueType::I32), - I32Xor => Validator::validate_binop(context, ValueType::I32), - I32Shl => Validator::validate_binop(context, ValueType::I32), - I32ShrS => Validator::validate_binop(context, ValueType::I32), - I32ShrU => Validator::validate_binop(context, ValueType::I32), - I32Rotl => Validator::validate_binop(context, ValueType::I32), - I32Rotr => Validator::validate_binop(context, ValueType::I32), + }, + SetGlobal(index) => { + Validator::validate_set_global(context, index)?; + context.sink.emit(isa::Instruction::SetGlobal(index)); - I64Clz => Validator::validate_unop(context, ValueType::I64), - I64Ctz => Validator::validate_unop(context, ValueType::I64), - I64Popcnt => Validator::validate_unop(context, ValueType::I64), - I64Add => Validator::validate_binop(context, ValueType::I64), - I64Sub => Validator::validate_binop(context, ValueType::I64), - I64Mul => Validator::validate_binop(context, ValueType::I64), - I64DivS => Validator::validate_binop(context, ValueType::I64), - I64DivU => Validator::validate_binop(context, ValueType::I64), - I64RemS => Validator::validate_binop(context, ValueType::I64), - I64RemU => Validator::validate_binop(context, ValueType::I64), - I64And => Validator::validate_binop(context, ValueType::I64), - I64Or => Validator::validate_binop(context, ValueType::I64), - I64Xor => Validator::validate_binop(context, ValueType::I64), - I64Shl => Validator::validate_binop(context, ValueType::I64), - I64ShrS => Validator::validate_binop(context, ValueType::I64), - I64ShrU => Validator::validate_binop(context, ValueType::I64), - I64Rotl => Validator::validate_binop(context, ValueType::I64), - I64Rotr => Validator::validate_binop(context, ValueType::I64), + }, - F32Abs => Validator::validate_unop(context, ValueType::F32), - F32Neg => Validator::validate_unop(context, ValueType::F32), - F32Ceil => Validator::validate_unop(context, ValueType::F32), - F32Floor => Validator::validate_unop(context, ValueType::F32), - F32Trunc => Validator::validate_unop(context, ValueType::F32), - F32Nearest => Validator::validate_unop(context, ValueType::F32), - F32Sqrt => Validator::validate_unop(context, ValueType::F32), - F32Add => Validator::validate_binop(context, ValueType::F32), - F32Sub => Validator::validate_binop(context, ValueType::F32), - F32Mul => Validator::validate_binop(context, ValueType::F32), - F32Div => Validator::validate_binop(context, ValueType::F32), - F32Min => Validator::validate_binop(context, ValueType::F32), - F32Max => Validator::validate_binop(context, ValueType::F32), - F32Copysign => Validator::validate_binop(context, ValueType::F32), + I32Load(align, offset) => { + Validator::validate_load(context, align, 4, ValueType::I32)?; + context.sink.emit(isa::Instruction::I32Load(offset)); - F64Abs => Validator::validate_unop(context, ValueType::F64), - F64Neg => Validator::validate_unop(context, ValueType::F64), - F64Ceil => Validator::validate_unop(context, ValueType::F64), - F64Floor => Validator::validate_unop(context, ValueType::F64), - F64Trunc => Validator::validate_unop(context, ValueType::F64), - F64Nearest => Validator::validate_unop(context, ValueType::F64), - F64Sqrt => Validator::validate_unop(context, ValueType::F64), - F64Add => Validator::validate_binop(context, ValueType::F64), - F64Sub => Validator::validate_binop(context, ValueType::F64), - F64Mul => Validator::validate_binop(context, ValueType::F64), - F64Div => Validator::validate_binop(context, ValueType::F64), - F64Min => Validator::validate_binop(context, ValueType::F64), - F64Max => Validator::validate_binop(context, ValueType::F64), - F64Copysign => Validator::validate_binop(context, ValueType::F64), + }, + I64Load(align, offset) => { + Validator::validate_load(context, align, 8, ValueType::I64)?; + context.sink.emit(isa::Instruction::I64Load(offset)); - I32WrapI64 => Validator::validate_cvtop(context, ValueType::I64, ValueType::I32), - I32TruncSF32 => Validator::validate_cvtop(context, ValueType::F32, ValueType::I32), - I32TruncUF32 => Validator::validate_cvtop(context, ValueType::F32, ValueType::I32), - I32TruncSF64 => Validator::validate_cvtop(context, ValueType::F64, ValueType::I32), - I32TruncUF64 => Validator::validate_cvtop(context, ValueType::F64, ValueType::I32), - I64ExtendSI32 => Validator::validate_cvtop(context, ValueType::I32, ValueType::I64), - I64ExtendUI32 => Validator::validate_cvtop(context, ValueType::I32, ValueType::I64), - I64TruncSF32 => Validator::validate_cvtop(context, ValueType::F32, ValueType::I64), - I64TruncUF32 => Validator::validate_cvtop(context, ValueType::F32, ValueType::I64), - I64TruncSF64 => Validator::validate_cvtop(context, ValueType::F64, ValueType::I64), - I64TruncUF64 => Validator::validate_cvtop(context, ValueType::F64, ValueType::I64), - F32ConvertSI32 => Validator::validate_cvtop(context, ValueType::I32, ValueType::F32), - F32ConvertUI32 => Validator::validate_cvtop(context, ValueType::I32, ValueType::F32), - F32ConvertSI64 => Validator::validate_cvtop(context, ValueType::I64, ValueType::F32), - F32ConvertUI64 => Validator::validate_cvtop(context, ValueType::I64, ValueType::F32), - F32DemoteF64 => Validator::validate_cvtop(context, ValueType::F64, ValueType::F32), - F64ConvertSI32 => Validator::validate_cvtop(context, ValueType::I32, ValueType::F64), - F64ConvertUI32 => Validator::validate_cvtop(context, ValueType::I32, ValueType::F64), - F64ConvertSI64 => Validator::validate_cvtop(context, ValueType::I64, ValueType::F64), - F64ConvertUI64 => Validator::validate_cvtop(context, ValueType::I64, ValueType::F64), - F64PromoteF32 => Validator::validate_cvtop(context, ValueType::F32, ValueType::F64), + }, + F32Load(align, offset) => { + Validator::validate_load(context, align, 4, ValueType::F32)?; + context.sink.emit(isa::Instruction::F32Load(offset)); - I32ReinterpretF32 => Validator::validate_cvtop(context, ValueType::F32, ValueType::I32), - I64ReinterpretF64 => Validator::validate_cvtop(context, ValueType::F64, ValueType::I64), - F32ReinterpretI32 => Validator::validate_cvtop(context, ValueType::I32, ValueType::F32), - F64ReinterpretI64 => Validator::validate_cvtop(context, ValueType::I64, ValueType::F64), + }, + F64Load(align, offset) => { + Validator::validate_load(context, align, 8, ValueType::F64)?; + context.sink.emit(isa::Instruction::F64Load(offset)); + + }, + I32Load8S(align, offset) => { + Validator::validate_load(context, align, 1, ValueType::I32)?; + context.sink.emit(isa::Instruction::I32Load8S(offset)); + + }, + I32Load8U(align, offset) => { + Validator::validate_load(context, align, 1, ValueType::I32)?; + context.sink.emit(isa::Instruction::I32Load8U(offset)); + + }, + I32Load16S(align, offset) => { + Validator::validate_load(context, align, 2, ValueType::I32)?; + context.sink.emit(isa::Instruction::I32Load16S(offset)); + + }, + I32Load16U(align, offset) => { + Validator::validate_load(context, align, 2, ValueType::I32)?; + context.sink.emit(isa::Instruction::I32Load16U(offset)); + + }, + I64Load8S(align, offset) => { + Validator::validate_load(context, align, 1, ValueType::I64)?; + context.sink.emit(isa::Instruction::I64Load8S(offset)); + + }, + I64Load8U(align, offset) => { + Validator::validate_load(context, align, 1, ValueType::I64)?; + context.sink.emit(isa::Instruction::I64Load8U(offset)); + + }, + I64Load16S(align, offset) => { + Validator::validate_load(context, align, 2, ValueType::I64)?; + context.sink.emit(isa::Instruction::I64Load16S(offset)); + + }, + I64Load16U(align, offset) => { + Validator::validate_load(context, align, 2, ValueType::I64)?; + context.sink.emit(isa::Instruction::I64Load16U(offset)); + + }, + I64Load32S(align, offset) => { + Validator::validate_load(context, align, 4, ValueType::I64)?; + context.sink.emit(isa::Instruction::I64Load32S(offset)); + + }, + I64Load32U(align, offset) => { + Validator::validate_load(context, align, 4, ValueType::I64)?; + context.sink.emit(isa::Instruction::I64Load32U(offset)); + + }, + + I32Store(align, offset) => { + Validator::validate_store(context, align, 4, ValueType::I32)?; + context.sink.emit(isa::Instruction::I32Store(offset)); + + }, + I64Store(align, offset) => { + Validator::validate_store(context, align, 8, ValueType::I64)?; + context.sink.emit(isa::Instruction::I64Store(offset)); + + }, + F32Store(align, offset) => { + Validator::validate_store(context, align, 4, ValueType::F32)?; + context.sink.emit(isa::Instruction::F32Store(offset)); + + }, + F64Store(align, offset) => { + Validator::validate_store(context, align, 8, ValueType::F64)?; + context.sink.emit(isa::Instruction::F64Store(offset)); + + }, + I32Store8(align, offset) => { + Validator::validate_store(context, align, 1, ValueType::I32)?; + context.sink.emit(isa::Instruction::I32Store8(offset)); + + }, + I32Store16(align, offset) => { + Validator::validate_store(context, align, 2, ValueType::I32)?; + context.sink.emit(isa::Instruction::I32Store16(offset)); + + }, + I64Store8(align, offset) => { + Validator::validate_store(context, align, 1, ValueType::I64)?; + context.sink.emit(isa::Instruction::I64Store8(offset)); + + }, + I64Store16(align, offset) => { + Validator::validate_store(context, align, 2, ValueType::I64)?; + context.sink.emit(isa::Instruction::I64Store16(offset)); + + }, + I64Store32(align, offset) => { + Validator::validate_store(context, align, 4, ValueType::I64)?; + context.sink.emit(isa::Instruction::I64Store32(offset)); + + }, + + CurrentMemory(_) => { + Validator::validate_current_memory(context)?; + context.sink.emit(isa::Instruction::CurrentMemory); + + }, + GrowMemory(_) => { + Validator::validate_grow_memory(context)?; + context.sink.emit(isa::Instruction::GrowMemory); + + }, + + I32Const(v) => { + Validator::validate_const(context, ValueType::I32)?; + context.sink.emit(isa::Instruction::I32Const(v)); + + }, + I64Const(v) => { + Validator::validate_const(context, ValueType::I64)?; + context.sink.emit(isa::Instruction::I64Const(v)); + + }, + F32Const(v) => { + Validator::validate_const(context, ValueType::F32)?; + context.sink.emit(isa::Instruction::F32Const(v)); + + }, + F64Const(v) => { + Validator::validate_const(context, ValueType::F64)?; + context.sink.emit(isa::Instruction::F64Const(v)); + + }, + + I32Eqz => { + Validator::validate_testop(context, ValueType::I32)?; + context.sink.emit(isa::Instruction::I32Eqz); + + }, + I32Eq => { + Validator::validate_relop(context, ValueType::I32)?; + context.sink.emit(isa::Instruction::I32Eq); + + }, + I32Ne => { + Validator::validate_relop(context, ValueType::I32)?; + context.sink.emit(isa::Instruction::I32Ne); + + }, + I32LtS => { + Validator::validate_relop(context, ValueType::I32)?; + context.sink.emit(isa::Instruction::I32LtS); + + }, + I32LtU => { + Validator::validate_relop(context, ValueType::I32)?; + context.sink.emit(isa::Instruction::I32LtU); + + }, + I32GtS => { + Validator::validate_relop(context, ValueType::I32)?; + context.sink.emit(isa::Instruction::I32GtS); + + }, + I32GtU => { + Validator::validate_relop(context, ValueType::I32)?; + context.sink.emit(isa::Instruction::I32GtU); + + }, + I32LeS => { + Validator::validate_relop(context, ValueType::I32)?; + context.sink.emit(isa::Instruction::I32LeS); + + }, + I32LeU => { + Validator::validate_relop(context, ValueType::I32)?; + context.sink.emit(isa::Instruction::I32LeU); + + }, + I32GeS => { + Validator::validate_relop(context, ValueType::I32)?; + context.sink.emit(isa::Instruction::I32GeS); + + }, + I32GeU => { + Validator::validate_relop(context, ValueType::I32)?; + context.sink.emit(isa::Instruction::I32GeU); + + }, + + I64Eqz => { + Validator::validate_testop(context, ValueType::I64)?; + context.sink.emit(isa::Instruction::I64Eqz); + + }, + I64Eq => { + Validator::validate_relop(context, ValueType::I64)?; + context.sink.emit(isa::Instruction::I64Eq); + + }, + I64Ne => { + Validator::validate_relop(context, ValueType::I64)?; + context.sink.emit(isa::Instruction::I64Ne); + + }, + I64LtS => { + Validator::validate_relop(context, ValueType::I64)?; + context.sink.emit(isa::Instruction::I64LtS); + + }, + I64LtU => { + Validator::validate_relop(context, ValueType::I64)?; + context.sink.emit(isa::Instruction::I64LtU); + + }, + I64GtS => { + Validator::validate_relop(context, ValueType::I64)?; + context.sink.emit(isa::Instruction::I64GtS); + + }, + I64GtU => { + Validator::validate_relop(context, ValueType::I64)?; + context.sink.emit(isa::Instruction::I64GtU); + + }, + I64LeS => { + Validator::validate_relop(context, ValueType::I64)?; + context.sink.emit(isa::Instruction::I64LeS); + + }, + I64LeU => { + Validator::validate_relop(context, ValueType::I64)?; + context.sink.emit(isa::Instruction::I64LeU); + + }, + I64GeS => { + Validator::validate_relop(context, ValueType::I64)?; + context.sink.emit(isa::Instruction::I64GeS); + + }, + I64GeU => { + Validator::validate_relop(context, ValueType::I64)?; + context.sink.emit(isa::Instruction::I64GeU); + + }, + + F32Eq => { + Validator::validate_relop(context, ValueType::F32)?; + context.sink.emit(isa::Instruction::F32Eq); + + }, + F32Ne => { + Validator::validate_relop(context, ValueType::F32)?; + context.sink.emit(isa::Instruction::F32Ne); + + }, + F32Lt => { + Validator::validate_relop(context, ValueType::F32)?; + context.sink.emit(isa::Instruction::F32Lt); + + }, + F32Gt => { + Validator::validate_relop(context, ValueType::F32)?; + context.sink.emit(isa::Instruction::F32Gt); + + }, + F32Le => { + Validator::validate_relop(context, ValueType::F32)?; + context.sink.emit(isa::Instruction::F32Le); + + }, + F32Ge => { + Validator::validate_relop(context, ValueType::F32)?; + context.sink.emit(isa::Instruction::F32Ge); + + }, + + F64Eq => { + Validator::validate_relop(context, ValueType::F64)?; + context.sink.emit(isa::Instruction::F64Eq); + + }, + F64Ne => { + Validator::validate_relop(context, ValueType::F64)?; + context.sink.emit(isa::Instruction::F64Ne); + + }, + F64Lt => { + Validator::validate_relop(context, ValueType::F64)?; + context.sink.emit(isa::Instruction::F64Lt); + + }, + F64Gt => { + Validator::validate_relop(context, ValueType::F64)?; + context.sink.emit(isa::Instruction::F64Gt); + + }, + F64Le => { + Validator::validate_relop(context, ValueType::F64)?; + context.sink.emit(isa::Instruction::F64Le); + + }, + F64Ge => { + Validator::validate_relop(context, ValueType::F64)?; + context.sink.emit(isa::Instruction::F64Ge); + + }, + + I32Clz => { + Validator::validate_unop(context, ValueType::I32)?; + context.sink.emit(isa::Instruction::I32Clz); + + }, + I32Ctz => { + Validator::validate_unop(context, ValueType::I32)?; + context.sink.emit(isa::Instruction::I32Ctz); + + }, + I32Popcnt => { + Validator::validate_unop(context, ValueType::I32)?; + context.sink.emit(isa::Instruction::I32Popcnt); + + }, + I32Add => { + Validator::validate_binop(context, ValueType::I32)?; + context.sink.emit(isa::Instruction::I32Add); + + }, + I32Sub => { + Validator::validate_binop(context, ValueType::I32)?; + context.sink.emit(isa::Instruction::I32Sub); + + }, + I32Mul => { + Validator::validate_binop(context, ValueType::I32)?; + context.sink.emit(isa::Instruction::I32Mul); + + }, + I32DivS => { + Validator::validate_binop(context, ValueType::I32)?; + context.sink.emit(isa::Instruction::I32DivS); + + }, + I32DivU => { + Validator::validate_binop(context, ValueType::I32)?; + context.sink.emit(isa::Instruction::I32DivU); + + }, + I32RemS => { + Validator::validate_binop(context, ValueType::I32)?; + context.sink.emit(isa::Instruction::I32RemS); + + }, + I32RemU => { + Validator::validate_binop(context, ValueType::I32)?; + context.sink.emit(isa::Instruction::I32RemU); + + }, + I32And => { + Validator::validate_binop(context, ValueType::I32)?; + context.sink.emit(isa::Instruction::I32And); + + }, + I32Or => { + Validator::validate_binop(context, ValueType::I32)?; + context.sink.emit(isa::Instruction::I32Or); + + }, + I32Xor => { + Validator::validate_binop(context, ValueType::I32)?; + context.sink.emit(isa::Instruction::I32Xor); + + }, + I32Shl => { + Validator::validate_binop(context, ValueType::I32)?; + context.sink.emit(isa::Instruction::I32Shl); + + }, + I32ShrS => { + Validator::validate_binop(context, ValueType::I32)?; + context.sink.emit(isa::Instruction::I32ShrS); + + }, + I32ShrU => { + Validator::validate_binop(context, ValueType::I32)?; + context.sink.emit(isa::Instruction::I32ShrU); + + }, + I32Rotl => { + Validator::validate_binop(context, ValueType::I32)?; + context.sink.emit(isa::Instruction::I32Rotl); + + }, + I32Rotr => { + Validator::validate_binop(context, ValueType::I32)?; + context.sink.emit(isa::Instruction::I32Rotr); + + }, + + I64Clz => { + Validator::validate_unop(context, ValueType::I64)?; + context.sink.emit(isa::Instruction::I64Clz); + + }, + I64Ctz => { + Validator::validate_unop(context, ValueType::I64)?; + context.sink.emit(isa::Instruction::I64Ctz); + + }, + I64Popcnt => { + Validator::validate_unop(context, ValueType::I64)?; + context.sink.emit(isa::Instruction::I64Popcnt); + + }, + I64Add => { + Validator::validate_binop(context, ValueType::I64)?; + context.sink.emit(isa::Instruction::I64Add); + + }, + I64Sub => { + Validator::validate_binop(context, ValueType::I64)?; + context.sink.emit(isa::Instruction::I64Sub); + + }, + I64Mul => { + Validator::validate_binop(context, ValueType::I64)?; + context.sink.emit(isa::Instruction::I64Mul); + + }, + I64DivS => { + Validator::validate_binop(context, ValueType::I64)?; + context.sink.emit(isa::Instruction::I64DivS); + + }, + I64DivU => { + Validator::validate_binop(context, ValueType::I64)?; + context.sink.emit(isa::Instruction::I64DivU); + + }, + I64RemS => { + Validator::validate_binop(context, ValueType::I64)?; + context.sink.emit(isa::Instruction::I64RemS); + + }, + I64RemU => { + Validator::validate_binop(context, ValueType::I64)?; + context.sink.emit(isa::Instruction::I64RemU); + + }, + I64And => { + Validator::validate_binop(context, ValueType::I64)?; + context.sink.emit(isa::Instruction::I64And); + + }, + I64Or => { + Validator::validate_binop(context, ValueType::I64)?; + context.sink.emit(isa::Instruction::I64Or); + + }, + I64Xor => { + Validator::validate_binop(context, ValueType::I64)?; + context.sink.emit(isa::Instruction::I64Xor); + + }, + I64Shl => { + Validator::validate_binop(context, ValueType::I64)?; + context.sink.emit(isa::Instruction::I64Shl); + + }, + I64ShrS => { + Validator::validate_binop(context, ValueType::I64)?; + context.sink.emit(isa::Instruction::I64ShrS); + + }, + I64ShrU => { + Validator::validate_binop(context, ValueType::I64)?; + context.sink.emit(isa::Instruction::I64ShrU); + + }, + I64Rotl => { + Validator::validate_binop(context, ValueType::I64)?; + context.sink.emit(isa::Instruction::I64Rotl); + + }, + I64Rotr => { + Validator::validate_binop(context, ValueType::I64)?; + context.sink.emit(isa::Instruction::I64Rotr); + + }, + + F32Abs => { + Validator::validate_unop(context, ValueType::F32)?; + context.sink.emit(isa::Instruction::F32Abs); + + }, + F32Neg => { + Validator::validate_unop(context, ValueType::F32)?; + context.sink.emit(isa::Instruction::F32Neg); + + }, + F32Ceil => { + Validator::validate_unop(context, ValueType::F32)?; + context.sink.emit(isa::Instruction::F32Ceil); + + }, + F32Floor => { + Validator::validate_unop(context, ValueType::F32)?; + context.sink.emit(isa::Instruction::F32Floor); + + }, + F32Trunc => { + Validator::validate_unop(context, ValueType::F32)?; + context.sink.emit(isa::Instruction::F32Trunc); + + }, + F32Nearest => { + Validator::validate_unop(context, ValueType::F32)?; + context.sink.emit(isa::Instruction::F32Nearest); + + }, + F32Sqrt => { + Validator::validate_unop(context, ValueType::F32)?; + context.sink.emit(isa::Instruction::F32Sqrt); + + }, + F32Add => { + Validator::validate_binop(context, ValueType::F32)?; + context.sink.emit(isa::Instruction::F32Add); + + }, + F32Sub => { + Validator::validate_binop(context, ValueType::F32)?; + context.sink.emit(isa::Instruction::F32Sub); + + }, + F32Mul => { + Validator::validate_binop(context, ValueType::F32)?; + context.sink.emit(isa::Instruction::F32Mul); + + }, + F32Div => { + Validator::validate_binop(context, ValueType::F32)?; + context.sink.emit(isa::Instruction::F32Div); + + }, + F32Min => { + Validator::validate_binop(context, ValueType::F32)?; + context.sink.emit(isa::Instruction::F32Min); + + }, + F32Max => { + Validator::validate_binop(context, ValueType::F32)?; + context.sink.emit(isa::Instruction::F32Max); + + }, + F32Copysign => { + Validator::validate_binop(context, ValueType::F32)?; + context.sink.emit(isa::Instruction::F32Copysign); + + }, + + F64Abs => { + Validator::validate_unop(context, ValueType::F64)?; + context.sink.emit(isa::Instruction::F64Abs); + + }, + F64Neg => { + Validator::validate_unop(context, ValueType::F64)?; + context.sink.emit(isa::Instruction::F64Neg); + + }, + F64Ceil => { + Validator::validate_unop(context, ValueType::F64)?; + context.sink.emit(isa::Instruction::F64Ceil); + + }, + F64Floor => { + Validator::validate_unop(context, ValueType::F64)?; + context.sink.emit(isa::Instruction::F64Floor); + + }, + F64Trunc => { + Validator::validate_unop(context, ValueType::F64)?; + context.sink.emit(isa::Instruction::F64Trunc); + + }, + F64Nearest => { + Validator::validate_unop(context, ValueType::F64)?; + context.sink.emit(isa::Instruction::F64Nearest); + + }, + F64Sqrt => { + Validator::validate_unop(context, ValueType::F64)?; + context.sink.emit(isa::Instruction::F64Sqrt); + + }, + F64Add => { + Validator::validate_binop(context, ValueType::F64)?; + context.sink.emit(isa::Instruction::F64Add); + + }, + F64Sub => { + Validator::validate_binop(context, ValueType::F64)?; + context.sink.emit(isa::Instruction::F64Sub); + + }, + F64Mul => { + Validator::validate_binop(context, ValueType::F64)?; + context.sink.emit(isa::Instruction::F64Mul); + + }, + F64Div => { + Validator::validate_binop(context, ValueType::F64)?; + context.sink.emit(isa::Instruction::F64Div); + + }, + F64Min => { + Validator::validate_binop(context, ValueType::F64)?; + context.sink.emit(isa::Instruction::F64Min); + + }, + F64Max => { + Validator::validate_binop(context, ValueType::F64)?; + context.sink.emit(isa::Instruction::F64Max); + + }, + F64Copysign => { + Validator::validate_binop(context, ValueType::F64)?; + context.sink.emit(isa::Instruction::F64Copysign); + + }, + + I32WrapI64 => { + Validator::validate_cvtop(context, ValueType::I64, ValueType::I32)?; + context.sink.emit(isa::Instruction::I32WrapI64); + + }, + I32TruncSF32 => { + Validator::validate_cvtop(context, ValueType::F32, ValueType::I32)?; + context.sink.emit(isa::Instruction::I32TruncSF32); + + }, + I32TruncUF32 => { + Validator::validate_cvtop(context, ValueType::F32, ValueType::I32)?; + context.sink.emit(isa::Instruction::I32TruncUF32); + + }, + I32TruncSF64 => { + Validator::validate_cvtop(context, ValueType::F64, ValueType::I32)?; + context.sink.emit(isa::Instruction::I32TruncSF64); + + }, + I32TruncUF64 => { + Validator::validate_cvtop(context, ValueType::F64, ValueType::I32)?; + context.sink.emit(isa::Instruction::I32TruncUF64); + + }, + I64ExtendSI32 => { + Validator::validate_cvtop(context, ValueType::I32, ValueType::I64)?; + context.sink.emit(isa::Instruction::I64ExtendSI32); + + }, + I64ExtendUI32 => { + Validator::validate_cvtop(context, ValueType::I32, ValueType::I64)?; + context.sink.emit(isa::Instruction::I64ExtendUI32); + + }, + I64TruncSF32 => { + Validator::validate_cvtop(context, ValueType::F32, ValueType::I64)?; + context.sink.emit(isa::Instruction::I64TruncSF32); + + }, + I64TruncUF32 => { + Validator::validate_cvtop(context, ValueType::F32, ValueType::I64)?; + context.sink.emit(isa::Instruction::I64TruncUF32); + + }, + I64TruncSF64 => { + Validator::validate_cvtop(context, ValueType::F64, ValueType::I64)?; + context.sink.emit(isa::Instruction::I64TruncSF64); + + }, + I64TruncUF64 => { + Validator::validate_cvtop(context, ValueType::F64, ValueType::I64)?; + context.sink.emit(isa::Instruction::I64TruncUF64); + + }, + F32ConvertSI32 => { + Validator::validate_cvtop(context, ValueType::I32, ValueType::F32)?; + context.sink.emit(isa::Instruction::F32ConvertSI32); + + }, + F32ConvertUI32 => { + Validator::validate_cvtop(context, ValueType::I32, ValueType::F32)?; + context.sink.emit(isa::Instruction::F32ConvertUI32); + + }, + F32ConvertSI64 => { + Validator::validate_cvtop(context, ValueType::I64, ValueType::F32)?; + context.sink.emit(isa::Instruction::F32ConvertSI64); + + }, + F32ConvertUI64 => { + Validator::validate_cvtop(context, ValueType::I64, ValueType::F32)?; + context.sink.emit(isa::Instruction::F32ConvertUI64); + + }, + F32DemoteF64 => { + Validator::validate_cvtop(context, ValueType::F64, ValueType::F32)?; + context.sink.emit(isa::Instruction::F32DemoteF64); + + }, + F64ConvertSI32 => { + Validator::validate_cvtop(context, ValueType::I32, ValueType::F64)?; + context.sink.emit(isa::Instruction::F64ConvertSI32); + + }, + F64ConvertUI32 => { + Validator::validate_cvtop(context, ValueType::I32, ValueType::F64)?; + context.sink.emit(isa::Instruction::F64ConvertUI32); + + }, + F64ConvertSI64 => { + Validator::validate_cvtop(context, ValueType::I64, ValueType::F64)?; + context.sink.emit(isa::Instruction::F64ConvertSI64); + + }, + F64ConvertUI64 => { + Validator::validate_cvtop(context, ValueType::I64, ValueType::F64)?; + context.sink.emit(isa::Instruction::F64ConvertUI64); + + }, + F64PromoteF32 => { + Validator::validate_cvtop(context, ValueType::F32, ValueType::F64)?; + context.sink.emit(isa::Instruction::F64PromoteF32); + + }, + + I32ReinterpretF32 => { + Validator::validate_cvtop(context, ValueType::F32, ValueType::I32)?; + context.sink.emit(isa::Instruction::I32ReinterpretF32); + + }, + I64ReinterpretF64 => { + Validator::validate_cvtop(context, ValueType::F64, ValueType::I64)?; + context.sink.emit(isa::Instruction::I64ReinterpretF64); + + }, + F32ReinterpretI32 => { + Validator::validate_cvtop(context, ValueType::I32, ValueType::F32)?; + context.sink.emit(isa::Instruction::F32ReinterpretI32); + + }, + F64ReinterpretI64 => { + Validator::validate_cvtop(context, ValueType::I64, ValueType::F64)?; + context.sink.emit(isa::Instruction::F64ReinterpretI64); + + }, } + + Ok(InstructionOutcome::ValidateNextInstruction) } fn validate_const(context: &mut FunctionValidationContext, value_type: ValueType) -> Result { @@ -408,48 +1190,6 @@ impl Validator { Ok(InstructionOutcome::ValidateNextInstruction) } - fn validate_block(context: &mut FunctionValidationContext, block_type: BlockType) -> Result { - context.push_label(BlockFrameType::Block, block_type).map(|_| InstructionOutcome::ValidateNextInstruction) - } - - fn validate_loop(context: &mut FunctionValidationContext, block_type: BlockType) -> Result { - context.push_label(BlockFrameType::Loop, block_type).map(|_| InstructionOutcome::ValidateNextInstruction) - } - - fn validate_if(context: &mut FunctionValidationContext, block_type: BlockType) -> Result { - context.pop_value(ValueType::I32.into())?; - context.push_label(BlockFrameType::IfTrue, block_type).map(|_| InstructionOutcome::ValidateNextInstruction) - } - - fn validate_else(context: &mut FunctionValidationContext) -> Result { - let block_type = { - let top_frame = context.top_label()?; - if top_frame.frame_type != BlockFrameType::IfTrue { - return Err(Error("Misplaced else instruction".into())); - } - top_frame.block_type - }; - context.pop_label()?; - - if let BlockType::Value(value_type) = block_type { - context.pop_value(value_type.into())?; - } - context.push_label(BlockFrameType::IfFalse, block_type).map(|_| InstructionOutcome::ValidateNextInstruction) - } - - fn validate_end(context: &mut FunctionValidationContext) -> Result { - { - let top_frame = context.top_label()?; - if top_frame.frame_type == BlockFrameType::IfTrue { - if top_frame.block_type != BlockType::NoResult { - return Err(Error(format!("If block without else required to have NoResult block type. But it have {:?} type", top_frame.block_type))); - } - } - } - - context.pop_label().map(|_| InstructionOutcome::ValidateNextInstruction) - } - fn validate_br(context: &mut FunctionValidationContext, idx: u32) -> Result { let (frame_type, frame_block_type) = { let frame = context.require_label(idx)?; @@ -587,7 +1327,7 @@ impl<'a> FunctionValidationContext<'a> { value_stack: StackWithLimit::with_limit(value_stack_limit), frame_stack: StackWithLimit::with_limit(frame_stack_limit), return_type: Some(return_type), - labels: HashMap::new(), + sink: Sink::new(), } } @@ -645,13 +1385,12 @@ impl<'a> FunctionValidationContext<'a> { Ok(self.frame_stack.top()?) } - fn push_label(&mut self, frame_type: BlockFrameType, block_type: BlockType) -> Result<(), Error> { + fn push_label(&mut self, frame_type: BlockFrameType, block_type: BlockType, branch_label: LabelId) -> Result<(), Error> { Ok(self.frame_stack.push(BlockFrame { frame_type: frame_type, block_type: block_type, begin_position: self.position, - branch_position: self.position, - end_position: self.position, + branch_label, value_stack_len: self.value_stack.len(), polymorphic_stack: false, })?) @@ -678,9 +1417,6 @@ impl<'a> FunctionValidationContext<'a> { ))); } - if !self.frame_stack.is_empty() { - self.labels.insert(frame.begin_position, self.position); - } if let BlockType::Value(value_type) = frame.block_type { self.push_value(value_type.into())?; } @@ -700,8 +1436,44 @@ impl<'a> FunctionValidationContext<'a> { Ok(self.locals.type_of_local(idx).map(StackValueType::from)?) } - fn into_labels(self) -> HashMap { - self.labels + fn require_target(&self, depth: u32) -> Result { + let frame = self.require_label(depth)?; + let label = frame.branch_label; + + let keep: u8 = match (frame.frame_type, frame.block_type) { + (BlockFrameType::Loop, _) => 0, + (_, BlockType::NoResult) => 0, + (_, BlockType::Value(_)) => 1, + }; + + let value_stack_height = self.value_stack.len() as u32; + let drop = value_stack_height - frame.value_stack_len as u32 - keep as u32; + + Ok(Target { + label, + keep, + drop, + }) + } + + fn relative_local_depth(&mut self, idx: u32) -> Result { + // TODO: Comment stack layout + let value_stack_height = self.value_stack.len() as u32; + let locals_and_params_count = self.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 no in 32-bit range")) + )?; + Ok(depth) + } + + fn into_code(self) -> isa::Instructions { + isa::Instructions { + code: self.sink.into_inner(), + } } } @@ -752,3 +1524,169 @@ impl PartialEq for ValueType { other == self } } + +#[derive(Clone)] +struct Target { + label: LabelId, + drop: u32, + keep: u8, +} + +enum Reloc { + Br { + pc: u32, + }, + BrTable { + pc: u32, + idx: usize, + }, +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +struct LabelId(usize); +enum Label { + Resolved(u32), + NotResolved, +} + +struct Sink { + ins: Vec, + labels: Vec