From f2d99ce8883aea7ba9a0a5c66607d058095c551a Mon Sep 17 00:00:00 2001 From: NikVolf Date: Fri, 19 May 2017 16:54:53 +0300 Subject: [PATCH] tabify all --- ext/src/main.rs | 22 +- gas/src/main.rs | 20 +- opt/src/main.rs | 24 +- rust-runner/src/alloc.rs | 26 +- rust-runner/src/call_args.rs | 76 +-- rust-runner/src/main.rs | 118 ++--- samples/empty_contract.rs | 22 +- src/ext.rs | 162 +++--- src/gas.rs | 160 +++--- src/optimizer.rs | 924 +++++++++++++++++------------------ src/symbols.rs | 254 +++++----- 11 files changed, 904 insertions(+), 904 deletions(-) diff --git a/ext/src/main.rs b/ext/src/main.rs index 3c74790..99c7e22 100644 --- a/ext/src/main.rs +++ b/ext/src/main.rs @@ -5,18 +5,18 @@ use std::env; fn main() { - wasm_utils::init_log(); + wasm_utils::init_log(); - let args = env::args().collect::>(); - if args.len() != 3 { - println!("Usage: {} input_file.wasm output_file.wasm", args[0]); - return; - } + let args = env::args().collect::>(); + if args.len() != 3 { + println!("Usage: {} input_file.wasm output_file.wasm", args[0]); + return; + } - let module = wasm_utils::externalize( - parity_wasm::deserialize_file(&args[1]).expect("Module to deserialize ok"), - vec!["_free", "_malloc"], - ); + let module = wasm_utils::externalize( + parity_wasm::deserialize_file(&args[1]).expect("Module to deserialize ok"), + vec!["_free", "_malloc"], + ); - parity_wasm::serialize_to_file(&args[2], module).expect("Module to serialize ok"); + parity_wasm::serialize_to_file(&args[2], module).expect("Module to serialize ok"); } diff --git a/gas/src/main.rs b/gas/src/main.rs index 27371da..d1cd5e2 100644 --- a/gas/src/main.rs +++ b/gas/src/main.rs @@ -5,18 +5,18 @@ use std::env; fn main() { - wasm_utils::init_log(); + wasm_utils::init_log(); - let args = env::args().collect::>(); - if args.len() != 3 { - println!("Usage: {} input_file.wasm output_file.wasm", args[0]); - return; - } + let args = env::args().collect::>(); + if args.len() != 3 { + println!("Usage: {} input_file.wasm output_file.wasm", args[0]); + return; + } - // Loading module - let module = parity_wasm::deserialize_file(&args[1]).expect("Module deserialization to succeed"); + // Loading module + let module = parity_wasm::deserialize_file(&args[1]).expect("Module deserialization to succeed"); - let result = wasm_utils::inject_gas_counter(module); + let result = wasm_utils::inject_gas_counter(module); - parity_wasm::serialize_to_file(&args[2], result).expect("Module serialization to succeed") + parity_wasm::serialize_to_file(&args[2], result).expect("Module serialization to succeed") } diff --git a/opt/src/main.rs b/opt/src/main.rs index 4b96420..bc11ef2 100644 --- a/opt/src/main.rs +++ b/opt/src/main.rs @@ -5,20 +5,20 @@ use std::env; fn main() { - wasm_utils::init_log(); + wasm_utils::init_log(); - let args = env::args().collect::>(); - if args.len() < 3 { - println!("Usage: {} input_file.wasm output_file.wasm", args[0]); - return; - } + let args = env::args().collect::>(); + if args.len() < 3 { + println!("Usage: {} input_file.wasm output_file.wasm", args[0]); + return; + } - let mut module = parity_wasm::deserialize_file(&args[1]).unwrap(); + let mut module = parity_wasm::deserialize_file(&args[1]).unwrap(); - // Invoke optimizer - // Contract is supposed to have only these functions as public api - // All other symbols not usable by this list is optimized away - wasm_utils::optimize(&mut module, vec!["_call"]).expect("Optimizer to finish without errors"); + // Invoke optimizer + // Contract is supposed to have only these functions as public api + // All other symbols not usable by this list is optimized away + wasm_utils::optimize(&mut module, vec!["_call"]).expect("Optimizer to finish without errors"); - parity_wasm::serialize_to_file(&args[2], module).unwrap(); + parity_wasm::serialize_to_file(&args[2], module).unwrap(); } diff --git a/rust-runner/src/alloc.rs b/rust-runner/src/alloc.rs index 63c0eff..095e179 100644 --- a/rust-runner/src/alloc.rs +++ b/rust-runner/src/alloc.rs @@ -2,26 +2,26 @@ use parity_wasm::interpreter::{self, ModuleInstance}; use runtime::Runtime; pub struct Arena { - pub runtime: Runtime, + pub runtime: Runtime, } #[derive(Debug)] pub struct Error; impl Arena { - pub fn alloc(&self, size: u32) -> Result { - // todo: maybe use unsafe cell since it has nothing to do with threads - let previous_top = self.runtime.env().dynamic_top.get(); - self.runtime.env().dynamic_top.set(previous_top + size); - Ok(previous_top) - } + pub fn alloc(&self, size: u32) -> Result { + // todo: maybe use unsafe cell since it has nothing to do with threads + let previous_top = self.runtime.env().dynamic_top.get(); + self.runtime.env().dynamic_top.set(previous_top + size); + Ok(previous_top) + } } impl interpreter::UserFunctionInterface for Arena { - fn call(&mut self, _module: &ModuleInstance, context: interpreter::CallerContext) -> Result, interpreter::Error> { - let amount = context.value_stack.pop_as::()?; - self.alloc(amount as u32) - .map(|val| Some((val as i32).into())) - .map_err(|e| interpreter::Error::Trap(format!("Allocator failure: {}", "todo: format arg"))) - } + fn call(&mut self, _module: &ModuleInstance, context: interpreter::CallerContext) -> Result, interpreter::Error> { + let amount = context.value_stack.pop_as::()?; + self.alloc(amount as u32) + .map(|val| Some((val as i32).into())) + .map_err(|e| interpreter::Error::Trap(format!("Allocator failure: {}", "todo: format arg"))) + } } \ No newline at end of file diff --git a/rust-runner/src/call_args.rs b/rust-runner/src/call_args.rs index 2e9bd8d..c36b4e5 100644 --- a/rust-runner/src/call_args.rs +++ b/rust-runner/src/call_args.rs @@ -4,66 +4,66 @@ use runtime; use WasmMemoryPtr; fn write_u32(dst: &mut [u8], val: u32) { - dst[0] = (val & 0x000000ff) as u8; - dst[1] = ((val & 0x0000ff00) >> 8) as u8; - dst[2] = ((val & 0x00ff0000) >> 16) as u8; - dst[3] = ((val & 0xff000000) >> 24) as u8; + dst[0] = (val & 0x000000ff) as u8; + dst[1] = ((val & 0x0000ff00) >> 8) as u8; + dst[2] = ((val & 0x00ff0000) >> 16) as u8; + dst[3] = ((val & 0xff000000) >> 24) as u8; } #[derive(Debug)] pub enum Error { - Allocator(runtime::ErrorAlloc), - Interpreter(interpreter::Error), + Allocator(runtime::ErrorAlloc), + Interpreter(interpreter::Error), } impl From for Error { - fn from(err: runtime::ErrorAlloc) -> Self { - Error::Allocator(err) - } + fn from(err: runtime::ErrorAlloc) -> Self { + Error::Allocator(err) + } } impl From for Error { - fn from(err: interpreter::Error) -> Self { - Error::Interpreter(err) - } + fn from(err: interpreter::Error) -> Self { + Error::Interpreter(err) + } } pub fn init( - memory: &interpreter::MemoryInstance, - runtime: &mut runtime::Runtime, - input: &[u8], + memory: &interpreter::MemoryInstance, + runtime: &mut runtime::Runtime, + input: &[u8], ) -> Result { - let mut input_ptr_slc = [0u8; 4]; - let mut input_length = [0u8; 4]; + let mut input_ptr_slc = [0u8; 4]; + let mut input_length = [0u8; 4]; - let descriptor_ptr = runtime.alloc(16)?; + let descriptor_ptr = runtime.alloc(16)?; - println!("descriptor_ptr: {}", descriptor_ptr); + println!("descriptor_ptr: {}", descriptor_ptr); - if input.len() > 0 { - let input_ptr = runtime.alloc(input.len() as u32)?; - write_u32(&mut input_ptr_slc, input_ptr); - write_u32(&mut input_length, input.len() as u32); - memory.set(input_ptr, input)?; - println!("input_ptr: {}", input_ptr); - } else { - write_u32(&mut input_ptr_slc, 0); - write_u32(&mut input_length, 0); - } + if input.len() > 0 { + let input_ptr = runtime.alloc(input.len() as u32)?; + write_u32(&mut input_ptr_slc, input_ptr); + write_u32(&mut input_length, input.len() as u32); + memory.set(input_ptr, input)?; + println!("input_ptr: {}", input_ptr); + } else { + write_u32(&mut input_ptr_slc, 0); + write_u32(&mut input_length, 0); + } - memory.set(descriptor_ptr, &input_ptr_slc)?; - memory.set(descriptor_ptr+4, &input_length)?; + memory.set(descriptor_ptr, &input_ptr_slc)?; + memory.set(descriptor_ptr+4, &input_length)?; - // zero result ptr/len - memory.set(descriptor_ptr+8, &[0u8; 4])?; - memory.set(descriptor_ptr+12, &[0u8; 4])?; + // zero result ptr/len + memory.set(descriptor_ptr+8, &[0u8; 4])?; + memory.set(descriptor_ptr+12, &[0u8; 4])?; - println!("descriptor: {:?}", memory.get(descriptor_ptr, 16)); + println!("descriptor: {:?}", memory.get(descriptor_ptr, 16)); - Ok(descriptor_ptr as i32) + Ok(descriptor_ptr as i32) } fn _read_u32(slc: &[u8]) -> u32 { - use std::ops::Shl; - (slc[0] as u32) + (slc[1] as u32).shl(8) + (slc[2] as u32).shl(16) + (slc[3] as u32).shl(24) + use std::ops::Shl; + (slc[0] as u32) + (slc[1] as u32).shl(8) + (slc[2] as u32).shl(16) + (slc[3] as u32).shl(24) } \ No newline at end of file diff --git a/rust-runner/src/main.rs b/rust-runner/src/main.rs index 8693e89..4bb561e 100644 --- a/rust-runner/src/main.rs +++ b/rust-runner/src/main.rs @@ -1,6 +1,6 @@ /* - Rust contract demo runner + Rust contract demo runner */ @@ -19,81 +19,81 @@ pub const DEFAULT_MEMORY_INDEX: interpreter::ItemIndex = interpreter::ItemIndex: pub type WasmMemoryPtr = i32; fn main() { - // First, load wasm contract as a module - wasm_utils::init_log(); + // First, load wasm contract as a module + wasm_utils::init_log(); - let args = env::args().collect::>(); - if args.len() != 2 { - println!("Usage: {} contract.wasm", args[0]); - return; - } + let args = env::args().collect::>(); + if args.len() != 2 { + println!("Usage: {} contract.wasm", args[0]); + return; + } - let module = parity_wasm::deserialize_file(&args[1]).expect("Module deserialization to succeed"); + let module = parity_wasm::deserialize_file(&args[1]).expect("Module deserialization to succeed"); - let program = parity_wasm::interpreter::ProgramInstance::new() - .expect("Program instance to be created"); + let program = parity_wasm::interpreter::ProgramInstance::new() + .expect("Program instance to be created"); - // Add module to the programm - let module_instance = program.add_module("contract", module).expect("Module to be added successfully"); + // Add module to the programm + let module_instance = program.add_module("contract", module).expect("Module to be added successfully"); { - let env_instance = program.module("env").expect("env module to exist"); - let env_memory = env_instance.memory(interpreter::ItemIndex::Internal(0)) - .expect("liner memory to exist"); + let env_instance = program.module("env").expect("env module to exist"); + let env_memory = env_instance.memory(interpreter::ItemIndex::Internal(0)) + .expect("liner memory to exist"); - // Second, create runtime and program instance - let mut runtime = runtime::Runtime::with_params( - env_memory.clone(), // memory shared ptr - 5*1024*1024, // default stack space - 65536, // runner arbitrary gas limit - ); + // Second, create runtime and program instance + let mut runtime = runtime::Runtime::with_params( + env_memory.clone(), // memory shared ptr + 5*1024*1024, // default stack space + 65536, // runner arbitrary gas limit + ); - // Initialize call descriptor - let descriptor = call_args::init( - &*env_memory, - &mut runtime, - &[3u8; 128], - ).expect("call descriptor initialization to succeed"); + // Initialize call descriptor + let descriptor = call_args::init( + &*env_memory, + &mut runtime, + &[3u8; 128], + ).expect("call descriptor initialization to succeed"); // create native env module with native add && sub implementations let functions = interpreter::UserFunctions { executor: &mut runtime, functions: vec![ - interpreter::UserFunction { - name: "_storage_read".to_owned(), - params: vec![elements::ValueType::I32, elements::ValueType::I32], - result: Some(elements::ValueType::I32), - }, - interpreter::UserFunction { - name: "_storage_write".to_owned(), - params: vec![elements::ValueType::I32, elements::ValueType::I32], - result: Some(elements::ValueType::I32), - }, - interpreter::UserFunction { - name: "_malloc".to_owned(), - params: vec![elements::ValueType::I32], - result: Some(elements::ValueType::I32), - }, - interpreter::UserFunction { - name: "gas".to_owned(), - params: vec![elements::ValueType::I32], - result: None, - }, - interpreter::UserFunction { - name: "_free".to_owned(), - params: vec![elements::ValueType::I32], - result: None, - }, + interpreter::UserFunction { + name: "_storage_read".to_owned(), + params: vec![elements::ValueType::I32, elements::ValueType::I32], + result: Some(elements::ValueType::I32), + }, + interpreter::UserFunction { + name: "_storage_write".to_owned(), + params: vec![elements::ValueType::I32, elements::ValueType::I32], + result: Some(elements::ValueType::I32), + }, + interpreter::UserFunction { + name: "_malloc".to_owned(), + params: vec![elements::ValueType::I32], + result: Some(elements::ValueType::I32), + }, + interpreter::UserFunction { + name: "gas".to_owned(), + params: vec![elements::ValueType::I32], + result: None, + }, + interpreter::UserFunction { + name: "_free".to_owned(), + params: vec![elements::ValueType::I32], + result: None, + }, ], }; let native_env_instance = Arc::new(interpreter::env_native_module(env_instance, functions).unwrap()); - // Form ExecutionParams (payload + env link) + // Form ExecutionParams (payload + env link) let params = interpreter::ExecutionParams::with_external("env".into(), native_env_instance) - .add_argument(interpreter::RuntimeValue::I32(descriptor)); + .add_argument(interpreter::RuntimeValue::I32(descriptor)); - module_instance.execute_export("_call", params) - .expect("_call to execute successfully") - .expect("_call function to return result ptr"); - } + module_instance.execute_export("_call", params) + .expect("_call to execute successfully") + .expect("_call function to return result ptr"); + } } \ No newline at end of file diff --git a/samples/empty_contract.rs b/samples/empty_contract.rs index 027a60c..f226f2b 100644 --- a/samples/empty_contract.rs +++ b/samples/empty_contract.rs @@ -13,15 +13,15 @@ pub fn call() { /* This produces the following code (after injecting gas counter & optimizing) (module - (type (;0;) (func)) - (type (;1;) (func (param i32))) - (import "env" "memory" (memory (;0;) 256 256)) - (import "env" "table" (table (;0;) 0 0 anyfunc)) - (import "env" "gas" (func (;0;) (type 1))) - (func (;1;) (type 0) - i32.const 2 - call 0 - nop) - (export "_call" (func 1)) - (data (i32.const 1212) "\1c\05")) + (type (;0;) (func)) + (type (;1;) (func (param i32))) + (import "env" "memory" (memory (;0;) 256 256)) + (import "env" "table" (table (;0;) 0 0 anyfunc)) + (import "env" "gas" (func (;0;) (type 1))) + (func (;1;) (type 0) + i32.const 2 + call 0 + nop) + (export "_call" (func 1)) + (data (i32.const 1212) "\1c\05")) */ \ No newline at end of file diff --git a/src/ext.rs b/src/ext.rs index 1cfd528..2571c58 100644 --- a/src/ext.rs +++ b/src/ext.rs @@ -3,99 +3,99 @@ use parity_wasm::{elements, builder}; type Insertion = (usize, u32, u32, String); pub fn update_call_index(opcodes: &mut elements::Opcodes, original_imports: usize, inserts: &[Insertion]) { - use parity_wasm::elements::Opcode::*; - for opcode in opcodes.elements_mut().iter_mut() { - match opcode { - &mut Block(_, ref mut block) | &mut If(_, ref mut block) | &mut Loop(_, ref mut block) => { - update_call_index(block, original_imports, inserts) - }, - &mut Call(ref mut call_index) => { - if let Some(pos) = inserts.iter().position(|x| x.1 == *call_index) { - *call_index = (original_imports + pos) as u32; - } else if *call_index as usize > original_imports { - *call_index += inserts.len() as u32; - } - }, - _ => { } - } - } + use parity_wasm::elements::Opcode::*; + for opcode in opcodes.elements_mut().iter_mut() { + match opcode { + &mut Block(_, ref mut block) | &mut If(_, ref mut block) | &mut Loop(_, ref mut block) => { + update_call_index(block, original_imports, inserts) + }, + &mut Call(ref mut call_index) => { + if let Some(pos) = inserts.iter().position(|x| x.1 == *call_index) { + *call_index = (original_imports + pos) as u32; + } else if *call_index as usize > original_imports { + *call_index += inserts.len() as u32; + } + }, + _ => { } + } + } } pub fn externalize( - module: elements::Module, - replaced_funcs: Vec<&str>, + module: elements::Module, + replaced_funcs: Vec<&str>, ) -> elements::Module { // Save import functions number for later - let import_funcs_total = module - .import_section().expect("Import section to exist") - .entries() - .iter() - .filter(|e| if let &elements::External::Function(_) = e.external() { true } else { false }) - .count(); + let import_funcs_total = module + .import_section().expect("Import section to exist") + .entries() + .iter() + .filter(|e| if let &elements::External::Function(_) = e.external() { true } else { false }) + .count(); - // First, we find functions indices that are to be rewired to externals - // Triple is (function_index (callable), type_index, function_name) - let mut replaces: Vec = replaced_funcs - .into_iter() - .filter_map(|f| { - let export = module - .export_section().expect("Export section to exist") - .entries().iter().enumerate() - .find(|&(_, entry)| entry.field() == f) - .expect("All functions of interest to exist"); + // First, we find functions indices that are to be rewired to externals + // Triple is (function_index (callable), type_index, function_name) + let mut replaces: Vec = replaced_funcs + .into_iter() + .filter_map(|f| { + let export = module + .export_section().expect("Export section to exist") + .entries().iter().enumerate() + .find(|&(_, entry)| entry.field() == f) + .expect("All functions of interest to exist"); - if let &elements::Internal::Function(func_idx) = export.1.internal() { - let type_ref = module - .functions_section().expect("Functions section to exist") - .entries()[func_idx as usize - import_funcs_total] - .type_ref(); + if let &elements::Internal::Function(func_idx) = export.1.internal() { + let type_ref = module + .functions_section().expect("Functions section to exist") + .entries()[func_idx as usize - import_funcs_total] + .type_ref(); - Some((export.0, func_idx, type_ref, export.1.field().to_owned())) - } else { - None - } - }) - .collect(); + Some((export.0, func_idx, type_ref, export.1.field().to_owned())) + } else { + None + } + }) + .collect(); - replaces.sort_by_key(|e| e.0); + replaces.sort_by_key(|e| e.0); - // Second, we duplicate them as import definitions - let mut mbuilder = builder::from_module(module); - for &(_, _, type_ref, ref field) in replaces.iter() { - mbuilder.push_import( - builder::import() - .module("env") - .field(field) - .external().func(type_ref) - .build() - ); - } + // Second, we duplicate them as import definitions + let mut mbuilder = builder::from_module(module); + for &(_, _, type_ref, ref field) in replaces.iter() { + mbuilder.push_import( + builder::import() + .module("env") + .field(field) + .external().func(type_ref) + .build() + ); + } - // Back to mutable access - let mut module = mbuilder.build(); + // Back to mutable access + let mut module = mbuilder.build(); - // Third, rewire all calls to imported functions and update all other calls indices - for section in module.sections_mut() { - match section { - &mut elements::Section::Code(ref mut code_section) => { - for ref mut func_body in code_section.bodies_mut() { - update_call_index(func_body.code_mut(), import_funcs_total, &replaces); - } - }, - &mut elements::Section::Export(ref mut export_section) => { - for ref mut export in export_section.entries_mut() { - match export.internal_mut() { - &mut elements::Internal::Function(ref mut func_index) => { - if *func_index >= import_funcs_total as u32 { *func_index += replaces.len() as u32; } - }, - _ => {} - } - } - }, - _ => { } - } - } + // Third, rewire all calls to imported functions and update all other calls indices + for section in module.sections_mut() { + match section { + &mut elements::Section::Code(ref mut code_section) => { + for ref mut func_body in code_section.bodies_mut() { + update_call_index(func_body.code_mut(), import_funcs_total, &replaces); + } + }, + &mut elements::Section::Export(ref mut export_section) => { + for ref mut export in export_section.entries_mut() { + match export.internal_mut() { + &mut elements::Internal::Function(ref mut func_index) => { + if *func_index >= import_funcs_total as u32 { *func_index += replaces.len() as u32; } + }, + _ => {} + } + } + }, + _ => { } + } + } - module + module } \ No newline at end of file diff --git a/src/gas.rs b/src/gas.rs index e69c288..a57e9d6 100644 --- a/src/gas.rs +++ b/src/gas.rs @@ -2,98 +2,98 @@ use parity_wasm::{elements, builder}; pub fn update_call_index(opcodes: &mut elements::Opcodes, inserted_index: u32) { - use parity_wasm::elements::Opcode::*; - for opcode in opcodes.elements_mut().iter_mut() { - match opcode { - &mut Block(_, ref mut block) | &mut If(_, ref mut block) | &mut Loop(_, ref mut block) => { - update_call_index(block, inserted_index) - }, - &mut Call(ref mut call_index) => { - if *call_index >= inserted_index { *call_index += 1} - }, - _ => { } - } - } + use parity_wasm::elements::Opcode::*; + for opcode in opcodes.elements_mut().iter_mut() { + match opcode { + &mut Block(_, ref mut block) | &mut If(_, ref mut block) | &mut Loop(_, ref mut block) => { + update_call_index(block, inserted_index) + }, + &mut Call(ref mut call_index) => { + if *call_index >= inserted_index { *call_index += 1} + }, + _ => { } + } + } } pub fn inject_counter(opcodes: &mut elements::Opcodes, gas_func: u32) { - use parity_wasm::elements::Opcode::*; - for opcode in opcodes.elements_mut().iter_mut() { - match opcode { - &mut Block(_, ref mut block) | &mut If(_, ref mut block) | &mut Loop(_, ref mut block) => { - inject_counter(block, gas_func) - }, - _ => { } - } - } + use parity_wasm::elements::Opcode::*; + for opcode in opcodes.elements_mut().iter_mut() { + match opcode { + &mut Block(_, ref mut block) | &mut If(_, ref mut block) | &mut Loop(_, ref mut block) => { + inject_counter(block, gas_func) + }, + _ => { } + } + } - let ops = opcodes.elements_mut().len() as u32; - opcodes.elements_mut().insert(0, I32Const(ops as i32)); - opcodes.elements_mut().insert(1, Call(gas_func)); + let ops = opcodes.elements_mut().len() as u32; + opcodes.elements_mut().insert(0, I32Const(ops as i32)); + opcodes.elements_mut().insert(1, Call(gas_func)); } pub fn inject_gas_counter(module: elements::Module) -> elements::Module { - // Injecting gas counting external - let mut mbuilder = builder::from_module(module); - let import_sig = mbuilder.push_signature( - builder::signature() - .param().i32() - .build_sig() - ); + // Injecting gas counting external + let mut mbuilder = builder::from_module(module); + let import_sig = mbuilder.push_signature( + builder::signature() + .param().i32() + .build_sig() + ); - let mut gas_func = mbuilder.push_import( - builder::import() - .module("env") - .field("gas") - .external().func(import_sig) - .build() - ); + let mut gas_func = mbuilder.push_import( + builder::import() + .module("env") + .field("gas") + .external().func(import_sig) + .build() + ); - // back to plain module - let mut module = mbuilder.build(); + // back to plain module + let mut module = mbuilder.build(); - assert!(module.global_section().is_some()); + assert!(module.global_section().is_some()); - // calculate actual function index of the imported definition - // (substract all imports that are NOT functions) + // calculate actual function index of the imported definition + // (substract all imports that are NOT functions) - for import_entry in module.import_section().expect("Builder should have insert the import section").entries() { - match *import_entry.external() { - elements::External::Function(_) => {}, - _ => { gas_func -= 1; } - } - } + for import_entry in module.import_section().expect("Builder should have insert the import section").entries() { + match *import_entry.external() { + elements::External::Function(_) => {}, + _ => { gas_func -= 1; } + } + } - // Updating calling addresses (all calls to function index >= `gas_func` should be incremented) - for section in module.sections_mut() { - match section { - &mut elements::Section::Code(ref mut code_section) => { - for ref mut func_body in code_section.bodies_mut() { - update_call_index(func_body.code_mut(), gas_func); - inject_counter(func_body.code_mut(), gas_func); - } - }, - &mut elements::Section::Export(ref mut export_section) => { - for ref mut export in export_section.entries_mut() { - match export.internal_mut() { - &mut elements::Internal::Function(ref mut func_index) => { - if *func_index >= gas_func { *func_index += 1} - }, - _ => {} - } - } - }, - &mut elements::Section::Element(ref mut elements_section) => { - for ref mut segment in elements_section.entries_mut() { - // update all indirect call addresses initial values - for func_index in segment.members_mut() { - if *func_index >= gas_func { *func_index += 1} - } - } - }, - _ => { } - } - } + // Updating calling addresses (all calls to function index >= `gas_func` should be incremented) + for section in module.sections_mut() { + match section { + &mut elements::Section::Code(ref mut code_section) => { + for ref mut func_body in code_section.bodies_mut() { + update_call_index(func_body.code_mut(), gas_func); + inject_counter(func_body.code_mut(), gas_func); + } + }, + &mut elements::Section::Export(ref mut export_section) => { + for ref mut export in export_section.entries_mut() { + match export.internal_mut() { + &mut elements::Internal::Function(ref mut func_index) => { + if *func_index >= gas_func { *func_index += 1} + }, + _ => {} + } + } + }, + &mut elements::Section::Element(ref mut elements_section) => { + for ref mut segment in elements_section.entries_mut() { + // update all indirect call addresses initial values + for func_index in segment.members_mut() { + if *func_index >= gas_func { *func_index += 1} + } + } + }, + _ => { } + } + } - module + module } \ No newline at end of file diff --git a/src/optimizer.rs b/src/optimizer.rs index 2b5aa63..2053b0d 100644 --- a/src/optimizer.rs +++ b/src/optimizer.rs @@ -5,546 +5,546 @@ use symbols::{Symbol, expand_symbols, push_code_symbols, resolve_function}; #[derive(Debug)] pub enum Error { - /// Since optimizer starts with export entries, export - /// section is supposed to exist. - NoExportSection, + /// Since optimizer starts with export entries, export + /// section is supposed to exist. + NoExportSection, } pub fn optimize( - module: &mut elements::Module, // Module to optimize - used_exports: Vec<&str>, // List of only exports that will be usable after optimization + module: &mut elements::Module, // Module to optimize + used_exports: Vec<&str>, // List of only exports that will be usable after optimization ) -> Result<(), Error> { - // WebAssembly exports optimizer - // Motivation: emscripten compiler backend compiles in many unused exports - // which in turn compile in unused imports and leaves unused functions + // WebAssembly exports optimizer + // Motivation: emscripten compiler backend compiles in many unused exports + // which in turn compile in unused imports and leaves unused functions - // Algo starts from the top, listing all items that should stay - let mut stay = HashSet::new(); - for (index, entry) in module.export_section().ok_or(Error::NoExportSection)?.entries().iter().enumerate() { - if used_exports.iter().find(|e| **e == entry.field()).is_some() { - stay.insert(Symbol::Export(index)); - } - } + // Algo starts from the top, listing all items that should stay + let mut stay = HashSet::new(); + for (index, entry) in module.export_section().ok_or(Error::NoExportSection)?.entries().iter().enumerate() { + if used_exports.iter().find(|e| **e == entry.field()).is_some() { + stay.insert(Symbol::Export(index)); + } + } - // All symbols used in data/element segments are also should be preserved - let mut init_symbols = Vec::new(); - if let Some(data_section) = module.data_section() { - for segment in data_section.entries() { - push_code_symbols(&module, segment.offset().code(), &mut init_symbols); - } - } - if let Some(elements_section) = module.elements_section() { - for segment in elements_section.entries() { - push_code_symbols(&module, segment.offset().code(), &mut init_symbols); - for func_index in segment.members() { - stay.insert(resolve_function(&module, *func_index)); - } - } - } - for symbol in init_symbols.drain(..) { stay.insert(symbol); } + // All symbols used in data/element segments are also should be preserved + let mut init_symbols = Vec::new(); + if let Some(data_section) = module.data_section() { + for segment in data_section.entries() { + push_code_symbols(&module, segment.offset().code(), &mut init_symbols); + } + } + if let Some(elements_section) = module.elements_section() { + for segment in elements_section.entries() { + push_code_symbols(&module, segment.offset().code(), &mut init_symbols); + for func_index in segment.members() { + stay.insert(resolve_function(&module, *func_index)); + } + } + } + for symbol in init_symbols.drain(..) { stay.insert(symbol); } - // Call function which will traverse the list recursively, filling stay with all symbols - // that are already used by those which already there - expand_symbols(module, &mut stay); + // Call function which will traverse the list recursively, filling stay with all symbols + // that are already used by those which already there + expand_symbols(module, &mut stay); - for symbol in stay.iter() { - trace!("symbol to stay: {:?}", symbol); - } + for symbol in stay.iter() { + trace!("symbol to stay: {:?}", symbol); + } - // Keep track of referreable symbols to rewire calls/globals - let mut eliminated_funcs = Vec::new(); - let mut eliminated_globals = Vec::new(); - let mut eliminated_types = Vec::new(); + // Keep track of referreable symbols to rewire calls/globals + let mut eliminated_funcs = Vec::new(); + let mut eliminated_globals = Vec::new(); + let mut eliminated_types = Vec::new(); - // First, iterate through types - let mut index = 0; - let mut old_index = 0; + // First, iterate through types + let mut index = 0; + let mut old_index = 0; - { - loop { - if type_section(module).map(|section| section.types_mut().len()).unwrap_or(0) == index { break; } + { + loop { + if type_section(module).map(|section| section.types_mut().len()).unwrap_or(0) == index { break; } - if stay.contains(&Symbol::Type(old_index)) { - index += 1; - } else { - type_section(module) - .expect("If type section does not exists, the loop will break at the beginning of first iteration") - .types_mut().remove(index); - eliminated_types.push(old_index); - trace!("Eliminated type({})", old_index); - } - old_index += 1; - } - } + if stay.contains(&Symbol::Type(old_index)) { + index += 1; + } else { + type_section(module) + .expect("If type section does not exists, the loop will break at the beginning of first iteration") + .types_mut().remove(index); + eliminated_types.push(old_index); + trace!("Eliminated type({})", old_index); + } + old_index += 1; + } + } - // Second, iterate through imports - let mut top_funcs = 0; - let mut top_globals = 0; - index = 0; - old_index = 0; + // Second, iterate through imports + let mut top_funcs = 0; + let mut top_globals = 0; + index = 0; + old_index = 0; - if let Some(imports) = import_section(module) { - loop { - let mut remove = false; - match imports.entries()[index].external() { - &elements::External::Function(_) => { - if stay.contains(&Symbol::Import(old_index)) { - index += 1; - } else { - remove = true; - eliminated_funcs.push(top_funcs); - trace!("Eliminated import({}) func({}, {})", old_index, top_funcs, imports.entries()[index].field()); - } - top_funcs += 1; - }, - &elements::External::Global(_) => { - if stay.contains(&Symbol::Import(old_index)) { - index += 1; - } else { - remove = true; - eliminated_globals.push(top_globals); - trace!("Eliminated import({}) global({}, {})", old_index, top_globals, imports.entries()[index].field()); - } - top_globals += 1; - }, - _ => { - index += 1; - } - } - if remove { - imports.entries_mut().remove(index); - } + if let Some(imports) = import_section(module) { + loop { + let mut remove = false; + match imports.entries()[index].external() { + &elements::External::Function(_) => { + if stay.contains(&Symbol::Import(old_index)) { + index += 1; + } else { + remove = true; + eliminated_funcs.push(top_funcs); + trace!("Eliminated import({}) func({}, {})", old_index, top_funcs, imports.entries()[index].field()); + } + top_funcs += 1; + }, + &elements::External::Global(_) => { + if stay.contains(&Symbol::Import(old_index)) { + index += 1; + } else { + remove = true; + eliminated_globals.push(top_globals); + trace!("Eliminated import({}) global({}, {})", old_index, top_globals, imports.entries()[index].field()); + } + top_globals += 1; + }, + _ => { + index += 1; + } + } + if remove { + imports.entries_mut().remove(index); + } - old_index += 1; + old_index += 1; - if index == imports.entries().len() { break; } - } - } + if index == imports.entries().len() { break; } + } + } - // Third, iterate through globals - if let Some(globals) = global_section(module) { - index = 0; - old_index = 0; + // Third, iterate through globals + if let Some(globals) = global_section(module) { + index = 0; + old_index = 0; - loop { - if globals.entries_mut().len() == index { break; } - if stay.contains(&Symbol::Global(old_index)) { - index += 1; - } else { - globals.entries_mut().remove(index); - eliminated_globals.push(top_globals + old_index); - trace!("Eliminated global({})", top_globals + old_index); - } - old_index += 1; - } - } + loop { + if globals.entries_mut().len() == index { break; } + if stay.contains(&Symbol::Global(old_index)) { + index += 1; + } else { + globals.entries_mut().remove(index); + eliminated_globals.push(top_globals + old_index); + trace!("Eliminated global({})", top_globals + old_index); + } + old_index += 1; + } + } - // Forth, delete orphaned functions - if functions_section(module).is_some() && code_section(module).is_some() { - index = 0; - old_index = 0; + // Forth, delete orphaned functions + if functions_section(module).is_some() && code_section(module).is_some() { + index = 0; + old_index = 0; - loop { - if functions_section(module).expect("Functons section to exist").entries_mut().len() == index { break; } - if stay.contains(&Symbol::Function(old_index)) { - index += 1; - } else { - functions_section(module).expect("Functons section to exist").entries_mut().remove(index); - code_section(module).expect("Code section to exist").bodies_mut().remove(index); + loop { + if functions_section(module).expect("Functons section to exist").entries_mut().len() == index { break; } + if stay.contains(&Symbol::Function(old_index)) { + index += 1; + } else { + functions_section(module).expect("Functons section to exist").entries_mut().remove(index); + code_section(module).expect("Code section to exist").bodies_mut().remove(index); - eliminated_funcs.push(top_funcs + old_index); - trace!("Eliminated function({})", top_funcs + old_index); - } - old_index += 1; - } - } + eliminated_funcs.push(top_funcs + old_index); + trace!("Eliminated function({})", top_funcs + old_index); + } + old_index += 1; + } + } - // Fifth, eliminate unused exports - { - let exports = export_section(module).ok_or(Error::NoExportSection)?; + // Fifth, eliminate unused exports + { + let exports = export_section(module).ok_or(Error::NoExportSection)?; - index = 0; - old_index = 0; + index = 0; + old_index = 0; - loop { - if exports.entries_mut().len() == index { break; } - if stay.contains(&Symbol::Export(old_index)) { - index += 1; - } else { - trace!("Eliminated export({}, {})", old_index, exports.entries_mut()[index].field()); - exports.entries_mut().remove(index); - } - old_index += 1; - } - } + loop { + if exports.entries_mut().len() == index { break; } + if stay.contains(&Symbol::Export(old_index)) { + index += 1; + } else { + trace!("Eliminated export({}, {})", old_index, exports.entries_mut()[index].field()); + exports.entries_mut().remove(index); + } + old_index += 1; + } + } - if eliminated_globals.len() > 0 || eliminated_funcs.len() > 0 || eliminated_types.len() > 0 { - // Finaly, rewire all calls, globals references and types to the new indices - // (only if there is anything to do) - eliminated_globals.sort(); - eliminated_funcs.sort(); - eliminated_types.sort(); + if eliminated_globals.len() > 0 || eliminated_funcs.len() > 0 || eliminated_types.len() > 0 { + // Finaly, rewire all calls, globals references and types to the new indices + // (only if there is anything to do) + eliminated_globals.sort(); + eliminated_funcs.sort(); + eliminated_types.sort(); - for section in module.sections_mut() { - match section { - &mut elements::Section::Function(ref mut function_section) if eliminated_types.len() > 0 => { - for ref mut func_signature in function_section.entries_mut() { - let totalle = eliminated_types.iter().take_while(|i| (**i as u32) < func_signature.type_ref()).count(); - *func_signature.type_ref_mut() -= totalle as u32; - } - }, - &mut elements::Section::Import(ref mut import_section) if eliminated_types.len() > 0 => { - for ref mut import_entry in import_section.entries_mut() { - if let &mut elements::External::Function(ref mut type_ref) = import_entry.external_mut() { - let totalle = eliminated_types.iter().take_while(|i| (**i as u32) < *type_ref).count(); - *type_ref -= totalle as u32; - } - } - }, - &mut elements::Section::Code(ref mut code_section) if eliminated_globals.len() > 0 || eliminated_funcs.len() > 0 => { - for ref mut func_body in code_section.bodies_mut() { - if eliminated_funcs.len() > 0 { - update_call_index(func_body.code_mut(), &eliminated_funcs); - } - if eliminated_globals.len() > 0 { - update_global_index(func_body.code_mut().elements_mut(), &eliminated_globals) - } - } - }, - &mut elements::Section::Export(ref mut export_section) => { - for ref mut export in export_section.entries_mut() { - match export.internal_mut() { - &mut elements::Internal::Function(ref mut func_index) => { - let totalle = eliminated_funcs.iter().take_while(|i| (**i as u32) < *func_index).count(); - *func_index -= totalle as u32; - }, - &mut elements::Internal::Global(ref mut global_index) => { - let totalle = eliminated_globals.iter().take_while(|i| (**i as u32) < *global_index).count(); - *global_index -= totalle as u32; - }, - _ => {} - } - } - }, - &mut elements::Section::Global(ref mut global_section) => { - for ref mut global_entry in global_section.entries_mut() { - update_global_index(global_entry.init_expr_mut().code_mut(), &eliminated_globals) - } - }, - &mut elements::Section::Data(ref mut data_section) => { - for ref mut segment in data_section.entries_mut() { - update_global_index(segment.offset_mut().code_mut(), &eliminated_globals) - } - }, - &mut elements::Section::Element(ref mut elements_section) => { - for ref mut segment in elements_section.entries_mut() { - update_global_index(segment.offset_mut().code_mut(), &eliminated_globals); - // update all indirect call addresses initial values - for func_index in segment.members_mut() { - let totalle = eliminated_funcs.iter().take_while(|i| (**i as u32) < *func_index).count(); - *func_index -= totalle as u32; - } - } - }, - _ => { } - } - } - } + for section in module.sections_mut() { + match section { + &mut elements::Section::Function(ref mut function_section) if eliminated_types.len() > 0 => { + for ref mut func_signature in function_section.entries_mut() { + let totalle = eliminated_types.iter().take_while(|i| (**i as u32) < func_signature.type_ref()).count(); + *func_signature.type_ref_mut() -= totalle as u32; + } + }, + &mut elements::Section::Import(ref mut import_section) if eliminated_types.len() > 0 => { + for ref mut import_entry in import_section.entries_mut() { + if let &mut elements::External::Function(ref mut type_ref) = import_entry.external_mut() { + let totalle = eliminated_types.iter().take_while(|i| (**i as u32) < *type_ref).count(); + *type_ref -= totalle as u32; + } + } + }, + &mut elements::Section::Code(ref mut code_section) if eliminated_globals.len() > 0 || eliminated_funcs.len() > 0 => { + for ref mut func_body in code_section.bodies_mut() { + if eliminated_funcs.len() > 0 { + update_call_index(func_body.code_mut(), &eliminated_funcs); + } + if eliminated_globals.len() > 0 { + update_global_index(func_body.code_mut().elements_mut(), &eliminated_globals) + } + } + }, + &mut elements::Section::Export(ref mut export_section) => { + for ref mut export in export_section.entries_mut() { + match export.internal_mut() { + &mut elements::Internal::Function(ref mut func_index) => { + let totalle = eliminated_funcs.iter().take_while(|i| (**i as u32) < *func_index).count(); + *func_index -= totalle as u32; + }, + &mut elements::Internal::Global(ref mut global_index) => { + let totalle = eliminated_globals.iter().take_while(|i| (**i as u32) < *global_index).count(); + *global_index -= totalle as u32; + }, + _ => {} + } + } + }, + &mut elements::Section::Global(ref mut global_section) => { + for ref mut global_entry in global_section.entries_mut() { + update_global_index(global_entry.init_expr_mut().code_mut(), &eliminated_globals) + } + }, + &mut elements::Section::Data(ref mut data_section) => { + for ref mut segment in data_section.entries_mut() { + update_global_index(segment.offset_mut().code_mut(), &eliminated_globals) + } + }, + &mut elements::Section::Element(ref mut elements_section) => { + for ref mut segment in elements_section.entries_mut() { + update_global_index(segment.offset_mut().code_mut(), &eliminated_globals); + // update all indirect call addresses initial values + for func_index in segment.members_mut() { + let totalle = eliminated_funcs.iter().take_while(|i| (**i as u32) < *func_index).count(); + *func_index -= totalle as u32; + } + } + }, + _ => { } + } + } + } - Ok(()) + Ok(()) } pub fn update_call_index(opcodes: &mut elements::Opcodes, eliminated_indices: &[usize]) { - use parity_wasm::elements::Opcode::*; - for opcode in opcodes.elements_mut().iter_mut() { - match opcode { - &mut Block(_, ref mut block) | &mut If(_, ref mut block) | &mut Loop(_, ref mut block) => { - update_call_index(block, eliminated_indices) - }, - &mut Call(ref mut call_index) => { - let totalle = eliminated_indices.iter().take_while(|i| (**i as u32) < *call_index).count(); - trace!("rewired call {} -> call {}", *call_index, *call_index - totalle as u32); - *call_index -= totalle as u32; - }, - _ => { }, - } - } + use parity_wasm::elements::Opcode::*; + for opcode in opcodes.elements_mut().iter_mut() { + match opcode { + &mut Block(_, ref mut block) | &mut If(_, ref mut block) | &mut Loop(_, ref mut block) => { + update_call_index(block, eliminated_indices) + }, + &mut Call(ref mut call_index) => { + let totalle = eliminated_indices.iter().take_while(|i| (**i as u32) < *call_index).count(); + trace!("rewired call {} -> call {}", *call_index, *call_index - totalle as u32); + *call_index -= totalle as u32; + }, + _ => { }, + } + } } /// Updates global references considering the _ordered_ list of eliminated indices pub fn update_global_index(opcodes: &mut Vec, eliminated_indices: &[usize]) { - use parity_wasm::elements::Opcode::*; - for opcode in opcodes.iter_mut() { - match opcode { - &mut Block(_, ref mut block) | &mut If(_, ref mut block) | &mut Loop(_, ref mut block) => { - update_global_index(block.elements_mut(), eliminated_indices) - }, - &mut GetGlobal(ref mut index) | &mut SetGlobal(ref mut index) => { - let totalle = eliminated_indices.iter().take_while(|i| (**i as u32) < *index).count(); - trace!("rewired global {} -> global {}", *index, *index - totalle as u32); - *index -= totalle as u32; - }, - _ => { }, - } - } + use parity_wasm::elements::Opcode::*; + for opcode in opcodes.iter_mut() { + match opcode { + &mut Block(_, ref mut block) | &mut If(_, ref mut block) | &mut Loop(_, ref mut block) => { + update_global_index(block.elements_mut(), eliminated_indices) + }, + &mut GetGlobal(ref mut index) | &mut SetGlobal(ref mut index) => { + let totalle = eliminated_indices.iter().take_while(|i| (**i as u32) < *index).count(); + trace!("rewired global {} -> global {}", *index, *index - totalle as u32); + *index -= totalle as u32; + }, + _ => { }, + } + } } pub fn import_section<'a>(module: &'a mut elements::Module) -> Option<&'a mut elements::ImportSection> { for section in module.sections_mut() { - match section { - &mut elements::Section::Import(ref mut sect) => { - return Some(sect); - }, - _ => { } - } - } - None + match section { + &mut elements::Section::Import(ref mut sect) => { + return Some(sect); + }, + _ => { } + } + } + None } pub fn global_section<'a>(module: &'a mut elements::Module) -> Option<&'a mut elements::GlobalSection> { for section in module.sections_mut() { - match section { - &mut elements::Section::Global(ref mut sect) => { - return Some(sect); - }, - _ => { } - } - } - None + match section { + &mut elements::Section::Global(ref mut sect) => { + return Some(sect); + }, + _ => { } + } + } + None } pub fn functions_section<'a>(module: &'a mut elements::Module) -> Option<&'a mut elements::FunctionsSection> { for section in module.sections_mut() { - match section { - &mut elements::Section::Function(ref mut sect) => { - return Some(sect); - }, - _ => { } - } - } - None + match section { + &mut elements::Section::Function(ref mut sect) => { + return Some(sect); + }, + _ => { } + } + } + None } pub fn code_section<'a>(module: &'a mut elements::Module) -> Option<&'a mut elements::CodeSection> { for section in module.sections_mut() { - match section { - &mut elements::Section::Code(ref mut sect) => { - return Some(sect); - }, - _ => { } - } - } - None + match section { + &mut elements::Section::Code(ref mut sect) => { + return Some(sect); + }, + _ => { } + } + } + None } pub fn export_section<'a>(module: &'a mut elements::Module) -> Option<&'a mut elements::ExportSection> { for section in module.sections_mut() { - match section { - &mut elements::Section::Export(ref mut sect) => { - return Some(sect); - }, - _ => { } - } - } - None + match section { + &mut elements::Section::Export(ref mut sect) => { + return Some(sect); + }, + _ => { } + } + } + None } pub fn type_section<'a>(module: &'a mut elements::Module) -> Option<&'a mut elements::TypeSection> { for section in module.sections_mut() { - match section { - &mut elements::Section::Type(ref mut sect) => { - return Some(sect); - }, - _ => { } - } - } - None + match section { + &mut elements::Section::Type(ref mut sect) => { + return Some(sect); + }, + _ => { } + } + } + None } #[cfg(test)] mod tests { - use parity_wasm::{builder, elements}; - use super::*; + use parity_wasm::{builder, elements}; + use super::*; - /// @spec 0 - /// Optimizer presumes that export section exists and contains - /// all symbols passed as a second parameter. Since empty module - /// obviously contains no export section, optimizer should return - /// error on it. - #[test] - fn empty() { - let mut module = builder::module().build(); - let result = optimize(&mut module, vec!["_call"]); + /// @spec 0 + /// Optimizer presumes that export section exists and contains + /// all symbols passed as a second parameter. Since empty module + /// obviously contains no export section, optimizer should return + /// error on it. + #[test] + fn empty() { + let mut module = builder::module().build(); + let result = optimize(&mut module, vec!["_call"]); - assert!(result.is_err()); - } + assert!(result.is_err()); + } - /// @spec 1 - /// Imagine the unoptimized module has two own functions, `_call` and `_random` - /// and exports both of them in the export section. During optimization, the `_random` - /// function should vanish completely, given we pass `_call` as the only function to stay - /// in the module. - #[test] - fn minimal() { - let mut module = builder::module() - .function() - .signature().param().i32().build() - .build() - .function() - .signature() - .param().i32() - .param().i32() - .build() - .build() - .export() - .field("_call") - .internal().func(0).build() - .export() - .field("_random") - .internal().func(1).build() - .build(); - assert_eq!(module.export_section().expect("export section to be generated").entries().len(), 2); + /// @spec 1 + /// Imagine the unoptimized module has two own functions, `_call` and `_random` + /// and exports both of them in the export section. During optimization, the `_random` + /// function should vanish completely, given we pass `_call` as the only function to stay + /// in the module. + #[test] + fn minimal() { + let mut module = builder::module() + .function() + .signature().param().i32().build() + .build() + .function() + .signature() + .param().i32() + .param().i32() + .build() + .build() + .export() + .field("_call") + .internal().func(0).build() + .export() + .field("_random") + .internal().func(1).build() + .build(); + assert_eq!(module.export_section().expect("export section to be generated").entries().len(), 2); - optimize(&mut module, vec!["_call"]).expect("optimizer to succeed"); + optimize(&mut module, vec!["_call"]).expect("optimizer to succeed"); - assert_eq!( - 1, - module.export_section().expect("export section to be generated").entries().len(), - "There should only 1 (one) export entry in the optimized module" - ); + assert_eq!( + 1, + module.export_section().expect("export section to be generated").entries().len(), + "There should only 1 (one) export entry in the optimized module" + ); - assert_eq!( - 1, - module.functions_section().expect("functions section to be generated").entries().len(), - "There should 2 (two) functions in the optimized module" - ); - } + assert_eq!( + 1, + module.functions_section().expect("functions section to be generated").entries().len(), + "There should 2 (two) functions in the optimized module" + ); + } - /// @spec 2 - /// Imagine there is one exported function in unoptimized module, `_call`, that we specify as the one - /// to stay during the optimization. The code of this function uses global during the execution. - /// This sayed global should survive the optimization. - #[test] - fn globals() { - let mut module = builder::module() - .global() - .value_type().i32() - .build() - .function() - .signature().param().i32().build() - .body() - .with_opcodes(elements::Opcodes::new( - vec![ - elements::Opcode::GetGlobal(0), - elements::Opcode::End - ] - )) - .build() - .build() - .export() - .field("_call") - .internal().func(0).build() - .build(); + /// @spec 2 + /// Imagine there is one exported function in unoptimized module, `_call`, that we specify as the one + /// to stay during the optimization. The code of this function uses global during the execution. + /// This sayed global should survive the optimization. + #[test] + fn globals() { + let mut module = builder::module() + .global() + .value_type().i32() + .build() + .function() + .signature().param().i32().build() + .body() + .with_opcodes(elements::Opcodes::new( + vec![ + elements::Opcode::GetGlobal(0), + elements::Opcode::End + ] + )) + .build() + .build() + .export() + .field("_call") + .internal().func(0).build() + .build(); - optimize(&mut module, vec!["_call"]).expect("optimizer to succeed"); + optimize(&mut module, vec!["_call"]).expect("optimizer to succeed"); - assert_eq!( - 1, - module.global_section().expect("global section to be generated").entries().len(), - "There should 1 (one) global entry in the optimized module, since _call function uses it" - ); - } + assert_eq!( + 1, + module.global_section().expect("global section to be generated").entries().len(), + "There should 1 (one) global entry in the optimized module, since _call function uses it" + ); + } - /// @spec 2 - /// Imagine there is one exported function in unoptimized module, `_call`, that we specify as the one - /// to stay during the optimization. The code of this function uses one global during the execution, - /// but we have a bunch of other unused globals in the code. Last globals should not survive the optimization, - /// while the former should. - #[test] - fn globals_2() { - let mut module = builder::module() - .global() - .value_type().i32() - .build() - .global() - .value_type().i64() - .build() - .global() - .value_type().f32() - .build() - .function() - .signature().param().i32().build() - .body() - .with_opcodes(elements::Opcodes::new( - vec![ - elements::Opcode::GetGlobal(1), - elements::Opcode::End - ] - )) - .build() - .build() - .export() - .field("_call") - .internal().func(0).build() - .build(); + /// @spec 2 + /// Imagine there is one exported function in unoptimized module, `_call`, that we specify as the one + /// to stay during the optimization. The code of this function uses one global during the execution, + /// but we have a bunch of other unused globals in the code. Last globals should not survive the optimization, + /// while the former should. + #[test] + fn globals_2() { + let mut module = builder::module() + .global() + .value_type().i32() + .build() + .global() + .value_type().i64() + .build() + .global() + .value_type().f32() + .build() + .function() + .signature().param().i32().build() + .body() + .with_opcodes(elements::Opcodes::new( + vec![ + elements::Opcode::GetGlobal(1), + elements::Opcode::End + ] + )) + .build() + .build() + .export() + .field("_call") + .internal().func(0).build() + .build(); - optimize(&mut module, vec!["_call"]).expect("optimizer to succeed"); + optimize(&mut module, vec!["_call"]).expect("optimizer to succeed"); - assert_eq!( - 1, - module.global_section().expect("global section to be generated").entries().len(), - "There should 1 (one) global entry in the optimized module, since _call function uses only one" - ); - } + assert_eq!( + 1, + module.global_section().expect("global section to be generated").entries().len(), + "There should 1 (one) global entry in the optimized module, since _call function uses only one" + ); + } - /// @spec 3 - /// Imagine the unoptimized module has two own functions, `_call` and `_random` - /// and exports both of them in the export section. Function `_call` also calls `_random` - /// in its function body. The optimization should kick `_random` function from the export section - /// but preserve it's body. - #[test] - fn call_ref() { - let mut module = builder::module() - .function() - .signature().param().i32().build() - .body() - .with_opcodes(elements::Opcodes::new( - vec![ - elements::Opcode::Call(1), - elements::Opcode::End - ] - )) - .build() - .build() - .function() - .signature() - .param().i32() - .param().i32() - .build() - .build() - .export() - .field("_call") - .internal().func(0).build() - .export() - .field("_random") - .internal().func(1).build() - .build(); - assert_eq!(module.export_section().expect("export section to be generated").entries().len(), 2); + /// @spec 3 + /// Imagine the unoptimized module has two own functions, `_call` and `_random` + /// and exports both of them in the export section. Function `_call` also calls `_random` + /// in its function body. The optimization should kick `_random` function from the export section + /// but preserve it's body. + #[test] + fn call_ref() { + let mut module = builder::module() + .function() + .signature().param().i32().build() + .body() + .with_opcodes(elements::Opcodes::new( + vec![ + elements::Opcode::Call(1), + elements::Opcode::End + ] + )) + .build() + .build() + .function() + .signature() + .param().i32() + .param().i32() + .build() + .build() + .export() + .field("_call") + .internal().func(0).build() + .export() + .field("_random") + .internal().func(1).build() + .build(); + assert_eq!(module.export_section().expect("export section to be generated").entries().len(), 2); - optimize(&mut module, vec!["_call"]).expect("optimizer to succeed"); + optimize(&mut module, vec!["_call"]).expect("optimizer to succeed"); - assert_eq!( - 1, - module.export_section().expect("export section to be generated").entries().len(), - "There should only 1 (one) export entry in the optimized module" - ); + assert_eq!( + 1, + module.export_section().expect("export section to be generated").entries().len(), + "There should only 1 (one) export entry in the optimized module" + ); - assert_eq!( - 2, - module.functions_section().expect("functions section to be generated").entries().len(), - "There should 2 (two) functions in the optimized module" - ); - } + assert_eq!( + 2, + module.functions_section().expect("functions section to be generated").entries().len(), + "There should 2 (two) functions in the optimized module" + ); + } } \ No newline at end of file diff --git a/src/symbols.rs b/src/symbols.rs index b86efb9..29d47bc 100644 --- a/src/symbols.rs +++ b/src/symbols.rs @@ -3,150 +3,150 @@ use std::collections::HashSet; #[derive(PartialEq, Eq, Hash, Copy, Clone, Debug)] pub enum Symbol { - Type(usize), - Import(usize), - Global(usize), - Function(usize), - Export(usize), + Type(usize), + Import(usize), + Global(usize), + Function(usize), + Export(usize), } pub fn resolve_function(module: &elements::Module, index: u32) -> Symbol { - let mut functions = 0; - if let Some(import_section) = module.import_section() { - for (item_index, item) in import_section.entries().iter().enumerate() { - match item.external() { - &elements::External::Function(_) => { - if functions == index { - return Symbol::Import(item_index as usize); - } - functions += 1; - }, - _ => {} - } - } - } + let mut functions = 0; + if let Some(import_section) = module.import_section() { + for (item_index, item) in import_section.entries().iter().enumerate() { + match item.external() { + &elements::External::Function(_) => { + if functions == index { + return Symbol::Import(item_index as usize); + } + functions += 1; + }, + _ => {} + } + } + } - Symbol::Function(index as usize - functions as usize) + Symbol::Function(index as usize - functions as usize) } pub fn resolve_global(module: &elements::Module, index: u32) -> Symbol { - let mut globals = 0; - if let Some(import_section) = module.import_section() { - for (item_index, item) in import_section.entries().iter().enumerate() { - match item.external() { - &elements::External::Global(_) => { - if globals == index { - return Symbol::Import(item_index as usize); - } - globals += 1; - }, - _ => {} - } - } - } + let mut globals = 0; + if let Some(import_section) = module.import_section() { + for (item_index, item) in import_section.entries().iter().enumerate() { + match item.external() { + &elements::External::Global(_) => { + if globals == index { + return Symbol::Import(item_index as usize); + } + globals += 1; + }, + _ => {} + } + } + } - Symbol::Global(index as usize - globals as usize) + Symbol::Global(index as usize - globals as usize) } pub fn push_code_symbols(module: &elements::Module, opcodes: &[elements::Opcode], dest: &mut Vec) { - use parity_wasm::elements::Opcode::*; + use parity_wasm::elements::Opcode::*; - for opcode in opcodes { - match opcode { - &Call(idx) => { - dest.push(resolve_function(module, idx)); - }, - &GetGlobal(idx) | &SetGlobal(idx) => { - dest.push(resolve_global(module, idx)) - }, - &If(_, ref block) | &Loop(_, ref block) | &Block(_, ref block) => { - push_code_symbols(module, block.elements(), dest); - }, - _ => { }, - } - } + for opcode in opcodes { + match opcode { + &Call(idx) => { + dest.push(resolve_function(module, idx)); + }, + &GetGlobal(idx) | &SetGlobal(idx) => { + dest.push(resolve_global(module, idx)) + }, + &If(_, ref block) | &Loop(_, ref block) | &Block(_, ref block) => { + push_code_symbols(module, block.elements(), dest); + }, + _ => { }, + } + } } pub fn expand_symbols(module: &elements::Module, set: &mut HashSet) { - use self::Symbol::*; + use self::Symbol::*; - // symbols that were already processed - let mut stop: HashSet = HashSet::new(); - let mut fringe = set.iter().cloned().collect::>(); - loop { - let next = match fringe.pop() { - Some(s) if stop.contains(&s) => { continue; } - Some(s) => s, - _ => { break; } - }; - trace!("Processing symbol {:?}", next); + // symbols that were already processed + let mut stop: HashSet = HashSet::new(); + let mut fringe = set.iter().cloned().collect::>(); + loop { + let next = match fringe.pop() { + Some(s) if stop.contains(&s) => { continue; } + Some(s) => s, + _ => { break; } + }; + trace!("Processing symbol {:?}", next); - match next { - Export(idx) => { - let entry = &module.export_section().expect("Export section to exist").entries()[idx]; - match entry.internal() { - &elements::Internal::Function(func_idx) => { - let symbol = resolve_function(module, func_idx); - if !stop.contains(&symbol) { - fringe.push(symbol); - } - set.insert(symbol); - }, - &elements::Internal::Global(global_idx) => { - let symbol = resolve_global(module, global_idx); - if !stop.contains(&symbol) { - fringe.push(symbol); - } - set.insert(symbol); - }, - _ => {} - } - }, - Import(idx) => { - let entry = &module.import_section().expect("Import section to exist").entries()[idx]; - match entry.external() { - &elements::External::Function(type_idx) => { - let type_symbol = Symbol::Type(type_idx as usize); - if !stop.contains(&type_symbol) { - fringe.push(type_symbol); - } - set.insert(type_symbol); - }, - _ => {} - } - }, - Function(idx) => { - let body = &module.code_section().expect("Code section to exist").bodies()[idx]; - let mut code_symbols = Vec::new(); - push_code_symbols(module, body.code().elements(), &mut code_symbols); - for symbol in code_symbols.drain(..) { - if !stop.contains(&symbol) { - fringe.push(symbol); - } - set.insert(symbol); - } + match next { + Export(idx) => { + let entry = &module.export_section().expect("Export section to exist").entries()[idx]; + match entry.internal() { + &elements::Internal::Function(func_idx) => { + let symbol = resolve_function(module, func_idx); + if !stop.contains(&symbol) { + fringe.push(symbol); + } + set.insert(symbol); + }, + &elements::Internal::Global(global_idx) => { + let symbol = resolve_global(module, global_idx); + if !stop.contains(&symbol) { + fringe.push(symbol); + } + set.insert(symbol); + }, + _ => {} + } + }, + Import(idx) => { + let entry = &module.import_section().expect("Import section to exist").entries()[idx]; + match entry.external() { + &elements::External::Function(type_idx) => { + let type_symbol = Symbol::Type(type_idx as usize); + if !stop.contains(&type_symbol) { + fringe.push(type_symbol); + } + set.insert(type_symbol); + }, + _ => {} + } + }, + Function(idx) => { + let body = &module.code_section().expect("Code section to exist").bodies()[idx]; + let mut code_symbols = Vec::new(); + push_code_symbols(module, body.code().elements(), &mut code_symbols); + for symbol in code_symbols.drain(..) { + if !stop.contains(&symbol) { + fringe.push(symbol); + } + set.insert(symbol); + } - let signature = &module.functions_section().expect("Functions section to exist").entries()[idx]; - let type_symbol = Symbol::Type(signature.type_ref() as usize); - if !stop.contains(&type_symbol) { - fringe.push(type_symbol); - } - set.insert(type_symbol); - }, - Global(idx) => { - let entry = &module.global_section().expect("Global section to exist").entries()[idx]; - let mut code_symbols = Vec::new(); - push_code_symbols(module, entry.init_expr().code(), &mut code_symbols); - for symbol in code_symbols.drain(..) { - if !stop.contains(&symbol) { - fringe.push(symbol); - } - set.insert(symbol); - } - } - _ => {} - } + let signature = &module.functions_section().expect("Functions section to exist").entries()[idx]; + let type_symbol = Symbol::Type(signature.type_ref() as usize); + if !stop.contains(&type_symbol) { + fringe.push(type_symbol); + } + set.insert(type_symbol); + }, + Global(idx) => { + let entry = &module.global_section().expect("Global section to exist").entries()[idx]; + let mut code_symbols = Vec::new(); + push_code_symbols(module, entry.init_expr().code(), &mut code_symbols); + for symbol in code_symbols.drain(..) { + if !stop.contains(&symbol) { + fringe.push(symbol); + } + set.insert(symbol); + } + } + _ => {} + } - stop.insert(next); - } + stop.insert(next); + } } \ No newline at end of file