From fc3d21a17aabbfa20eef5c4ea6ad937a71a811e8 Mon Sep 17 00:00:00 2001 From: Sergey Pepyakin Date: Mon, 8 Apr 2019 15:41:44 +0200 Subject: [PATCH] Express the compiler using validation trait --- src/validation/func.rs | 124 ++++++-------------- src/validation/mod.rs | 253 +++++++---------------------------------- 2 files changed, 73 insertions(+), 304 deletions(-) diff --git a/src/validation/func.rs b/src/validation/func.rs index b31471b..b5d2882 100644 --- a/src/validation/func.rs +++ b/src/validation/func.rs @@ -24,8 +24,6 @@ struct BlockFrame { /// 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, /// 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, @@ -156,11 +154,15 @@ pub fn drive( module: &ModuleContext, func: &Func, body: &FuncBody, - validator: T, ) -> Result { let (params, result_ty) = module.require_function_type(func.type_ref())?; - let ins_size_estimate = body.code().elements().len(); + let code = body.code().elements(); + let code_len = code.len(); + if code_len == 0 { + return Err(Error("Non-empty function body expected".into())); + } + let mut context = FunctionValidationContext::new( &module, Locals::new(params, body.locals())?, @@ -169,110 +171,62 @@ pub fn drive( result_ty, ); - let mut compiler = Compiler { - sink: Sink::with_instruction_capacity(ins_size_estimate), - label_stack: Vec::new(), - }; - let end_label = compiler.sink.new_label(); - compiler - .label_stack - .push(BlockFrameType::Block { end_label }); + let mut validator = T::new(&context); - assert!(context.frame_stack.is_empty()); - - let body = body.code().elements(); - let body_len = body.len(); - if body_len == 0 { - return Err(Error("Non-empty function body expected".into())); - } - - loop { - let instruction = &body[context.position]; - - compiler - .compile_instruction(&mut context, instruction) + for (position, instruction) in code.iter().enumerate() { + validator + .next_instruction(&mut context, instruction) .map_err(|err| { Error(format!( "At instruction {:?}(@{}): {}", - instruction, context.position, err + instruction, position, err )) })?; - - context.position += 1; - if context.position == body_len { - break; - } } + // The last `end` opcode should pop last instruction. + // TODO: This looks like it should be returned as an error? + assert!(context.frame_stack.is_empty()); + Ok(validator.finish()) } +// TODO: Move under prepare pub struct Compiler { /// A sink used to emit optimized code. sink: Sink, label_stack: Vec, } -impl Compiler { - pub fn compile( - module: &ModuleContext, - func: &Func, - body: &FuncBody, - ) -> Result { - let (params, result_ty) = module.require_function_type(func.type_ref())?; - - let ins_size_estimate = body.code().elements().len(); - let mut context = FunctionValidationContext::new( - &module, - Locals::new(params, body.locals())?, - DEFAULT_VALUE_STACK_LIMIT, - DEFAULT_FRAME_STACK_LIMIT, - result_ty, - ); - +impl FunctionValidator for Compiler { + type Output = isa::Instructions; + fn new(_module: &FunctionValidationContext) -> Self { let mut compiler = Compiler { - sink: Sink::with_instruction_capacity(ins_size_estimate), + 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.compile_function_body(&mut context, body.code().elements())?; - assert!(context.frame_stack.is_empty()); - - Ok(compiler.sink.into_inner()) + compiler } - - fn compile_function_body( + fn next_instruction( &mut self, - context: &mut FunctionValidationContext, - body: &[Instruction], + ctx: &mut FunctionValidationContext, + instruction: &Instruction, ) -> Result<(), Error> { - let body_len = body.len(); - if body_len == 0 { - return Err(Error("Non-empty function body expected".into())); - } - - loop { - let instruction = &body[context.position]; - - self.compile_instruction(context, instruction) - .map_err(|err| { - Error(format!( - "At instruction {:?}(@{}): {}", - instruction, context.position, err - )) - })?; - - context.position += 1; - if context.position == body_len { - return Ok(()); - } - } + self.compile_instruction(ctx, instruction) } + fn finish(self) -> Self::Output { + self.sink.into_inner() + } +} +impl Compiler { fn compile_instruction( &mut self, context: &mut FunctionValidationContext, @@ -1139,11 +1093,9 @@ impl Compiler { } /// Function validation context. -struct FunctionValidationContext<'a> { +pub struct FunctionValidationContext<'a> { /// Wasm module module: &'a ModuleContext, - /// Current instruction position. - position: usize, /// Local variables. locals: Locals<'a>, /// Value stack. @@ -1164,7 +1116,6 @@ impl<'a> FunctionValidationContext<'a> { ) -> Self { let mut ctx = FunctionValidationContext { module: module, - position: 0, locals: locals, value_stack: StackWithLimit::with_limit(value_stack_limit), frame_stack: StackWithLimit::with_limit(frame_stack_limit), @@ -1176,7 +1127,6 @@ impl<'a> FunctionValidationContext<'a> { let _ = push_label( StartedWith::Block, return_type, - ctx.position, &ctx.value_stack, &mut ctx.frame_stack, ); @@ -1187,7 +1137,7 @@ impl<'a> FunctionValidationContext<'a> { self.return_type } - fn step(&mut self, instruction: &Instruction) -> Result<(), Error> { + pub fn step(&mut self, instruction: &Instruction) -> Result<(), Error> { use self::Instruction::*; match *instruction { @@ -1202,7 +1152,6 @@ impl<'a> FunctionValidationContext<'a> { push_label( StartedWith::Block, block_type, - self.position, &self.value_stack, &mut self.frame_stack, )?; @@ -1211,7 +1160,6 @@ impl<'a> FunctionValidationContext<'a> { push_label( StartedWith::Loop, block_type, - self.position, &self.value_stack, &mut self.frame_stack, )?; @@ -1225,7 +1173,6 @@ impl<'a> FunctionValidationContext<'a> { push_label( StartedWith::If, block_type, - self.position, &self.value_stack, &mut self.frame_stack, )?; @@ -1245,7 +1192,6 @@ impl<'a> FunctionValidationContext<'a> { push_label( StartedWith::Else, block_type, - self.position, &self.value_stack, &mut self.frame_stack, )?; @@ -2173,14 +2119,12 @@ fn tee_value( fn push_label( started_with: StartedWith, block_type: BlockType, - position: usize, value_stack: &StackWithLimit, frame_stack: &mut StackWithLimit, ) -> Result<(), Error> { Ok(frame_stack.push(BlockFrame { started_with, block_type: block_type, - begin_position: position, value_stack_len: value_stack.len(), polymorphic_stack: false, })?) diff --git a/src/validation/mod.rs b/src/validation/mod.rs index fc26fa7..ba2429c 100644 --- a/src/validation/mod.rs +++ b/src/validation/mod.rs @@ -10,7 +10,6 @@ use hashbrown::HashSet; use std::collections::HashSet; use self::context::ModuleContextBuilder; -use self::func::Compiler; use common::stack; use isa; use memory_units::Pages; @@ -179,22 +178,52 @@ pub trait Validation { type Output; type FunctionValidator: FunctionValidator; fn new(module: &Module) -> Self; - fn create_function_validator(&mut self) -> Self::FunctionValidator; - fn on_function_validated(&mut self, index: u32, - output: <::FunctionValidator as FunctionValidator>::Output - ); + fn on_function_validated( + &mut self, + index: u32, + output: <::FunctionValidator as FunctionValidator>::Output, + ); fn finish(self) -> Self::Output; } pub trait FunctionValidator { type Output; - fn new() -> Self; - fn next_instruction(&mut self, instruction: &Instruction) -> Result<(), ()>; + fn new(ctx: &func::FunctionValidationContext) -> Self; + fn next_instruction( + &mut self, + ctx: &mut func::FunctionValidationContext, + instruction: &Instruction, + ) -> Result<(), Error>; 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: &mut Module) -> Result { +pub fn validate_module2(module: &Module) -> Result { let mut context_builder = ModuleContextBuilder::new(); let mut imported_globals = Vec::new(); let mut validation = V::new(&module); @@ -285,8 +314,7 @@ pub fn validate_module2(module: &mut Module) -> Result(&context, function, function_body).map_err( |Error(ref msg)| { Error(format!( "Function #{} reading/validation error: {}", @@ -406,210 +434,7 @@ pub fn validate_module2(module: &mut Module) -> Result Result { - let mut context_builder = ModuleContextBuilder::new(); - let mut imported_globals = Vec::new(); - let mut code_map = Vec::new(); - - // Copy types from module as is. - context_builder.set_types( - module - .type_section() - .map(|ts| { - ts.types() - .into_iter() - .map(|&Type::Function(ref ty)| ty) - .cloned() - .collect() - }) - .unwrap_or_default(), - ); - - // Fill elements with imported values. - for import_entry in module - .import_section() - .map(|i| i.entries()) - .unwrap_or_default() - { - match *import_entry.external() { - External::Function(idx) => context_builder.push_func_type_index(idx), - External::Table(ref table) => context_builder.push_table(table.clone()), - External::Memory(ref memory) => context_builder.push_memory(memory.clone()), - External::Global(ref global) => { - context_builder.push_global(global.clone()); - imported_globals.push(global.clone()); - } - } - } - - // Concatenate elements with defined in the module. - if let Some(function_section) = module.function_section() { - for func_entry in function_section.entries() { - context_builder.push_func_type_index(func_entry.type_ref()) - } - } - if let Some(table_section) = module.table_section() { - for table_entry in table_section.entries() { - validate_table_type(table_entry)?; - context_builder.push_table(table_entry.clone()); - } - } - if let Some(mem_section) = module.memory_section() { - for mem_entry in mem_section.entries() { - validate_memory_type(mem_entry)?; - context_builder.push_memory(mem_entry.clone()); - } - } - if let Some(global_section) = module.global_section() { - for global_entry in global_section.entries() { - validate_global_entry(global_entry, &imported_globals)?; - context_builder.push_global(global_entry.global_type().clone()); - } - } - - let context = context_builder.build(); - - let function_section_len = module - .function_section() - .map(|s| s.entries().len()) - .unwrap_or(0); - let code_section_len = module.code_section().map(|s| s.bodies().len()).unwrap_or(0); - if function_section_len != code_section_len { - return Err(Error(format!( - "length of function section is {}, while len of code section is {}", - function_section_len, code_section_len - ))); - } - - // validate every function body in user modules - if function_section_len != 0 { - // tests use invalid code - let function_section = module - .function_section() - .expect("function_section_len != 0; qed"); - let code_section = module - .code_section() - .expect("function_section_len != 0; function_section_len == code_section_len; qed"); - // check every function body - for (index, function) in function_section.entries().iter().enumerate() { - let function_body = code_section - .bodies() - .get(index as usize) - .ok_or(Error(format!("Missing body for function {}", index)))?; - let code = Compiler::compile(&context, function, function_body).map_err(|e| { - let Error(ref msg) = e; - Error(format!( - "Function #{} reading/validation error: {}", - index, msg - )) - })?; - code_map.push(code); - } - } - - // validate start section - if let Some(start_fn_idx) = module.start_section() { - let (params, return_ty) = context.require_function(start_fn_idx)?; - if return_ty != BlockType::NoResult || params.len() != 0 { - return Err(Error( - "start function expected to have type [] -> []".into(), - )); - } - } - - // validate export section - if let Some(export_section) = module.export_section() { - let mut export_names = HashSet::with_capacity(export_section.entries().len()); - for export in export_section.entries() { - // HashSet::insert returns false if item already in set. - let duplicate = export_names.insert(export.field()) == false; - if duplicate { - return Err(Error(format!("duplicate export {}", export.field()))); - } - match *export.internal() { - Internal::Function(function_index) => { - context.require_function(function_index)?; - } - Internal::Global(global_index) => { - context.require_global(global_index, Some(false))?; - } - Internal::Memory(memory_index) => { - context.require_memory(memory_index)?; - } - Internal::Table(table_index) => { - context.require_table(table_index)?; - } - } - } - } - - // validate import section - if let Some(import_section) = module.import_section() { - for import in import_section.entries() { - match *import.external() { - External::Function(function_type_index) => { - context.require_function_type(function_type_index)?; - } - External::Global(ref global_type) => { - if global_type.is_mutable() { - return Err(Error(format!( - "trying to import mutable global {}", - import.field() - ))); - } - } - External::Memory(ref memory_type) => { - validate_memory_type(memory_type)?; - } - External::Table(ref table_type) => { - validate_table_type(table_type)?; - } - } - } - } - - // there must be no greater than 1 table in tables index space - if context.tables().len() > 1 { - return Err(Error(format!( - "too many tables in index space: {}", - context.tables().len() - ))); - } - - // there must be no greater than 1 linear memory in memory index space - if context.memories().len() > 1 { - return Err(Error(format!( - "too many memory regions in index space: {}", - context.memories().len() - ))); - } - - // use data section to initialize linear memory regions - if let Some(data_section) = module.data_section() { - for data_segment in data_section.entries() { - context.require_memory(data_segment.index())?; - let init_ty = expr_const_type(data_segment.offset(), context.globals())?; - if init_ty != ValueType::I32 { - return Err(Error("segment offset should return I32".into())); - } - } - } - - // use element section to fill tables - if let Some(element_section) = module.elements_section() { - for element_segment in element_section.entries() { - context.require_table(element_segment.index())?; - - let init_ty = expr_const_type(element_segment.offset(), context.globals())?; - if init_ty != ValueType::I32 { - return Err(Error("segment offset should return I32".into())); - } - - for function_index in element_segment.members() { - context.require_function(*function_index)?; - } - } - } - + let code_map = validate_module2::(&module)?; Ok(ValidatedModule { module, code_map }) }