From 45a66fa3b8fab211639c729afe352a01b0b77a24 Mon Sep 17 00:00:00 2001 From: Chris Fallin Date: Sat, 13 Nov 2021 16:31:11 -0800 Subject: [PATCH] Initial support for all Wasm MVP opcodes in frontend. Still have a few fuzz failures. --- src/frontend.rs | 116 ++++++++++----- src/ir.rs | 2 + src/op_traits.rs | 376 ++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 456 insertions(+), 38 deletions(-) diff --git a/src/frontend.rs b/src/frontend.rs index 3cda3c8..2df8b73 100644 --- a/src/frontend.rs +++ b/src/frontend.rs @@ -39,12 +39,33 @@ fn handle_payload<'a>( } Payload::ImportSection(mut reader) => { for _ in 0..reader.get_count() { - if let ImportSectionEntryType::Function(sig_idx) = reader.read()?.ty { - module.funcs.push(FuncDecl::Import(sig_idx as SignatureId)); - *next_func += 1; + match reader.read()?.ty { + ImportSectionEntryType::Function(sig_idx) => { + module.funcs.push(FuncDecl::Import(sig_idx as SignatureId)); + *next_func += 1; + } + ImportSectionEntryType::Global(ty) => { + module.globals.push(ty.content_type); + } + ImportSectionEntryType::Table(ty) => { + module.tables.push(ty.element_type); + } + _ => {} } } } + Payload::GlobalSection(mut reader) => { + for _ in 0..reader.get_count() { + let global = reader.read()?; + module.globals.push(global.ty.content_type); + } + } + Payload::TableSection(mut reader) => { + for _ in 0..reader.get_count() { + let table = reader.read()?; + module.tables.push(table.element_type); + } + } Payload::FunctionSection(mut reader) => { for _ in 0..reader.get_count() { let sig_idx = reader.read()? as SignatureId; @@ -104,7 +125,7 @@ struct FunctionBodyBuilder<'a, 'b> { body: &'b mut FunctionBody<'a>, cur_block: Option, ctrl_stack: Vec, - op_stack: Vec, + op_stack: Vec<(Type, ValueId)>, } #[derive(Clone, Debug)] @@ -126,7 +147,7 @@ enum Frame { start_depth: usize, out: BlockId, el: BlockId, - param_values: Vec, + param_values: Vec<(Type, ValueId)>, params: Vec, results: Vec, }, @@ -183,6 +204,18 @@ impl<'a, 'b> FunctionBodyBuilder<'a, 'b> { self.body.blocks[block].params.extend_from_slice(tys); } + fn pop_n(&mut self, n: usize) -> Vec { + self.op_stack + .split_off(n) + .into_iter() + .map(|(_ty, value)| value) + .collect() + } + + fn pop_1(&mut self) -> ValueId { + self.op_stack.pop().unwrap().1 + } + fn handle_op(&mut self, op: Operator<'a>) -> Result<()> { match &op { Operator::Unreachable @@ -364,7 +397,7 @@ impl<'a, 'b> FunctionBodyBuilder<'a, 'b> { Operator::Nop => {} Operator::Drop => { - self.op_stack.pop().unwrap(); + let _ = self.pop_1(); } Operator::End => match self.ctrl_stack.pop() { @@ -385,7 +418,7 @@ impl<'a, 'b> FunctionBodyBuilder<'a, 'b> { }) => { // Generate a branch to the out-block with // blockparams for the results. - let result_values = self.op_stack.split_off(results.len()); + let result_values = self.pop_n(results.len()); self.emit_branch(out, &result_values[..]); assert_eq!(self.op_stack.len(), start_depth); self.cur_block = Some(out); @@ -401,13 +434,17 @@ impl<'a, 'b> FunctionBodyBuilder<'a, 'b> { }) => { // Generate a branch to the out-block with // blockparams for the results. - let result_values = self.op_stack.split_off(results.len()); + let result_values = self.pop_n(results.len()); self.emit_branch(out, &result_values[..]); // No `else`, so we need to generate a trivial // branch in the else-block. If the if-block-type // has results, they must be exactly the params. let else_result_values = param_values; assert_eq!(else_result_values.len(), results.len()); + let else_result_values = else_result_values + .iter() + .map(|(_ty, value)| *value) + .collect::>(); self.emit_branch(el, &else_result_values[..]); assert_eq!(self.op_stack.len(), start_depth); self.cur_block = Some(out); @@ -421,7 +458,7 @@ impl<'a, 'b> FunctionBodyBuilder<'a, 'b> { }) => { // Generate a branch to the out-block with // blockparams for the results. - let result_values = self.op_stack.split_off(results.len()); + let result_values = self.pop_n(results.len()); assert_eq!(self.op_stack.len(), start_depth); self.emit_branch(out, &result_values[..]); self.cur_block = Some(out); @@ -446,7 +483,7 @@ impl<'a, 'b> FunctionBodyBuilder<'a, 'b> { let (params, results) = self.block_params_and_results(*ty); let header = self.create_block(); self.add_block_params(header, ¶ms[..]); - let initial_args = self.op_stack.split_off(params.len()); + let initial_args = self.pop_n(params.len()); let start_depth = self.op_stack.len(); self.emit_branch(header, &initial_args[..]); self.cur_block = Some(header); @@ -467,7 +504,7 @@ impl<'a, 'b> FunctionBodyBuilder<'a, 'b> { let if_false = self.create_block(); let join = self.create_block(); self.add_block_params(join, &results[..]); - let cond = self.op_stack.pop().unwrap(); + let cond = self.pop_1(); let param_values = self.op_stack[self.op_stack.len() - params.len()..].to_vec(); let start_depth = self.op_stack.len(); self.ctrl_stack.push(Frame::If { @@ -492,7 +529,7 @@ impl<'a, 'b> FunctionBodyBuilder<'a, 'b> { results, } = self.ctrl_stack.pop().unwrap() { - let if_results = self.op_stack.split_off(results.len()); + let if_results = self.pop_n(results.len()); self.emit_branch(out, &if_results[..]); self.op_stack.extend(param_values); self.ctrl_stack.push(Frame::Else { @@ -510,13 +547,13 @@ impl<'a, 'b> FunctionBodyBuilder<'a, 'b> { Operator::Br { relative_depth } | Operator::BrIf { relative_depth } => { let cond = match &op { Operator::Br { .. } => None, - Operator::BrIf { .. } => Some(self.op_stack.pop().unwrap()), + Operator::BrIf { .. } => Some(self.pop_1()), _ => unreachable!(), }; // Get the frame we're branching to. let frame = self.relative_frame(*relative_depth).clone(); // Get the args off the stack. - let args = self.op_stack.split_off(frame.br_args().len()); + let args = self.pop_n(frame.br_args().len()); // Finally, generate the branch itself. match cond { None => { @@ -533,14 +570,14 @@ impl<'a, 'b> FunctionBodyBuilder<'a, 'b> { Operator::BrTable { table } => { // Get the selector index. - let index = self.op_stack.pop().unwrap(); + let index = self.pop_1(); // Get the signature of the default frame; this tells // us the signature of all frames (since wasmparser // validates the input for us). Pop that many args. let default_frame = self.relative_frame(table.default()); let default_term_target = default_frame.br_target(); let arg_len = default_frame.br_args().len(); - let args = self.op_stack.split_off(arg_len); + let args = self.pop_n(arg_len); // Generate a branch terminator with the same args for // every branch target. let mut term_targets = vec![]; @@ -556,9 +593,7 @@ impl<'a, 'b> FunctionBodyBuilder<'a, 'b> { } Operator::Return => { - let retvals = self - .op_stack - .split_off(self.module.signatures[self.my_sig].returns.len()); + let retvals = self.pop_n(self.module.signatures[self.my_sig].returns.len()); self.emit_ret(&retvals[..]); self.cur_block = None; } @@ -677,7 +712,7 @@ impl<'a, 'b> FunctionBodyBuilder<'a, 'b> { kind: ValueKind::BlockParam(self.cur_block.unwrap(), i), ty, }); - self.op_stack.push(value_id); + self.op_stack.push((ty, value_id)); } } @@ -686,25 +721,33 @@ impl<'a, 'b> FunctionBodyBuilder<'a, 'b> { let inst = self.body.blocks[block].insts.len() as InstId; let mut inputs = vec![]; - for input in op_inputs(self.module, self.my_sig, &self.body.locals[..], &op)? - .into_iter() - .rev() + for input in op_inputs( + self.module, + self.my_sig, + &self.body.locals[..], + &self.op_stack[..], + &op, + )? + .into_iter() + .rev() { - let stack_top = self.op_stack.pop().unwrap(); - assert_eq!(self.body.values[stack_top].ty, input); + let (stack_top_ty, stack_top) = self.op_stack.pop().unwrap(); + assert_eq!(stack_top_ty, input); inputs.push(Operand::value(stack_top)); } inputs.reverse(); let mut outputs = vec![]; - for output in op_outputs(self.module, &self.body.locals[..], &op)?.into_iter() { + for output_ty in + op_outputs(self.module, &self.body.locals[..], &self.op_stack[..], &op)?.into_iter() + { let val = self.body.values.len() as ValueId; outputs.push(val); self.body.values.push(ValueDef { kind: ValueKind::Inst(block, inst), - ty: output, + ty: output_ty, }); - self.op_stack.push(val); + self.op_stack.push((output_ty, val)); } self.body.blocks[block].insts.push(Inst { @@ -713,11 +756,18 @@ impl<'a, 'b> FunctionBodyBuilder<'a, 'b> { inputs, }); } else { - let _ = self - .op_stack - .split_off(op_inputs(self.module, self.my_sig, &self.body.locals[..], &op)?.len()); - for _ in 0..op_outputs(self.module, &self.body.locals[..], &op)?.len() { - self.op_stack.push(NO_VALUE); + let _ = self.pop_n( + op_inputs( + self.module, + self.my_sig, + &self.body.locals[..], + &self.op_stack[..], + &op, + )? + .len(), + ); + for ty in op_outputs(self.module, &self.body.locals[..], &self.op_stack[..], &op)? { + self.op_stack.push((ty, NO_VALUE)); } } diff --git a/src/ir.rs b/src/ir.rs index fa7576e..b60425f 100644 --- a/src/ir.rs +++ b/src/ir.rs @@ -14,6 +14,8 @@ pub const NO_VALUE: ValueId = usize::MAX; pub struct Module<'a> { pub funcs: Vec>, pub signatures: Vec, + pub globals: Vec, + pub tables: Vec, } #[derive(Clone, Debug)] diff --git a/src/op_traits.rs b/src/op_traits.rs index d3d93d9..e162d65 100644 --- a/src/op_traits.rs +++ b/src/op_traits.rs @@ -1,6 +1,6 @@ //! Metadata on operators. -use crate::ir::{Module, SignatureId}; +use crate::ir::{Module, SignatureId, ValueId}; use anyhow::{bail, Result}; use wasmparser::{Operator, Type}; @@ -8,6 +8,7 @@ pub fn op_inputs( module: &Module, my_sig: SignatureId, my_locals: &[Type], + op_stack: &[(Type, ValueId)], op: &Operator<'_>, ) -> Result> { match op { @@ -29,14 +30,201 @@ pub fn op_inputs( } &Operator::LocalGet { .. } => Ok(vec![]), - &Operator::I32Eqz => Ok(vec![Type::I32]), - &Operator::I32Eq => Ok(vec![Type::I32, Type::I32]), + &Operator::Select => { + let val_ty = op_stack[op_stack.len() - 2].0; + Ok(vec![val_ty, val_ty, Type::I32]) + } + &Operator::TypedSelect { ty } => Ok(vec![ty, ty, Type::I32]), + + &Operator::GlobalGet { .. } => Ok(vec![]), + &Operator::GlobalSet { global_index } => Ok(vec![module.globals[global_index as usize]]), + + Operator::I32Load { .. } + | Operator::I64Load { .. } + | Operator::F32Load { .. } + | Operator::F64Load { .. } + | Operator::I32Load8S { .. } + | Operator::I32Load8U { .. } + | Operator::I32Load16S { .. } + | Operator::I32Load16U { .. } + | Operator::I64Load8S { .. } + | Operator::I64Load8U { .. } + | Operator::I64Load16S { .. } + | Operator::I64Load16U { .. } + | Operator::I64Load32S { .. } + | Operator::I64Load32U { .. } => Ok(vec![Type::I32]), + + Operator::I32Store { .. } => Ok(vec![Type::I32, Type::I32]), + Operator::I64Store { .. } => Ok(vec![Type::I32, Type::I64]), + Operator::F32Store { .. } => Ok(vec![Type::I32, Type::F32]), + Operator::F64Store { .. } => Ok(vec![Type::I32, Type::F64]), + Operator::I32Store8 { .. } => Ok(vec![Type::I32, Type::I32]), + Operator::I32Store16 { .. } => Ok(vec![Type::I32, Type::I32]), + Operator::I64Store8 { .. } => Ok(vec![Type::I32, Type::I64]), + Operator::I64Store16 { .. } => Ok(vec![Type::I32, Type::I64]), + Operator::I64Store32 { .. } => Ok(vec![Type::I32, Type::I64]), + + Operator::I32Const { .. } + | Operator::I64Const { .. } + | Operator::F32Const { .. } + | Operator::F64Const { .. } => Ok(vec![]), + + Operator::I32Eqz => Ok(vec![Type::I32]), + Operator::I32Eq + | Operator::I32Ne + | Operator::I32LtS + | Operator::I32LtU + | Operator::I32GtS + | Operator::I32GtU + | Operator::I32LeS + | Operator::I32LeU + | Operator::I32GeS + | Operator::I32GeU => Ok(vec![Type::I32, Type::I32]), + + Operator::I64Eqz => Ok(vec![Type::I64]), + + Operator::I64Eq + | Operator::I64Ne + | Operator::I64LtS + | Operator::I64LtU + | Operator::I64GtU + | Operator::I64GtS + | Operator::I64LeS + | Operator::I64LeU + | Operator::I64GeS + | Operator::I64GeU => Ok(vec![Type::I64, Type::I64]), + + Operator::F32Eq + | Operator::F32Ne + | Operator::F32Lt + | Operator::F32Gt + | Operator::F32Le + | Operator::F32Ge => Ok(vec![Type::F32, Type::F32]), + + Operator::F64Eq + | Operator::F64Ne + | Operator::F64Lt + | Operator::F64Gt + | Operator::F64Le + | Operator::F64Ge => Ok(vec![Type::F64, Type::F64]), + + Operator::I32Clz | Operator::I32Ctz | Operator::I32Popcnt => Ok(vec![Type::I32]), + + Operator::I32Add + | Operator::I32Sub + | Operator::I32Mul + | Operator::I32DivS + | Operator::I32DivU + | Operator::I32RemS + | Operator::I32RemU + | Operator::I32And + | Operator::I32Or + | Operator::I32Xor + | Operator::I32Shl + | Operator::I32ShrS + | Operator::I32ShrU + | Operator::I32Rotl + | Operator::I32Rotr => Ok(vec![Type::I32, Type::I32]), + + Operator::I64Clz | Operator::I64Ctz | Operator::I64Popcnt => Ok(vec![Type::I64]), + + Operator::I64Add + | Operator::I64Sub + | Operator::I64Mul + | Operator::I64DivS + | Operator::I64DivU + | Operator::I64RemS + | Operator::I64RemU + | Operator::I64And + | Operator::I64Or + | Operator::I64Xor + | Operator::I64Shl + | Operator::I64ShrS + | Operator::I64ShrU + | Operator::I64Rotl + | Operator::I64Rotr => Ok(vec![Type::I64, Type::I64]), + + Operator::F32Abs + | Operator::F32Neg + | Operator::F32Ceil + | Operator::F32Floor + | Operator::F32Trunc + | Operator::F32Nearest + | Operator::F32Sqrt => Ok(vec![Type::F32]), + + Operator::F32Add + | Operator::F32Sub + | Operator::F32Mul + | Operator::F32Div + | Operator::F32Min + | Operator::F32Max + | Operator::F32Copysign => Ok(vec![Type::F32, Type::F32]), + + Operator::F64Abs + | Operator::F64Neg + | Operator::F64Ceil + | Operator::F64Floor + | Operator::F64Trunc + | Operator::F64Nearest + | Operator::F64Sqrt => Ok(vec![Type::F64]), + + Operator::F64Add + | Operator::F64Sub + | Operator::F64Mul + | Operator::F64Div + | Operator::F64Min + | Operator::F64Max + | Operator::F64Copysign => Ok(vec![Type::F64, Type::F64]), + + Operator::I32WrapI64 => Ok(vec![Type::I64]), + Operator::I32TruncF32S => Ok(vec![Type::F32]), + Operator::I32TruncF32U => Ok(vec![Type::F32]), + Operator::I32TruncF64S => Ok(vec![Type::F64]), + Operator::I32TruncF64U => Ok(vec![Type::F64]), + Operator::I64ExtendI32S => Ok(vec![Type::I32]), + Operator::I64ExtendI32U => Ok(vec![Type::I32]), + Operator::I64TruncF32S => Ok(vec![Type::F32]), + Operator::I64TruncF32U => Ok(vec![Type::F32]), + Operator::I64TruncF64S => Ok(vec![Type::F64]), + Operator::I64TruncF64U => Ok(vec![Type::F64]), + Operator::F32ConvertI32S => Ok(vec![Type::I32]), + Operator::F32ConvertI32U => Ok(vec![Type::I32]), + Operator::F32ConvertI64S => Ok(vec![Type::I64]), + Operator::F32ConvertI64U => Ok(vec![Type::I64]), + Operator::F32DemoteF64 => Ok(vec![Type::F64]), + Operator::F64ConvertI32S => Ok(vec![Type::I32]), + Operator::F64ConvertI32U => Ok(vec![Type::I32]), + Operator::F64ConvertI64S => Ok(vec![Type::I64]), + Operator::F64ConvertI64U => Ok(vec![Type::I64]), + Operator::F64PromoteF32 => Ok(vec![Type::F32]), + Operator::I32Extend8S => Ok(vec![Type::I32]), + Operator::I32Extend16S => Ok(vec![Type::I32]), + Operator::I64Extend8S => Ok(vec![Type::I64]), + Operator::I64Extend16S => Ok(vec![Type::I64]), + Operator::I64Extend32S => Ok(vec![Type::I64]), + Operator::I32TruncSatF32S => Ok(vec![Type::F32]), + Operator::I32TruncSatF32U => Ok(vec![Type::F32]), + Operator::I32TruncSatF64S => Ok(vec![Type::F64]), + Operator::I32TruncSatF64U => Ok(vec![Type::F64]), + Operator::I64TruncSatF32S => Ok(vec![Type::F32]), + Operator::I64TruncSatF32U => Ok(vec![Type::F32]), + Operator::I64TruncSatF64S => Ok(vec![Type::F64]), + Operator::I64TruncSatF64U => Ok(vec![Type::F64]), + Operator::TableGet { .. } => Ok(vec![Type::I32]), + Operator::TableSet { table } => Ok(vec![Type::I32, module.tables[*table as usize]]), + Operator::TableGrow { .. } => Ok(vec![Type::I32]), + Operator::TableSize { .. } => Ok(vec![]), _ => bail!("Unknown operator in op_inputs(): {:?}", op), } } -pub fn op_outputs(module: &Module, my_locals: &[Type], op: &Operator<'_>) -> Result> { +pub fn op_outputs( + module: &Module, + my_locals: &[Type], + op_stack: &[(Type, ValueId)], + op: &Operator<'_>, +) -> Result> { match op { &Operator::Unreachable | &Operator::Nop => Ok(vec![]), @@ -51,7 +239,185 @@ pub fn op_outputs(module: &Module, my_locals: &[Type], op: &Operator<'_>) -> Res &Operator::LocalSet { .. } | &Operator::LocalTee { .. } => Ok(vec![]), &Operator::LocalGet { local_index } => Ok(vec![my_locals[local_index as usize]]), - &Operator::I32Eqz | &Operator::I32Eq => Ok(vec![Type::I32]), + &Operator::Select => { + let val_ty = op_stack[op_stack.len() - 2].0; + Ok(vec![val_ty]) + } + &Operator::TypedSelect { ty } => Ok(vec![ty]), + &Operator::GlobalGet { global_index } => Ok(vec![module.globals[global_index as usize]]), + &Operator::GlobalSet { .. } => Ok(vec![]), + + Operator::I32Load { .. } + | Operator::I64Load { .. } + | Operator::F32Load { .. } + | Operator::F64Load { .. } + | Operator::I32Load8S { .. } + | Operator::I32Load8U { .. } + | Operator::I32Load16S { .. } + | Operator::I32Load16U { .. } => Ok(vec![Type::I32]), + Operator::I64Load8S { .. } + | Operator::I64Load8U { .. } + | Operator::I64Load16S { .. } + | Operator::I64Load16U { .. } + | Operator::I64Load32S { .. } + | Operator::I64Load32U { .. } => Ok(vec![Type::I64]), + + Operator::I32Store { .. } => Ok(vec![]), + Operator::I64Store { .. } => Ok(vec![]), + Operator::F32Store { .. } => Ok(vec![]), + Operator::F64Store { .. } => Ok(vec![]), + Operator::I32Store8 { .. } => Ok(vec![]), + Operator::I32Store16 { .. } => Ok(vec![]), + Operator::I64Store8 { .. } => Ok(vec![]), + Operator::I64Store16 { .. } => Ok(vec![]), + Operator::I64Store32 { .. } => Ok(vec![]), + + Operator::I32Const { .. } => Ok(vec![Type::I32]), + Operator::I64Const { .. } => Ok(vec![Type::I64]), + Operator::F32Const { .. } => Ok(vec![Type::F32]), + Operator::F64Const { .. } => Ok(vec![Type::F64]), + + Operator::I32Eqz + | Operator::I32Eq + | Operator::I32Ne + | Operator::I32LtS + | Operator::I32LtU + | Operator::I32GtS + | Operator::I32GtU + | Operator::I32LeS + | Operator::I32LeU + | Operator::I32GeS + | Operator::I32GeU + | Operator::I64Eqz + | Operator::I64Eq + | Operator::I64Ne + | Operator::I64LtS + | Operator::I64LtU + | Operator::I64GtU + | Operator::I64GtS + | Operator::I64LeS + | Operator::I64LeU + | Operator::I64GeS + | Operator::I64GeU + | Operator::F32Eq + | Operator::F32Ne + | Operator::F32Lt + | Operator::F32Gt + | Operator::F32Le + | Operator::F32Ge + | Operator::F64Eq + | Operator::F64Ne + | Operator::F64Lt + | Operator::F64Gt + | Operator::F64Le + | Operator::F64Ge => Ok(vec![Type::I32]), + + Operator::I32Clz + | Operator::I32Ctz + | Operator::I32Popcnt + | Operator::I32Add + | Operator::I32Sub + | Operator::I32Mul + | Operator::I32DivS + | Operator::I32DivU + | Operator::I32RemS + | Operator::I32RemU + | Operator::I32And + | Operator::I32Or + | Operator::I32Xor + | Operator::I32Shl + | Operator::I32ShrS + | Operator::I32ShrU + | Operator::I32Rotl + | Operator::I32Rotr => Ok(vec![Type::I32]), + + Operator::I64Clz + | Operator::I64Ctz + | Operator::I64Popcnt + | Operator::I64Add + | Operator::I64Sub + | Operator::I64Mul + | Operator::I64DivS + | Operator::I64DivU + | Operator::I64RemS + | Operator::I64RemU + | Operator::I64And + | Operator::I64Or + | Operator::I64Xor + | Operator::I64Shl + | Operator::I64ShrS + | Operator::I64ShrU + | Operator::I64Rotl + | Operator::I64Rotr => Ok(vec![Type::I64]), + + Operator::F32Abs + | Operator::F32Neg + | Operator::F32Ceil + | Operator::F32Floor + | Operator::F32Trunc + | Operator::F32Nearest + | Operator::F32Sqrt + | Operator::F32Add + | Operator::F32Sub + | Operator::F32Mul + | Operator::F32Div + | Operator::F32Min + | Operator::F32Max + | Operator::F32Copysign => Ok(vec![Type::F32]), + + Operator::F64Abs + | Operator::F64Neg + | Operator::F64Ceil + | Operator::F64Floor + | Operator::F64Trunc + | Operator::F64Nearest + | Operator::F64Sqrt + | Operator::F64Add + | Operator::F64Sub + | Operator::F64Mul + | Operator::F64Div + | Operator::F64Min + | Operator::F64Max + | Operator::F64Copysign => Ok(vec![Type::F64]), + + Operator::I32WrapI64 => Ok(vec![Type::I32]), + Operator::I32TruncF32S => Ok(vec![Type::I32]), + Operator::I32TruncF32U => Ok(vec![Type::I32]), + Operator::I32TruncF64S => Ok(vec![Type::I32]), + Operator::I32TruncF64U => Ok(vec![Type::I32]), + Operator::I64ExtendI32S => Ok(vec![Type::I64]), + Operator::I64ExtendI32U => Ok(vec![Type::I64]), + Operator::I64TruncF32S => Ok(vec![Type::I64]), + Operator::I64TruncF32U => Ok(vec![Type::I64]), + Operator::I64TruncF64S => Ok(vec![Type::I64]), + Operator::I64TruncF64U => Ok(vec![Type::I64]), + Operator::F32ConvertI32S => Ok(vec![Type::F32]), + Operator::F32ConvertI32U => Ok(vec![Type::F32]), + Operator::F32ConvertI64S => Ok(vec![Type::F32]), + Operator::F32ConvertI64U => Ok(vec![Type::F32]), + Operator::F32DemoteF64 => Ok(vec![Type::F32]), + Operator::F64ConvertI32S => Ok(vec![Type::F64]), + Operator::F64ConvertI32U => Ok(vec![Type::F64]), + Operator::F64ConvertI64S => Ok(vec![Type::F64]), + Operator::F64ConvertI64U => Ok(vec![Type::F64]), + Operator::F64PromoteF32 => Ok(vec![Type::F64]), + Operator::I32Extend8S => Ok(vec![Type::I32]), + Operator::I32Extend16S => Ok(vec![Type::I32]), + Operator::I64Extend8S => Ok(vec![Type::I64]), + Operator::I64Extend16S => Ok(vec![Type::I64]), + Operator::I64Extend32S => Ok(vec![Type::I64]), + Operator::I32TruncSatF32S => Ok(vec![Type::I32]), + Operator::I32TruncSatF32U => Ok(vec![Type::I32]), + Operator::I32TruncSatF64S => Ok(vec![Type::I32]), + Operator::I32TruncSatF64U => Ok(vec![Type::I32]), + Operator::I64TruncSatF32S => Ok(vec![Type::I64]), + Operator::I64TruncSatF32U => Ok(vec![Type::I64]), + Operator::I64TruncSatF64S => Ok(vec![Type::I64]), + Operator::I64TruncSatF64U => Ok(vec![Type::I64]), + Operator::TableGet { table } => Ok(vec![module.tables[*table as usize]]), + Operator::TableSet { .. } => Ok(vec![]), + Operator::TableGrow { .. } => Ok(vec![]), + Operator::TableSize { .. } => Ok(vec![Type::I32]), _ => bail!("Unknown operator in op_outputs(): {:?}", op), }