From ee0006731e04064da3c7d023e898d2bc8287198a Mon Sep 17 00:00:00 2001 From: Yury Delendik Date: Mon, 6 May 2019 18:55:38 -0500 Subject: [PATCH] Track original wasm instructions locations. --- crates/fuzz/src/lib.rs | 1 + examples/round-trip.rs | 2 +- src/emit.rs | 1 + src/function_builder.rs | 3 +- src/module/config.rs | 9 + src/module/functions/local_function/emit.rs | 9 + src/module/functions/local_function/mod.rs | 224 ++++++++++++-------- src/module/functions/mod.rs | 60 +++++- src/module/mod.rs | 29 ++- 9 files changed, 240 insertions(+), 98 deletions(-) diff --git a/crates/fuzz/src/lib.rs b/crates/fuzz/src/lib.rs index 3b56ff5c..2769b3b7 100755 --- a/crates/fuzz/src/lib.rs +++ b/crates/fuzz/src/lib.rs @@ -76,6 +76,7 @@ impl Config { .unwrap() .emit_wasm() .unwrap() + .into() } fn run_one(&self, wat: &str) -> Option { diff --git a/examples/round-trip.rs b/examples/round-trip.rs index c347e939..8808d615 100644 --- a/examples/round-trip.rs +++ b/examples/round-trip.rs @@ -4,7 +4,7 @@ fn main() { env_logger::init(); let a = std::env::args().nth(1).unwrap(); let m = walrus::Module::from_file(&a).unwrap(); - let wasm = m.emit_wasm().unwrap(); + let walrus::EmitResult { wasm, .. } = m.emit_wasm().unwrap(); if let Some(destination) = std::env::args().nth(2) { std::fs::write(destination, wasm).unwrap(); } diff --git a/src/emit.rs b/src/emit.rs index 4fabc98c..9585942f 100644 --- a/src/emit.rs +++ b/src/emit.rs @@ -15,6 +15,7 @@ pub struct EmitContext<'a> { pub indices: &'a mut IdsToIndices, pub encoder: Encoder<'a>, pub locals: IdHashMap>, + pub code_transform: Vec<(usize, usize)>, } pub struct SubContext<'a, 'cx> { diff --git a/src/function_builder.rs b/src/function_builder.rs index ee33863c..6f7c6026 100644 --- a/src/function_builder.rs +++ b/src/function_builder.rs @@ -1,4 +1,5 @@ use crate::ir::*; +use crate::map::IdHashMap; use crate::tombstone_arena::TombstoneArena; use crate::{FunctionId, LocalFunction, Module, TypeId, ValType}; use crate::{ModuleFunctions, ModuleTypes}; @@ -141,7 +142,7 @@ impl FunctionBuilder { results: ty.results().to_vec().into_boxed_slice(), exprs, }); - let func = LocalFunction::new(ty_id, args, self, entry); + let func = LocalFunction::new(ty_id, args, self, entry, IdHashMap::default()); funcs.add_local(func) } } diff --git a/src/module/config.rs b/src/module/config.rs index a0dd30e8..c4240ff5 100644 --- a/src/module/config.rs +++ b/src/module/config.rs @@ -11,6 +11,7 @@ pub struct ModuleConfig { pub(crate) skip_strict_validate: bool, pub(crate) skip_producers_section: bool, pub(crate) skip_name_section: bool, + pub(crate) preserve_code_transform: bool, } impl ModuleConfig { @@ -97,6 +98,14 @@ impl ModuleConfig { self } + /// Sets a flag to whether code transform is preverved during parsing. + /// + /// By default this flag is `false`. + pub fn preserve_code_transform(&mut self, preserve: bool) -> &mut ModuleConfig { + self.preserve_code_transform = preserve; + self + } + /// Parses an in-memory WebAssembly file into a `Module` using this /// configuration. pub fn parse(&self, wasm: &[u8]) -> Result { diff --git a/src/module/functions/local_function/emit.rs b/src/module/functions/local_function/emit.rs index 545d8041..f4bd9b7c 100644 --- a/src/module/functions/local_function/emit.rs +++ b/src/module/functions/local_function/emit.rs @@ -11,6 +11,7 @@ pub(crate) fn run( indices: &IdsToIndices, local_indices: &IdHashMap, encoder: &mut Encoder, + map: Option<&mut IdHashMap>, ) { let mut v = Emit { func, @@ -19,6 +20,7 @@ pub(crate) fn run( blocks: vec![], encoder, local_indices, + map, }; v.visit(func.entry_block()); } @@ -40,6 +42,9 @@ struct Emit<'a, 'b> { // The instruction sequence we are building up to emit. encoder: &'a mut Encoder<'b>, + + // Encoded ExprId -> offset map. + map: Option<&'a mut IdHashMap>, } impl Emit<'_, '_> { @@ -56,6 +61,10 @@ impl Emit<'_, '_> { let old = self.id; self.id = id; + if let Some(map) = self.map.as_mut() { + map.insert(old, self.encoder.pos()); + } + match self.func.get(id) { Const(e) => e.value.emit(self.encoder), Block(e) => self.visit_block(e), diff --git a/src/module/functions/local_function/mod.rs b/src/module/functions/local_function/mod.rs index 6fb35c7b..ad3d4773 100644 --- a/src/module/functions/local_function/mod.rs +++ b/src/module/functions/local_function/mod.rs @@ -35,9 +35,10 @@ pub struct LocalFunction { /// The entry block for this function. Always `Some` after the constructor /// returns. entry: Option, - // - // TODO: provenance: ExprId -> offset in code section of the original - // instruction. This will be necessary for preserving debug info. + + /// Stores the ExprId -> offset map in code section of the original + /// instruction. This will be necessary for preserving debug info. + pub offsets: IdHashMap, } impl LocalFunction { @@ -47,12 +48,14 @@ impl LocalFunction { args: Vec, exprs: FunctionBuilder, entry: BlockId, + offsets: IdHashMap, ) -> LocalFunction { LocalFunction { ty, args, entry: Some(entry), exprs, + offsets, } } @@ -66,13 +69,15 @@ impl LocalFunction { id: FunctionId, ty: TypeId, args: Vec, - body: wasmparser::OperatorsReader, + preserve_code_transform: bool, + mut body: wasmparser::OperatorsReader, ) -> Result { let mut func = LocalFunction { ty, exprs: FunctionBuilder::new(), args, entry: None, + offsets: IdHashMap::default(), }; let result: Vec<_> = module.types.get(ty).results().iter().cloned().collect(); @@ -86,9 +91,17 @@ impl LocalFunction { let entry = ctx.push_control(BlockKind::FunctionEntry, result.clone(), result); ctx.func.entry = Some(entry); - for inst in body { - let inst = inst?; - validate_instruction(&mut ctx, inst)?; + while !body.eof() { + let (inst, pos) = body.read_with_offset()?; + let expr_id = validate_instruction(&mut ctx, inst)?; + if preserve_code_transform { + match expr_id { + Some(id) => { + ctx.func.offsets.insert(id, pos); + } + None => (), + } + } } if !ctx.controls.is_empty() { bail!("function failed to end with `end`"); @@ -266,8 +279,9 @@ impl LocalFunction { indices: &IdsToIndices, local_indices: &IdHashMap, dst: &mut Encoder, + map: Option<&mut IdHashMap>, ) { - emit::run(self, indices, local_indices, dst) + emit::run(self, indices, local_indices, dst, map) } } @@ -349,27 +363,28 @@ impl DotExpr<'_, '_> { } } -fn validate_instruction(ctx: &mut ValidationContext, inst: Operator) -> Result<()> { +fn validate_instruction(ctx: &mut ValidationContext, inst: Operator) -> Result> { use crate::ir::ExtendedLoad::*; use crate::ValType::*; - let const_ = |ctx: &mut ValidationContext, ty, value| { + let const_ = |ctx: &mut ValidationContext, ty, value| -> Option { let expr = ctx.func.alloc(Const { value }); ctx.push_operand(Some(ty), expr); + Some(expr.into()) }; - let one_op = |ctx: &mut ValidationContext, input, output, op| -> Result<()> { + let one_op = |ctx: &mut ValidationContext, input, output, op| -> Result> { let (_, expr) = ctx.pop_operand_expected(Some(input))?; let expr = ctx.func.alloc(Unop { op, expr }); ctx.push_operand(Some(output), expr); - Ok(()) + Ok(Some(expr.into())) }; - let two_ops = |ctx: &mut ValidationContext, lhs, rhs, output, op| -> Result<()> { + let two_ops = |ctx: &mut ValidationContext, lhs, rhs, output, op| -> Result> { let (_, rhs) = ctx.pop_operand_expected(Some(rhs))?; let (_, lhs) = ctx.pop_operand_expected(Some(lhs))?; let expr = ctx.func.alloc(Binop { op, lhs, rhs }); ctx.push_operand(Some(output), expr); - Ok(()) + Ok(Some(expr.into())) }; let binop = |ctx, ty, op| two_ops(ctx, ty, ty, ty, op); @@ -387,7 +402,7 @@ fn validate_instruction(ctx: &mut ValidationContext, inst: Operator) -> Result<( }) }; - let load = |ctx: &mut ValidationContext, arg, ty, kind| -> Result<()> { + let load = |ctx: &mut ValidationContext, arg, ty, kind| -> Result> { let (_, address) = ctx.pop_operand_expected(Some(I32))?; let memory = ctx.indices.get_memory(0)?; let arg = mem_arg(&arg)?; @@ -398,10 +413,10 @@ fn validate_instruction(ctx: &mut ValidationContext, inst: Operator) -> Result<( memory, }); ctx.push_operand(Some(ty), expr); - Ok(()) + Ok(Some(expr.into())) }; - let store = |ctx: &mut ValidationContext, arg, ty, kind| -> Result<()> { + let store = |ctx: &mut ValidationContext, arg, ty, kind| -> Result> { let (_, value) = ctx.pop_operand_expected(Some(ty))?; let (_, address) = ctx.pop_operand_expected(Some(I32))?; let memory = ctx.indices.get_memory(0)?; @@ -414,10 +429,10 @@ fn validate_instruction(ctx: &mut ValidationContext, inst: Operator) -> Result<( value, }); ctx.add_to_current_frame_block(expr); - Ok(()) + Ok(Some(expr.into())) }; - let atomicrmw = |ctx: &mut ValidationContext, arg, ty, op, width| -> Result<()> { + let atomicrmw = |ctx: &mut ValidationContext, arg, ty, op, width| -> Result> { let (_, value) = ctx.pop_operand_expected(Some(ty))?; let (_, address) = ctx.pop_operand_expected(Some(I32))?; let memory = ctx.indices.get_memory(0)?; @@ -431,10 +446,10 @@ fn validate_instruction(ctx: &mut ValidationContext, inst: Operator) -> Result<( width, }); ctx.push_operand(Some(ty), expr); - Ok(()) + Ok(Some(expr.into())) }; - let cmpxchg = |ctx: &mut ValidationContext, arg, ty, width| -> Result<()> { + let cmpxchg = |ctx: &mut ValidationContext, arg, ty, width| -> Result> { let (_, replacement) = ctx.pop_operand_expected(Some(ty))?; let (_, expected) = ctx.pop_operand_expected(Some(ty))?; let (_, address) = ctx.pop_operand_expected(Some(I32))?; @@ -449,10 +464,10 @@ fn validate_instruction(ctx: &mut ValidationContext, inst: Operator) -> Result<( replacement, }); ctx.push_operand(Some(ty), expr); - Ok(()) + Ok(Some(expr.into())) }; - match inst { + let details: Option = match inst { Operator::Call { function_index } => { let func = ctx .indices @@ -464,6 +479,7 @@ fn validate_instruction(ctx: &mut ValidationContext, inst: Operator) -> Result<( args.reverse(); let expr = ctx.func.alloc(Call { func, args }); ctx.push_operands(fun_ty.results(), expr.into()); + Some(expr.into()) } Operator::CallIndirect { index, table_index } => { let type_id = ctx @@ -485,12 +501,14 @@ fn validate_instruction(ctx: &mut ValidationContext, inst: Operator) -> Result<( args, }); ctx.push_operands(ty.results(), expr.into()); + Some(expr.into()) } Operator::GetLocal { local_index } => { let local = ctx.indices.get_local(ctx.func_id, local_index)?; let ty = ctx.module.locals.get(local).ty(); let expr = ctx.func.alloc(LocalGet { local }); ctx.push_operand(Some(ty), expr); + Some(expr.into()) } Operator::SetLocal { local_index } => { let local = ctx.indices.get_local(ctx.func_id, local_index)?; @@ -498,6 +516,7 @@ fn validate_instruction(ctx: &mut ValidationContext, inst: Operator) -> Result<( let (_, value) = ctx.pop_operand_expected(Some(ty))?; let expr = ctx.func.alloc(LocalSet { local, value }); ctx.add_to_current_frame_block(expr); + Some(expr.into()) } Operator::TeeLocal { local_index } => { let local = ctx.indices.get_local(ctx.func_id, local_index)?; @@ -505,6 +524,7 @@ fn validate_instruction(ctx: &mut ValidationContext, inst: Operator) -> Result<( let (_, value) = ctx.pop_operand_expected(Some(ty))?; let expr = ctx.func.alloc(LocalTee { local, value }); ctx.push_operand(Some(ty), expr); + Some(expr.into()) } Operator::GetGlobal { global_index } => { let global = ctx @@ -514,6 +534,7 @@ fn validate_instruction(ctx: &mut ValidationContext, inst: Operator) -> Result<( let ty = ctx.module.globals.get(global).ty; let expr = ctx.func.alloc(GlobalGet { global }); ctx.push_operand(Some(ty), expr); + Some(expr.into()) } Operator::SetGlobal { global_index } => { let global = ctx @@ -524,15 +545,12 @@ fn validate_instruction(ctx: &mut ValidationContext, inst: Operator) -> Result<( let (_, value) = ctx.pop_operand_expected(Some(ty))?; let expr = ctx.func.alloc(GlobalSet { global, value }); ctx.add_to_current_frame_block(expr); + Some(expr.into()) } Operator::I32Const { value } => const_(ctx, I32, Value::I32(value)), Operator::I64Const { value } => const_(ctx, I64, Value::I64(value)), - Operator::F32Const { value } => { - const_(ctx, F32, Value::F32(f32::from_bits(value.bits()))); - } - Operator::F64Const { value } => { - const_(ctx, F64, Value::F64(f64::from_bits(value.bits()))); - } + Operator::F32Const { value } => const_(ctx, F32, Value::F32(f32::from_bits(value.bits()))), + Operator::F64Const { value } => const_(ctx, F64, Value::F64(f64::from_bits(value.bits()))), Operator::V128Const { value } => { let n = value.bytes(); let val = ((n[0] as u128) << 0) @@ -551,7 +569,7 @@ fn validate_instruction(ctx: &mut ValidationContext, inst: Operator) -> Result<( | ((n[13] as u128) << 104) | ((n[14] as u128) << 112) | ((n[15] as u128) << 120); - const_(ctx, V128, Value::V128(val)); + const_(ctx, V128, Value::V128(val)) } Operator::I32Eqz => testop(ctx, I32, UnaryOp::I32Eqz)?, Operator::I32Eq => relop(ctx, I32, BinaryOp::I32Eq)?, @@ -699,6 +717,7 @@ fn validate_instruction(ctx: &mut ValidationContext, inst: Operator) -> Result<( let (_, expr) = ctx.pop_operand()?; let expr = ctx.func.alloc(Drop { expr }); ctx.add_to_current_frame_block(expr); + Some(expr.into()) } Operator::Select => { let (_, condition) = ctx.pop_operand_expected(Some(I32))?; @@ -710,6 +729,7 @@ fn validate_instruction(ctx: &mut ValidationContext, inst: Operator) -> Result<( alternative, }); ctx.push_operand(t2, expr); + Some(expr.into()) } Operator::Return => { let fn_ty = ctx.module.funcs.get(ctx.func_id).ty(); @@ -717,18 +737,22 @@ fn validate_instruction(ctx: &mut ValidationContext, inst: Operator) -> Result<( let values = ctx.pop_operands(expected)?.into_boxed_slice(); let expr = ctx.func.alloc(Return { values }); ctx.unreachable(expr); + Some(expr.into()) } Operator::Unreachable => { let expr = ctx.func.alloc(Unreachable {}); ctx.unreachable(expr); + Some(expr.into()) } Operator::Block { ty } => { let results = ValType::from_block_ty(ty)?; - ctx.push_control(BlockKind::Block, results.clone(), results); + let id = ctx.push_control(BlockKind::Block, results.clone(), results); + Some(id.into()) } Operator::Loop { ty } => { let t = ValType::from_block_ty(ty)?; - ctx.push_control(BlockKind::Loop, vec![].into_boxed_slice(), t); + let id = ctx.push_control(BlockKind::Loop, vec![].into_boxed_slice(), t); + Some(id.into()) } Operator::If { ty } => { let ty = ValType::from_block_ty(ty)?; @@ -740,6 +764,7 @@ fn validate_instruction(ctx: &mut ValidationContext, inst: Operator) -> Result<( consequent, alternative: None, }); + Some(consequent.into()) } Operator::End => { let (results, block) = ctx.pop_control()?; @@ -782,6 +807,7 @@ fn validate_instruction(ctx: &mut ValidationContext, inst: Operator) -> Result<( _ => block.into(), }; ctx.push_operands(&results, id); + Some(block.into()) } Operator::Else => { let (results, consequent) = ctx.pop_control()?; @@ -799,6 +825,7 @@ fn validate_instruction(ctx: &mut ValidationContext, inst: Operator) -> Result<( bail!("`else` without a leading `if`") } last.alternative = Some(alternative); + Some(alternative.into()) } Operator::Br { relative_depth } => { let n = relative_depth as usize; @@ -811,6 +838,7 @@ fn validate_instruction(ctx: &mut ValidationContext, inst: Operator) -> Result<( args, }); ctx.unreachable(expr); + Some(expr.into()) } Operator::BrIf { relative_depth } => { let n = relative_depth as usize; @@ -826,6 +854,7 @@ fn validate_instruction(ctx: &mut ValidationContext, inst: Operator) -> Result<( args, }); ctx.push_operands(&expected, expr.into()); + Some(expr.into()) } Operator::BrTable { table } => { let len = table.len(); @@ -869,6 +898,7 @@ fn validate_instruction(ctx: &mut ValidationContext, inst: Operator) -> Result<( }); ctx.unreachable(expr); + Some(expr.into()) } Operator::MemorySize { reserved } => { @@ -878,6 +908,7 @@ fn validate_instruction(ctx: &mut ValidationContext, inst: Operator) -> Result<( let memory = ctx.indices.get_memory(0)?; let expr = ctx.func.alloc(MemorySize { memory }); ctx.push_operand(Some(I32), expr); + Some(expr.into()) } Operator::MemoryGrow { reserved } => { if reserved != 0 { @@ -887,6 +918,7 @@ fn validate_instruction(ctx: &mut ValidationContext, inst: Operator) -> Result<( let memory = ctx.indices.get_memory(0)?; let expr = ctx.func.alloc(MemoryGrow { memory, pages }); ctx.push_operand(Some(I32), expr); + Some(expr.into()) } Operator::MemoryInit { segment } => { let (_, len) = ctx.pop_operand_expected(Some(I32))?; @@ -902,11 +934,13 @@ fn validate_instruction(ctx: &mut ValidationContext, inst: Operator) -> Result<( data, }); ctx.add_to_current_frame_block(expr); + Some(expr.into()) } Operator::DataDrop { segment } => { let data = ctx.indices.get_data(segment)?; let expr = ctx.func.alloc(DataDrop { data }); ctx.add_to_current_frame_block(expr); + Some(expr.into()) } Operator::MemoryCopy => { let (_, len) = ctx.pop_operand_expected(Some(I32))?; @@ -921,6 +955,7 @@ fn validate_instruction(ctx: &mut ValidationContext, inst: Operator) -> Result<( dst: memory, }); ctx.add_to_current_frame_block(expr); + Some(expr.into()) } Operator::MemoryFill => { let (_, len) = ctx.pop_operand_expected(Some(I32))?; @@ -934,9 +969,10 @@ fn validate_instruction(ctx: &mut ValidationContext, inst: Operator) -> Result<( memory, }); ctx.add_to_current_frame_block(expr); + Some(expr.into()) } - Operator::Nop => {} + Operator::Nop => None, Operator::I32Load { memarg } => load(ctx, memarg, I32, LoadKind::I32 { atomic: false })?, Operator::I64Load { memarg } => load(ctx, memarg, I64, LoadKind::I64 { atomic: false })?, @@ -1065,157 +1101,153 @@ fn validate_instruction(ctx: &mut ValidationContext, inst: Operator) -> Result<( } Operator::I32AtomicRmwAdd { memarg } => { - atomicrmw(ctx, memarg, I32, AtomicOp::Add, AtomicWidth::I32)?; + atomicrmw(ctx, memarg, I32, AtomicOp::Add, AtomicWidth::I32)? } Operator::I64AtomicRmwAdd { memarg } => { - atomicrmw(ctx, memarg, I64, AtomicOp::Add, AtomicWidth::I64)?; + atomicrmw(ctx, memarg, I64, AtomicOp::Add, AtomicWidth::I64)? } Operator::I32AtomicRmw8UAdd { memarg } => { - atomicrmw(ctx, memarg, I32, AtomicOp::Add, AtomicWidth::I32_8)?; + atomicrmw(ctx, memarg, I32, AtomicOp::Add, AtomicWidth::I32_8)? } Operator::I32AtomicRmw16UAdd { memarg } => { - atomicrmw(ctx, memarg, I32, AtomicOp::Add, AtomicWidth::I32_16)?; + atomicrmw(ctx, memarg, I32, AtomicOp::Add, AtomicWidth::I32_16)? } Operator::I64AtomicRmw8UAdd { memarg } => { - atomicrmw(ctx, memarg, I64, AtomicOp::Add, AtomicWidth::I64_8)?; + atomicrmw(ctx, memarg, I64, AtomicOp::Add, AtomicWidth::I64_8)? } Operator::I64AtomicRmw16UAdd { memarg } => { - atomicrmw(ctx, memarg, I64, AtomicOp::Add, AtomicWidth::I64_16)?; + atomicrmw(ctx, memarg, I64, AtomicOp::Add, AtomicWidth::I64_16)? } Operator::I64AtomicRmw32UAdd { memarg } => { - atomicrmw(ctx, memarg, I64, AtomicOp::Add, AtomicWidth::I64_32)?; + atomicrmw(ctx, memarg, I64, AtomicOp::Add, AtomicWidth::I64_32)? } Operator::I32AtomicRmwSub { memarg } => { - atomicrmw(ctx, memarg, I32, AtomicOp::Sub, AtomicWidth::I32)?; + atomicrmw(ctx, memarg, I32, AtomicOp::Sub, AtomicWidth::I32)? } Operator::I64AtomicRmwSub { memarg } => { - atomicrmw(ctx, memarg, I64, AtomicOp::Sub, AtomicWidth::I64)?; + atomicrmw(ctx, memarg, I64, AtomicOp::Sub, AtomicWidth::I64)? } Operator::I32AtomicRmw8USub { memarg } => { - atomicrmw(ctx, memarg, I32, AtomicOp::Sub, AtomicWidth::I32_8)?; + atomicrmw(ctx, memarg, I32, AtomicOp::Sub, AtomicWidth::I32_8)? } Operator::I32AtomicRmw16USub { memarg } => { - atomicrmw(ctx, memarg, I32, AtomicOp::Sub, AtomicWidth::I32_16)?; + atomicrmw(ctx, memarg, I32, AtomicOp::Sub, AtomicWidth::I32_16)? } Operator::I64AtomicRmw8USub { memarg } => { - atomicrmw(ctx, memarg, I64, AtomicOp::Sub, AtomicWidth::I64_8)?; + atomicrmw(ctx, memarg, I64, AtomicOp::Sub, AtomicWidth::I64_8)? } Operator::I64AtomicRmw16USub { memarg } => { - atomicrmw(ctx, memarg, I64, AtomicOp::Sub, AtomicWidth::I64_16)?; + atomicrmw(ctx, memarg, I64, AtomicOp::Sub, AtomicWidth::I64_16)? } Operator::I64AtomicRmw32USub { memarg } => { - atomicrmw(ctx, memarg, I64, AtomicOp::Sub, AtomicWidth::I64_32)?; + atomicrmw(ctx, memarg, I64, AtomicOp::Sub, AtomicWidth::I64_32)? } Operator::I32AtomicRmwAnd { memarg } => { - atomicrmw(ctx, memarg, I32, AtomicOp::And, AtomicWidth::I32)?; + atomicrmw(ctx, memarg, I32, AtomicOp::And, AtomicWidth::I32)? } Operator::I64AtomicRmwAnd { memarg } => { - atomicrmw(ctx, memarg, I64, AtomicOp::And, AtomicWidth::I64)?; + atomicrmw(ctx, memarg, I64, AtomicOp::And, AtomicWidth::I64)? } Operator::I32AtomicRmw8UAnd { memarg } => { - atomicrmw(ctx, memarg, I32, AtomicOp::And, AtomicWidth::I32_8)?; + atomicrmw(ctx, memarg, I32, AtomicOp::And, AtomicWidth::I32_8)? } Operator::I32AtomicRmw16UAnd { memarg } => { - atomicrmw(ctx, memarg, I32, AtomicOp::And, AtomicWidth::I32_16)?; + atomicrmw(ctx, memarg, I32, AtomicOp::And, AtomicWidth::I32_16)? } Operator::I64AtomicRmw8UAnd { memarg } => { - atomicrmw(ctx, memarg, I64, AtomicOp::And, AtomicWidth::I64_8)?; + atomicrmw(ctx, memarg, I64, AtomicOp::And, AtomicWidth::I64_8)? } Operator::I64AtomicRmw16UAnd { memarg } => { - atomicrmw(ctx, memarg, I64, AtomicOp::And, AtomicWidth::I64_16)?; + atomicrmw(ctx, memarg, I64, AtomicOp::And, AtomicWidth::I64_16)? } Operator::I64AtomicRmw32UAnd { memarg } => { - atomicrmw(ctx, memarg, I64, AtomicOp::And, AtomicWidth::I64_32)?; + atomicrmw(ctx, memarg, I64, AtomicOp::And, AtomicWidth::I64_32)? } Operator::I32AtomicRmwOr { memarg } => { - atomicrmw(ctx, memarg, I32, AtomicOp::Or, AtomicWidth::I32)?; + atomicrmw(ctx, memarg, I32, AtomicOp::Or, AtomicWidth::I32)? } Operator::I64AtomicRmwOr { memarg } => { - atomicrmw(ctx, memarg, I64, AtomicOp::Or, AtomicWidth::I64)?; + atomicrmw(ctx, memarg, I64, AtomicOp::Or, AtomicWidth::I64)? } Operator::I32AtomicRmw8UOr { memarg } => { - atomicrmw(ctx, memarg, I32, AtomicOp::Or, AtomicWidth::I32_8)?; + atomicrmw(ctx, memarg, I32, AtomicOp::Or, AtomicWidth::I32_8)? } Operator::I32AtomicRmw16UOr { memarg } => { - atomicrmw(ctx, memarg, I32, AtomicOp::Or, AtomicWidth::I32_16)?; + atomicrmw(ctx, memarg, I32, AtomicOp::Or, AtomicWidth::I32_16)? } Operator::I64AtomicRmw8UOr { memarg } => { - atomicrmw(ctx, memarg, I64, AtomicOp::Or, AtomicWidth::I64_8)?; + atomicrmw(ctx, memarg, I64, AtomicOp::Or, AtomicWidth::I64_8)? } Operator::I64AtomicRmw16UOr { memarg } => { - atomicrmw(ctx, memarg, I64, AtomicOp::Or, AtomicWidth::I64_16)?; + atomicrmw(ctx, memarg, I64, AtomicOp::Or, AtomicWidth::I64_16)? } Operator::I64AtomicRmw32UOr { memarg } => { - atomicrmw(ctx, memarg, I64, AtomicOp::Or, AtomicWidth::I64_32)?; + atomicrmw(ctx, memarg, I64, AtomicOp::Or, AtomicWidth::I64_32)? } Operator::I32AtomicRmwXor { memarg } => { - atomicrmw(ctx, memarg, I32, AtomicOp::Xor, AtomicWidth::I32)?; + atomicrmw(ctx, memarg, I32, AtomicOp::Xor, AtomicWidth::I32)? } Operator::I64AtomicRmwXor { memarg } => { - atomicrmw(ctx, memarg, I64, AtomicOp::Xor, AtomicWidth::I64)?; + atomicrmw(ctx, memarg, I64, AtomicOp::Xor, AtomicWidth::I64)? } Operator::I32AtomicRmw8UXor { memarg } => { - atomicrmw(ctx, memarg, I32, AtomicOp::Xor, AtomicWidth::I32_8)?; + atomicrmw(ctx, memarg, I32, AtomicOp::Xor, AtomicWidth::I32_8)? } Operator::I32AtomicRmw16UXor { memarg } => { - atomicrmw(ctx, memarg, I32, AtomicOp::Xor, AtomicWidth::I32_16)?; + atomicrmw(ctx, memarg, I32, AtomicOp::Xor, AtomicWidth::I32_16)? } Operator::I64AtomicRmw8UXor { memarg } => { - atomicrmw(ctx, memarg, I64, AtomicOp::Xor, AtomicWidth::I64_8)?; + atomicrmw(ctx, memarg, I64, AtomicOp::Xor, AtomicWidth::I64_8)? } Operator::I64AtomicRmw16UXor { memarg } => { - atomicrmw(ctx, memarg, I64, AtomicOp::Xor, AtomicWidth::I64_16)?; + atomicrmw(ctx, memarg, I64, AtomicOp::Xor, AtomicWidth::I64_16)? } Operator::I64AtomicRmw32UXor { memarg } => { - atomicrmw(ctx, memarg, I64, AtomicOp::Xor, AtomicWidth::I64_32)?; + atomicrmw(ctx, memarg, I64, AtomicOp::Xor, AtomicWidth::I64_32)? } Operator::I32AtomicRmwXchg { memarg } => { - atomicrmw(ctx, memarg, I32, AtomicOp::Xchg, AtomicWidth::I32)?; + atomicrmw(ctx, memarg, I32, AtomicOp::Xchg, AtomicWidth::I32)? } Operator::I64AtomicRmwXchg { memarg } => { - atomicrmw(ctx, memarg, I64, AtomicOp::Xchg, AtomicWidth::I64)?; + atomicrmw(ctx, memarg, I64, AtomicOp::Xchg, AtomicWidth::I64)? } Operator::I32AtomicRmw8UXchg { memarg } => { - atomicrmw(ctx, memarg, I32, AtomicOp::Xchg, AtomicWidth::I32_8)?; + atomicrmw(ctx, memarg, I32, AtomicOp::Xchg, AtomicWidth::I32_8)? } Operator::I32AtomicRmw16UXchg { memarg } => { - atomicrmw(ctx, memarg, I32, AtomicOp::Xchg, AtomicWidth::I32_16)?; + atomicrmw(ctx, memarg, I32, AtomicOp::Xchg, AtomicWidth::I32_16)? } Operator::I64AtomicRmw8UXchg { memarg } => { - atomicrmw(ctx, memarg, I64, AtomicOp::Xchg, AtomicWidth::I64_8)?; + atomicrmw(ctx, memarg, I64, AtomicOp::Xchg, AtomicWidth::I64_8)? } Operator::I64AtomicRmw16UXchg { memarg } => { - atomicrmw(ctx, memarg, I64, AtomicOp::Xchg, AtomicWidth::I64_16)?; + atomicrmw(ctx, memarg, I64, AtomicOp::Xchg, AtomicWidth::I64_16)? } Operator::I64AtomicRmw32UXchg { memarg } => { - atomicrmw(ctx, memarg, I64, AtomicOp::Xchg, AtomicWidth::I64_32)?; + atomicrmw(ctx, memarg, I64, AtomicOp::Xchg, AtomicWidth::I64_32)? } - Operator::I32AtomicRmwCmpxchg { memarg } => { - cmpxchg(ctx, memarg, I32, AtomicWidth::I32)?; - } - Operator::I64AtomicRmwCmpxchg { memarg } => { - cmpxchg(ctx, memarg, I64, AtomicWidth::I64)?; - } + Operator::I32AtomicRmwCmpxchg { memarg } => cmpxchg(ctx, memarg, I32, AtomicWidth::I32)?, + Operator::I64AtomicRmwCmpxchg { memarg } => cmpxchg(ctx, memarg, I64, AtomicWidth::I64)?, Operator::I32AtomicRmw8UCmpxchg { memarg } => { - cmpxchg(ctx, memarg, I32, AtomicWidth::I32_8)?; + cmpxchg(ctx, memarg, I32, AtomicWidth::I32_8)? } Operator::I32AtomicRmw16UCmpxchg { memarg } => { - cmpxchg(ctx, memarg, I32, AtomicWidth::I32_16)?; + cmpxchg(ctx, memarg, I32, AtomicWidth::I32_16)? } Operator::I64AtomicRmw8UCmpxchg { memarg } => { - cmpxchg(ctx, memarg, I64, AtomicWidth::I64_8)?; + cmpxchg(ctx, memarg, I64, AtomicWidth::I64_8)? } Operator::I64AtomicRmw16UCmpxchg { memarg } => { - cmpxchg(ctx, memarg, I64, AtomicWidth::I64_16)?; + cmpxchg(ctx, memarg, I64, AtomicWidth::I64_16)? } Operator::I64AtomicRmw32UCmpxchg { memarg } => { - cmpxchg(ctx, memarg, I64, AtomicWidth::I64_32)?; + cmpxchg(ctx, memarg, I64, AtomicWidth::I64_32)? } Operator::Wake { ref memarg } => { let (_, count) = ctx.pop_operand_expected(Some(I32))?; @@ -1228,6 +1260,7 @@ fn validate_instruction(ctx: &mut ValidationContext, inst: Operator) -> Result<( arg: mem_arg(memarg)?, }); ctx.push_operand(Some(I32), expr); + Some(expr.into()) } Operator::I32Wait { ref memarg } | Operator::I64Wait { ref memarg } => { let (ty, sixty_four) = match inst { @@ -1247,6 +1280,7 @@ fn validate_instruction(ctx: &mut ValidationContext, inst: Operator) -> Result<( arg: mem_arg(memarg)?, }); ctx.push_operand(Some(I32), expr); + Some(expr.into()) } Operator::TableGet { table } => { @@ -1254,6 +1288,7 @@ fn validate_instruction(ctx: &mut ValidationContext, inst: Operator) -> Result<( let (_, index) = ctx.pop_operand_expected(Some(I32))?; let expr = ctx.func.alloc(TableGet { table, index }); ctx.push_operand(Some(Anyref), expr); + Some(expr.into()) } Operator::TableSet { table } => { let table = ctx.indices.get_table(table)?; @@ -1269,6 +1304,7 @@ fn validate_instruction(ctx: &mut ValidationContext, inst: Operator) -> Result<( value, }); ctx.add_to_current_frame_block(expr); + Some(expr.into()) } Operator::TableGrow { table } => { let table = ctx.indices.get_table(table)?; @@ -1284,27 +1320,36 @@ fn validate_instruction(ctx: &mut ValidationContext, inst: Operator) -> Result<( value, }); ctx.push_operand(Some(I32), expr); + Some(expr.into()) } Operator::TableSize { table } => { let table = ctx.indices.get_table(table)?; let expr = ctx.func.alloc(TableSize { table }); ctx.push_operand(Some(I32), expr); + Some(expr.into()) } Operator::RefNull => { let expr = ctx.func.alloc(RefNull {}); ctx.push_operand(Some(Anyref), expr); + Some(expr.into()) } Operator::RefIsNull => { let (_, value) = ctx.pop_operand_expected(Some(Anyref))?; let expr = ctx.func.alloc(RefIsNull { value }); ctx.push_operand(Some(I32), expr); + Some(expr.into()) } Operator::V8x16Shuffle { lines } => { let (_, hi) = ctx.pop_operand_expected(Some(V128))?; let (_, lo) = ctx.pop_operand_expected(Some(V128))?; - let expr = ctx.func.alloc(V128Shuffle { indices: lines, lo, hi }); + let expr = ctx.func.alloc(V128Shuffle { + indices: lines, + lo, + hi, + }); ctx.push_operand(Some(V128), expr); + Some(expr.into()) } Operator::I8x16Splat => one_op(ctx, I32, V128, UnaryOp::I8x16Splat)?, @@ -1410,6 +1455,7 @@ fn validate_instruction(ctx: &mut ValidationContext, inst: Operator) -> Result<( let (_, v1) = ctx.pop_operand_expected(Some(V128))?; let expr = ctx.func.alloc(V128Bitselect { mask, v1, v2 }); ctx.push_operand(Some(V128), expr); + Some(expr.into()) } Operator::I8x16Neg => unop(ctx, V128, UnaryOp::I8x16Neg)?, @@ -1502,6 +1548,6 @@ fn validate_instruction(ctx: &mut ValidationContext, inst: Operator) -> Result<( | op @ Operator::TableCopy => { bail!("Have not implemented support for opcode yet: {:?}", op) } - } - Ok(()) + }; + Ok(details) } diff --git a/src/module/functions/mod.rs b/src/module/functions/mod.rs index c5767da7..0bf9dc50 100644 --- a/src/module/functions/mod.rs +++ b/src/module/functions/mod.rs @@ -6,6 +6,8 @@ use crate::dot::Dot; use crate::emit::{Emit, EmitContext, Section}; use crate::encode::Encoder; use crate::error::Result; +use crate::ir::Expr; +use crate::map::IdHashMap; use crate::module::imports::ImportId; use crate::module::Module; use crate::parse::IndicesToIds; @@ -369,12 +371,25 @@ impl Module { bodies.push((id, body, args, ty)); } + let preserve_code_transform = self.config.preserve_code_transform; + // Wasm modules can often have a lot of functions and this operation can // take some time, so parse all function bodies in parallel. let results = bodies .into_par_iter() .map(|(id, body, args, ty)| { - (id, LocalFunction::parse(self, indices, id, ty, args, body)) + ( + id, + LocalFunction::parse( + self, + indices, + id, + ty, + args, + preserve_code_transform, + body, + ), + ) }) .collect::>(); @@ -413,6 +428,30 @@ fn used_local_functions<'a>(cx: &mut EmitContext<'a>) -> Vec<(FunctionId, &'a Lo functions } +fn remap_offsets( + src: &IdHashMap, + dst: &IdHashMap, +) -> Vec<(usize, usize)> { + let mut result = Vec::new(); + for (expr_id, src_offset) in src { + if let Some(dst_offset) = dst.get(expr_id) { + result.push((*src_offset, *dst_offset)); + } + } + result +} + +fn append_code_offsets( + code_transform: &mut Vec<(usize, usize)>, + code_offset: usize, + map: Vec<(usize, usize)>, +) { + for (src, dst) in map { + let dst = dst + code_offset; + code_transform.push((src, dst)); + } +} + impl Emit for ModuleFunctions { fn emit(&self, cx: &mut EmitContext) { log::debug!("emit code section"); @@ -424,6 +463,8 @@ impl Emit for ModuleFunctions { let mut cx = cx.start_section(Section::Code); cx.encoder.usize(functions.len()); + let generate_map = cx.module.config.preserve_code_transform; + // Functions can typically take awhile to serialize, so serialize // everything in parallel. Afterwards we'll actually place all the // functions together. @@ -433,15 +474,26 @@ impl Emit for ModuleFunctions { log::debug!("emit function {:?} {:?}", id, cx.module.funcs.get(id).name); let mut wasm = Vec::new(); let mut encoder = Encoder::new(&mut wasm); + let mut map = if generate_map { + Some(IdHashMap::default()) + } else { + None + }; + let (used_locals, local_indices) = func.emit_locals(cx.module, &mut encoder); - func.emit_instructions(cx.indices, &local_indices, &mut encoder); - (wasm, id, used_locals, local_indices) + func.emit_instructions(cx.indices, &local_indices, &mut encoder, map.as_mut()); + let map = map.map(|m| remap_offsets(&func.offsets, &m)); + (wasm, id, used_locals, local_indices, map) }) .collect::>(); cx.indices.locals.reserve(bytes.len()); - for (wasm, id, used_locals, local_indices) in bytes { + for (wasm, id, used_locals, local_indices, map) in bytes { + let code_offset = cx.encoder.pos(); cx.encoder.bytes(&wasm); + if let Some(map) = map { + append_code_offsets(&mut cx.code_transform, code_offset, map); + } cx.indices.locals.insert(id, local_indices); cx.locals.insert(id, used_locals); } diff --git a/src/module/mod.rs b/src/module/mod.rs index dd1c1ed4..6371a009 100644 --- a/src/module/mod.rs +++ b/src/module/mod.rs @@ -69,6 +69,23 @@ pub struct Module { pub(crate) config: ModuleConfig, } +/// Emitted module information. +#[derive(Debug)] +pub struct EmitResult { + /// The wasm file bytecode. + pub wasm: Vec, + + /// Optional code transform (if module generated from + /// the existing wasm file). + pub code_transform: Vec<(usize, usize)>, +} + +impl Into> for EmitResult { + fn into(self) -> Vec { + self.wasm + } +} + impl Module { /// Create a default, empty module that uses the given configuration. pub fn with_config(config: ModuleConfig) -> Self { @@ -230,13 +247,13 @@ impl Module { where P: AsRef, { - let buffer = self.emit_wasm()?; + let EmitResult { wasm: buffer, .. } = self.emit_wasm()?; fs::write(path, buffer).context("failed to write wasm module")?; Ok(()) } /// Emit this module into an in-memory wasm buffer. - pub fn emit_wasm(&self) -> Result> { + pub fn emit_wasm(&self) -> Result { log::debug!("start emit"); let indices = &mut IdsToIndices::default(); @@ -249,6 +266,7 @@ impl Module { indices, encoder: Encoder::new(&mut wasm), locals: Default::default(), + code_transform: Vec::new(), }; self.types.emit(&mut cx); self.imports.emit(&mut cx); @@ -285,8 +303,13 @@ impl Module { .raw(§ion.data()); } + let code_transform = cx.code_transform; + log::debug!("emission finished"); - Ok(wasm) + Ok(EmitResult { + wasm, + code_transform, + }) } /// Returns an iterator over all functions in this module