From 0d35c06ac63889b6f0d438d44962847efebefd8f Mon Sep 17 00:00:00 2001 From: Chris Fallin Date: Thu, 23 Feb 2023 18:40:25 -0800 Subject: [PATCH] WIP interpreter. --- src/bin/waffle-util.rs | 29 ++ src/interp.rs | 880 ++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 3 + src/passes/basic_opt.rs | 182 ++------- 4 files changed, 938 insertions(+), 156 deletions(-) create mode 100644 src/interp.rs diff --git a/src/bin/waffle-util.rs b/src/bin/waffle-util.rs index 0b4b14b..b3260e1 100644 --- a/src/bin/waffle-util.rs +++ b/src/bin/waffle-util.rs @@ -4,6 +4,7 @@ use anyhow::Result; use log::debug; use std::path::PathBuf; use structopt::StructOpt; +use waffle::InterpContext; use waffle::{FrontendOptions, Module}; #[derive(Debug, StructOpt)] @@ -46,6 +47,11 @@ enum Command { #[structopt(help = "Wasm file to produce", short = "o")] output: PathBuf, }, + #[structopt(name = "interp", about = "Interpret Waffle IR from Wasm")] + Interp { + #[structopt(help = "Wasm file to parse", short = "i")] + input: PathBuf, + }, } fn apply_options(opts: &Options, module: &mut Module) -> Result<()> { @@ -87,6 +93,29 @@ fn main() -> Result<()> { let produced = module.to_wasm_bytes()?; std::fs::write(output, &produced[..])?; } + Command::Interp { input } => { + let bytes = std::fs::read(input)?; + debug!("Loaded {} bytes of Wasm data", bytes.len()); + let mut module = Module::from_wasm_bytes(&bytes[..], &options)?; + apply_options(&opts, &mut module)?; + // Ensure all functions are expanded -- this is necessary + // for interpretation. + module.expand_all_funcs()?; + let mut ctx = InterpContext::new(&module); + debug!("Calling start function"); + if let Some(start) = module.start_func { + ctx.call(&module, start, &[]).unwrap(); + } + // Find a function called `_start`, if any. + if let Some(waffle::Export { + kind: waffle::ExportKind::Func(func), + .. + }) = module.exports.iter().find(|e| &e.name == "_start") + { + debug!("Calling _start"); + ctx.call(&module, *func, &[]).unwrap(); + } + } } Ok(()) diff --git a/src/interp.rs b/src/interp.rs new file mode 100644 index 0000000..bbf90f9 --- /dev/null +++ b/src/interp.rs @@ -0,0 +1,880 @@ +//! Waffle IR interpreter. + +use crate::entity::PerEntity; +use crate::ir::*; +use crate::ops::Operator; +use smallvec::{smallvec, SmallVec}; + +use std::collections::HashMap; + +#[derive(Debug, Clone)] +pub struct InterpContext { + memories: PerEntity, + tables: PerEntity, + globals: PerEntity, +} + +impl InterpContext { + pub fn new(module: &Module<'_>) -> Self { + let mut memories = PerEntity::default(); + for (memory, data) in module.memories.entries() { + let mut interp_mem = InterpMemory { + data: vec![0; data.initial_pages * 0x1_0000], + max_pages: data.maximum_pages.unwrap_or(0x1_0000), + }; + for segment in &data.segments { + interp_mem.data[segment.offset..(segment.offset + segment.data.len())] + .copy_from_slice(&segment.data[..]); + } + memories[memory] = interp_mem; + } + + let mut tables = PerEntity::default(); + for (table, data) in module.tables.entries() { + let interp_table = InterpTable { + elements: data.func_elements.clone().unwrap_or(vec![]), + }; + tables[table] = interp_table; + } + + let mut globals = PerEntity::default(); + for (global, data) in module.globals.entries() { + globals[global] = match data.ty { + Type::I32 => ConstVal::I32(data.value.unwrap_or(0) as u32), + Type::I64 => ConstVal::I64(data.value.unwrap_or(0)), + Type::F32 => ConstVal::F32(data.value.unwrap_or(0) as u32), + Type::F64 => ConstVal::F64(data.value.unwrap_or(0)), + _ => unimplemented!(), + }; + } + + InterpContext { + memories, + tables, + globals, + } + } + + pub fn call( + &mut self, + module: &Module<'_>, + func: Func, + args: &[ConstVal], + ) -> Option> { + let body = match &module.funcs[func] { + FuncDecl::Lazy(..) => panic!("Un-expanded function"), + FuncDecl::Import(_, name) => return self.call_import(&name[..], args), + FuncDecl::Body(_, _, body) => body, + }; + + log::trace!( + "Interp: entering func {}:\n{}\n", + func, + body.display_verbose("| ", Some(module)) + ); + log::trace!("args: {:?}", args); + + let mut frame = InterpStackFrame { + func, + cur_block: body.entry, + values: HashMap::new(), + }; + + for (&arg, &(_, blockparam)) in args.iter().zip(body.blocks[body.entry].params.iter()) { + log::trace!("Entry block param {} gets arg value {:?}", blockparam, arg); + frame.values.insert(blockparam, smallvec![arg]); + } + + loop { + log::trace!("Interpreting block {}", frame.cur_block); + for &inst in &body.blocks[frame.cur_block].insts { + log::trace!("Evaluating inst {}", inst); + let result = match &body.values[inst] { + &ValueDef::Alias(_) => smallvec![], + &ValueDef::PickOutput(val, idx, _) => { + let val = body.resolve_alias(val); + smallvec![frame.values.get(&val).unwrap()[idx]] + } + &ValueDef::Operator(Operator::Call { function_index }, ref args, _) => { + let args = args + .iter() + .map(|&arg| { + let arg = body.resolve_alias(arg); + let multivalue = frame.values.get(&arg).unwrap(); + assert_eq!(multivalue.len(), 1); + multivalue[0] + }) + .collect::>(); + self.call(module, function_index, &args[..])? + } + &ValueDef::Operator( + Operator::CallIndirect { table_index, .. }, + ref args, + _, + ) => { + let args = args + .iter() + .map(|&arg| { + let arg = body.resolve_alias(arg); + let multivalue = frame.values.get(&arg).unwrap(); + assert_eq!(multivalue.len(), 1); + multivalue[0] + }) + .collect::>(); + let idx = match args[0] { + ConstVal::I32(x) => x, + _ => panic!(), + }; + let func = self.tables[table_index].elements[idx as usize]; + self.call(module, func, &args[1..])? + } + &ValueDef::Operator(ref op, ref args, _) => { + let args = args + .iter() + .map(|&arg| { + let arg = body.resolve_alias(arg); + let multivalue = frame + .values + .get(&arg) + .ok_or_else(|| format!("Unset SSA value: {}", arg)) + .unwrap(); + assert_eq!(multivalue.len(), 1); + multivalue[0] + }) + .collect::>(); + let result = const_eval(op, &args[..], Some(self))?; + smallvec![result] + } + &ValueDef::None | &ValueDef::Placeholder(..) | &ValueDef::BlockParam(..) => { + unreachable!(); + } + }; + + log::trace!("Inst {} gets result {:?}", inst, result); + frame.values.insert(inst, result); + } + + match &body.blocks[frame.cur_block].terminator { + &Terminator::None => unreachable!(), + &Terminator::Unreachable => return None, + &Terminator::Br { ref target } => { + frame.apply_target(body, target); + } + &Terminator::CondBr { + cond, + ref if_true, + ref if_false, + } => { + let cond = body.resolve_alias(cond); + let cond = frame.values.get(&cond).unwrap(); + let cond = match cond[0] { + ConstVal::I32(x) => x != 0, + _ => unreachable!(), + }; + if cond { + frame.apply_target(body, if_true); + } else { + frame.apply_target(body, if_false); + } + } + &Terminator::Select { + value, + ref targets, + ref default, + } => { + let value = body.resolve_alias(value); + let value = frame.values.get(&value).unwrap(); + let value = match value[0] { + ConstVal::I32(x) => x, + _ => unreachable!(), + } as usize; + if value < targets.len() { + frame.apply_target(body, &targets[value]); + } else { + frame.apply_target(body, default); + } + } + &Terminator::Return { ref values } => { + let values = values + .iter() + .map(|&value| { + let value = body.resolve_alias(value); + frame.values.get(&value).unwrap()[0] + }) + .collect(); + log::trace!("returning from {}: {:?}", func, values); + return Some(values); + } + } + } + } + + fn call_import(&mut self, name: &str, _args: &[ConstVal]) -> Option> { + todo!("call_import: {}", name); + } +} + +#[derive(Debug, Clone, Default)] +pub struct InterpStackFrame { + func: Func, + cur_block: Block, + values: HashMap>, +} + +impl InterpStackFrame { + fn apply_target(&mut self, body: &FunctionBody, target: &BlockTarget) { + // Collect blockparam args. + let args = target + .args + .iter() + .map(|&arg| { + let arg = body.resolve_alias(arg); + self.values.get(&arg).unwrap().clone() + }) + .collect::>(); + log::trace!("taking target {:?} with args {:?}", target, args); + // Set blockparams. + for (arg, &(_, param)) in args + .into_iter() + .zip(body.blocks[target.block].params.iter()) + { + log::trace!("setting blockparam {} to {:?}", param, arg); + self.values.insert(param, arg); + } + // Set current block. + self.cur_block = target.block; + } +} + +#[derive(Debug, Clone, Default)] +pub struct InterpMemory { + data: Vec, + max_pages: usize, +} + +#[derive(Debug, Clone, Default)] +pub struct InterpTable { + elements: Vec, +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default)] +pub enum ConstVal { + I32(u32), + I64(u64), + F32(u32), + F64(u64), + #[default] + None, +} + +pub fn const_eval( + op: &Operator, + vals: &[ConstVal], + ctx: Option<&mut InterpContext>, +) -> Option { + match (op, vals) { + (Operator::I32Const { value }, []) => Some(ConstVal::I32(*value)), + (Operator::I64Const { value }, []) => Some(ConstVal::I64(*value)), + (Operator::F32Const { value }, []) => Some(ConstVal::F32(*value)), + (Operator::F64Const { value }, []) => Some(ConstVal::F64(*value)), + (Operator::I32Eqz, [ConstVal::I32(a)]) => Some(ConstVal::I32(if *a == 0 { 1 } else { 0 })), + (Operator::I32Eq, [ConstVal::I32(a), ConstVal::I32(b)]) => { + Some(ConstVal::I32(if a == b { 1 } else { 0 })) + } + (Operator::I32Ne, [ConstVal::I32(a), ConstVal::I32(b)]) => { + Some(ConstVal::I32(if a != b { 1 } else { 0 })) + } + (Operator::I32LtS, [ConstVal::I32(a), ConstVal::I32(b)]) => { + Some(ConstVal::I32(if (*a as i32) < (*b as i32) { 1 } else { 0 })) + } + (Operator::I32LtU, [ConstVal::I32(a), ConstVal::I32(b)]) => { + Some(ConstVal::I32(if a < b { 1 } else { 0 })) + } + (Operator::I32GtS, [ConstVal::I32(a), ConstVal::I32(b)]) => { + Some(ConstVal::I32(if (*a as i32) > (*b as i32) { 1 } else { 0 })) + } + (Operator::I32GtU, [ConstVal::I32(a), ConstVal::I32(b)]) => { + Some(ConstVal::I32(if a > b { 1 } else { 0 })) + } + (Operator::I32LeS, [ConstVal::I32(a), ConstVal::I32(b)]) => { + Some(ConstVal::I32(if (*a as i32) <= (*b as i32) { + 1 + } else { + 0 + })) + } + (Operator::I32LeU, [ConstVal::I32(a), ConstVal::I32(b)]) => { + Some(ConstVal::I32(if a <= b { 1 } else { 0 })) + } + (Operator::I32GeS, [ConstVal::I32(a), ConstVal::I32(b)]) => { + Some(ConstVal::I32(if (*a as i32) >= (*b as i32) { + 1 + } else { + 0 + })) + } + (Operator::I32GeU, [ConstVal::I32(a), ConstVal::I32(b)]) => { + Some(ConstVal::I32(if a >= b { 1 } else { 0 })) + } + (Operator::I64Eqz, [ConstVal::I64(a)]) => Some(ConstVal::I32(if *a == 0 { 1 } else { 0 })), + (Operator::I64Eq, [ConstVal::I64(a), ConstVal::I64(b)]) => { + Some(ConstVal::I32(if a == b { 1 } else { 0 })) + } + (Operator::I64Ne, [ConstVal::I64(a), ConstVal::I64(b)]) => { + Some(ConstVal::I32(if a != b { 1 } else { 0 })) + } + (Operator::I64LtS, [ConstVal::I64(a), ConstVal::I64(b)]) => { + Some(ConstVal::I32(if (*a as i64) < (*b as i64) { 1 } else { 0 })) + } + (Operator::I64LtU, [ConstVal::I64(a), ConstVal::I64(b)]) => { + Some(ConstVal::I32(if a < b { 1 } else { 0 })) + } + (Operator::I64GtS, [ConstVal::I64(a), ConstVal::I64(b)]) => { + Some(ConstVal::I32(if (*a as i64) > (*b as i64) { 1 } else { 0 })) + } + (Operator::I64GtU, [ConstVal::I64(a), ConstVal::I64(b)]) => { + Some(ConstVal::I32(if a > b { 1 } else { 0 })) + } + (Operator::I64LeS, [ConstVal::I64(a), ConstVal::I64(b)]) => { + Some(ConstVal::I32(if (*a as i64) <= (*b as i64) { + 1 + } else { + 0 + })) + } + (Operator::I64LeU, [ConstVal::I64(a), ConstVal::I64(b)]) => { + Some(ConstVal::I32(if a <= b { 1 } else { 0 })) + } + (Operator::I64GeS, [ConstVal::I64(a), ConstVal::I64(b)]) => { + Some(ConstVal::I32(if (*a as i64) >= (*b as i64) { + 1 + } else { + 0 + })) + } + (Operator::I64GeU, [ConstVal::I64(a), ConstVal::I64(b)]) => { + Some(ConstVal::I32(if a >= b { 1 } else { 0 })) + } + + (Operator::I32Clz, [ConstVal::I32(x)]) => Some(ConstVal::I32(x.leading_zeros())), + (Operator::I32Ctz, [ConstVal::I32(x)]) => Some(ConstVal::I32(x.trailing_zeros())), + (Operator::I32Popcnt, [ConstVal::I32(x)]) => Some(ConstVal::I32(x.count_ones())), + + (Operator::I32Add, [ConstVal::I32(a), ConstVal::I32(b)]) => { + Some(ConstVal::I32(a.wrapping_add(*b))) + } + (Operator::I32Sub, [ConstVal::I32(a), ConstVal::I32(b)]) => { + Some(ConstVal::I32(a.wrapping_sub(*b))) + } + (Operator::I32Mul, [ConstVal::I32(a), ConstVal::I32(b)]) => { + Some(ConstVal::I32(a.wrapping_mul(*b))) + } + (Operator::I32DivU, [ConstVal::I32(a), ConstVal::I32(b)]) => { + Some(ConstVal::I32(a.checked_div(*b)?)) + } + (Operator::I32DivS, [ConstVal::I32(a), ConstVal::I32(b)]) => { + Some(ConstVal::I32((*a as i32).checked_div(*b as i32)? as u32)) + } + (Operator::I32RemU, [ConstVal::I32(a), ConstVal::I32(b)]) => { + Some(ConstVal::I32(a.checked_rem(*b)?)) + } + (Operator::I32RemS, [ConstVal::I32(a), ConstVal::I32(b)]) => { + Some(ConstVal::I32((*a as i32).checked_rem(*b as i32)? as u32)) + } + (Operator::I32And, [ConstVal::I32(a), ConstVal::I32(b)]) => Some(ConstVal::I32(a & b)), + (Operator::I32Or, [ConstVal::I32(a), ConstVal::I32(b)]) => Some(ConstVal::I32(a | b)), + (Operator::I32Xor, [ConstVal::I32(a), ConstVal::I32(b)]) => Some(ConstVal::I32(a ^ b)), + (Operator::I32Shl, [ConstVal::I32(a), ConstVal::I32(b)]) => { + Some(ConstVal::I32(a.wrapping_shl(*b))) + } + (Operator::I32ShrS, [ConstVal::I32(a), ConstVal::I32(b)]) => { + Some(ConstVal::I32((*a as i32).wrapping_shr(*b) as u32)) + } + (Operator::I32ShrU, [ConstVal::I32(a), ConstVal::I32(b)]) => { + Some(ConstVal::I32(a.wrapping_shr(*b))) + } + (Operator::I32Rotl, [ConstVal::I32(a), ConstVal::I32(b)]) => { + Some(ConstVal::I32(a.rotate_left(*b & 0x1f))) + } + (Operator::I32Rotr, [ConstVal::I32(a), ConstVal::I32(b)]) => { + Some(ConstVal::I32(a.rotate_right(*b & 0x1f))) + } + + (Operator::I64Clz, [ConstVal::I64(x)]) => Some(ConstVal::I64(x.leading_zeros() as u64)), + (Operator::I64Ctz, [ConstVal::I64(x)]) => Some(ConstVal::I64(x.trailing_zeros() as u64)), + (Operator::I64Popcnt, [ConstVal::I64(x)]) => Some(ConstVal::I64(x.count_ones() as u64)), + + (Operator::I64Add, [ConstVal::I64(a), ConstVal::I64(b)]) => { + Some(ConstVal::I64(a.wrapping_add(*b))) + } + (Operator::I64Sub, [ConstVal::I64(a), ConstVal::I64(b)]) => { + Some(ConstVal::I64(a.wrapping_sub(*b))) + } + (Operator::I64Mul, [ConstVal::I64(a), ConstVal::I64(b)]) => { + Some(ConstVal::I64(a.wrapping_mul(*b))) + } + (Operator::I64DivU, [ConstVal::I64(a), ConstVal::I64(b)]) => { + Some(ConstVal::I64(a.checked_div(*b)?)) + } + (Operator::I64DivS, [ConstVal::I64(a), ConstVal::I64(b)]) => { + Some(ConstVal::I64((*a as i64).checked_div(*b as i64)? as u64)) + } + (Operator::I64RemU, [ConstVal::I64(a), ConstVal::I64(b)]) => { + Some(ConstVal::I64(a.checked_rem(*b)?)) + } + (Operator::I64RemS, [ConstVal::I64(a), ConstVal::I64(b)]) => { + Some(ConstVal::I64((*a as i64).checked_rem(*b as i64)? as u64)) + } + (Operator::I64And, [ConstVal::I64(a), ConstVal::I64(b)]) => Some(ConstVal::I64(a & b)), + (Operator::I64Or, [ConstVal::I64(a), ConstVal::I64(b)]) => Some(ConstVal::I64(a | b)), + (Operator::I64Xor, [ConstVal::I64(a), ConstVal::I64(b)]) => Some(ConstVal::I64(a ^ b)), + (Operator::I64Shl, [ConstVal::I64(a), ConstVal::I64(b)]) => { + Some(ConstVal::I64(a.wrapping_shl(*b as u32))) + } + (Operator::I64ShrS, [ConstVal::I64(a), ConstVal::I64(b)]) => { + Some(ConstVal::I64((*a as i64).wrapping_shr(*b as u32) as u64)) + } + (Operator::I64ShrU, [ConstVal::I64(a), ConstVal::I64(b)]) => { + Some(ConstVal::I64(a.wrapping_shr(*b as u32))) + } + (Operator::I64Rotl, [ConstVal::I64(a), ConstVal::I64(b)]) => { + Some(ConstVal::I64(a.rotate_left((*b as u32) & 0x3f))) + } + (Operator::I64Rotr, [ConstVal::I64(a), ConstVal::I64(b)]) => { + Some(ConstVal::I64(a.rotate_right((*b as u32) & 0x3f))) + } + + (Operator::F32Abs, [ConstVal::F32(a)]) => { + Some(ConstVal::F32(f32::from_bits(*a).abs().to_bits())) + } + (Operator::F32Neg, [ConstVal::F32(a)]) => { + Some(ConstVal::F32((-f32::from_bits(*a)).to_bits())) + } + (Operator::F32Ceil, [ConstVal::F32(a)]) => { + Some(ConstVal::F32(f32::from_bits(*a).ceil().to_bits())) + } + (Operator::F32Floor, [ConstVal::F32(a)]) => { + Some(ConstVal::F32(f32::from_bits(*a).floor().to_bits())) + } + (Operator::F32Nearest, [ConstVal::F32(a)]) => { + Some(ConstVal::F32(f32::from_bits(*a).round().to_bits())) + } + (Operator::F32Sqrt, [ConstVal::F32(a)]) => { + Some(ConstVal::F32(f32::from_bits(*a).sqrt().to_bits())) + } + (Operator::F32Add, [ConstVal::F32(a), ConstVal::F32(b)]) => Some(ConstVal::F32( + (f32::from_bits(*a) + f32::from_bits(*b)).to_bits(), + )), + (Operator::F32Sub, [ConstVal::F32(a), ConstVal::F32(b)]) => Some(ConstVal::F32( + (f32::from_bits(*a) - f32::from_bits(*b)).to_bits(), + )), + (Operator::F32Mul, [ConstVal::F32(a), ConstVal::F32(b)]) => Some(ConstVal::F32( + (f32::from_bits(*a) * f32::from_bits(*b)).to_bits(), + )), + (Operator::F32Div, [ConstVal::F32(a), ConstVal::F32(b)]) => Some(ConstVal::F32( + (f32::from_bits(*a) / f32::from_bits(*b)).to_bits(), + )), + (Operator::F32Min, [ConstVal::F32(a), ConstVal::F32(b)]) => Some(ConstVal::F32( + f32::min(f32::from_bits(*a), f32::from_bits(*b)).to_bits(), + )), + (Operator::F32Max, [ConstVal::F32(a), ConstVal::F32(b)]) => Some(ConstVal::F32( + f32::max(f32::from_bits(*a), f32::from_bits(*b)).to_bits(), + )), + (Operator::F32Copysign, [ConstVal::F32(a), ConstVal::F32(b)]) => Some(ConstVal::F32( + f32::copysign(f32::from_bits(*a), f32::from_bits(*b)).to_bits(), + )), + + (Operator::F64Abs, [ConstVal::F64(a)]) => { + Some(ConstVal::F64(f64::from_bits(*a).abs().to_bits())) + } + (Operator::F64Neg, [ConstVal::F64(a)]) => { + Some(ConstVal::F64((-f64::from_bits(*a)).to_bits())) + } + (Operator::F64Ceil, [ConstVal::F64(a)]) => { + Some(ConstVal::F64(f64::from_bits(*a).ceil().to_bits())) + } + (Operator::F64Floor, [ConstVal::F64(a)]) => { + Some(ConstVal::F64(f64::from_bits(*a).floor().to_bits())) + } + (Operator::F64Nearest, [ConstVal::F64(a)]) => { + Some(ConstVal::F64(f64::from_bits(*a).round().to_bits())) + } + (Operator::F64Sqrt, [ConstVal::F64(a)]) => { + Some(ConstVal::F64(f64::from_bits(*a).sqrt().to_bits())) + } + (Operator::F64Add, [ConstVal::F64(a), ConstVal::F64(b)]) => Some(ConstVal::F64( + (f64::from_bits(*a) + f64::from_bits(*b)).to_bits(), + )), + (Operator::F64Sub, [ConstVal::F64(a), ConstVal::F64(b)]) => Some(ConstVal::F64( + (f64::from_bits(*a) - f64::from_bits(*b)).to_bits(), + )), + (Operator::F64Mul, [ConstVal::F64(a), ConstVal::F64(b)]) => Some(ConstVal::F64( + (f64::from_bits(*a) * f64::from_bits(*b)).to_bits(), + )), + (Operator::F64Div, [ConstVal::F64(a), ConstVal::F64(b)]) => Some(ConstVal::F64( + (f64::from_bits(*a) / f64::from_bits(*b)).to_bits(), + )), + (Operator::F64Min, [ConstVal::F64(a), ConstVal::F64(b)]) => Some(ConstVal::F64( + f64::min(f64::from_bits(*a), f64::from_bits(*b)).to_bits(), + )), + (Operator::F64Max, [ConstVal::F64(a), ConstVal::F64(b)]) => Some(ConstVal::F64( + f64::max(f64::from_bits(*a), f64::from_bits(*b)).to_bits(), + )), + (Operator::F64Copysign, [ConstVal::F64(a), ConstVal::F64(b)]) => Some(ConstVal::F64( + f64::copysign(f64::from_bits(*a), f64::from_bits(*b)).to_bits(), + )), + + (Operator::I32WrapI64, [ConstVal::I64(a)]) => Some(ConstVal::I32(*a as u32)), + + (Operator::I32TruncF32S, [ConstVal::F32(a)]) => { + let a = f32::from_bits(*a); + if a >= (i32::MIN as f32) && a <= (i32::MAX as f32) { + Some(ConstVal::I32(a as i32 as u32)) + } else { + None + } + } + (Operator::I32TruncF32U, [ConstVal::F32(a)]) => { + let a = f32::from_bits(*a); + if a >= 0.0 && a <= (u32::MAX as f32) { + Some(ConstVal::I32(a as u32)) + } else { + None + } + } + (Operator::I32TruncF64S, [ConstVal::F64(a)]) => { + let a = f64::from_bits(*a); + if a >= (i32::MIN as f64) && a <= (i32::MAX as f64) { + Some(ConstVal::I32(a as i32 as u32)) + } else { + None + } + } + (Operator::I32TruncF64U, [ConstVal::F64(a)]) => { + let a = f64::from_bits(*a); + if a >= 0.0 && a <= (u32::MAX as f64) { + Some(ConstVal::I32(a as u32)) + } else { + None + } + } + + (Operator::I64TruncF32S, [ConstVal::F32(a)]) => { + let a = f32::from_bits(*a); + if a >= (i64::MIN as f32) && a <= (i64::MAX as f32) { + Some(ConstVal::I64(a as i64 as u64)) + } else { + None + } + } + (Operator::I64TruncF32U, [ConstVal::F32(a)]) => { + let a = f32::from_bits(*a); + if a >= 0.0 && a <= (u64::MAX as f32) { + Some(ConstVal::I64(a as u64)) + } else { + None + } + } + (Operator::I64TruncF64S, [ConstVal::F64(a)]) => { + let a = f64::from_bits(*a); + if a >= (i64::MIN as f64) && a <= (i64::MAX as f64) { + Some(ConstVal::I64(a as i64 as u64)) + } else { + None + } + } + (Operator::I64TruncF64U, [ConstVal::F64(a)]) => { + let a = f64::from_bits(*a); + if a >= 0.0 && a <= (u64::MAX as f64) { + Some(ConstVal::I64(a as u64)) + } else { + None + } + } + + (Operator::I32TruncSatF32S, [ConstVal::F32(a)]) => { + let a = f32::from_bits(*a); + Some(ConstVal::I32( + a.min(i32::MAX as f32).max(i32::MIN as f32) as i32 as u32, + )) + } + (Operator::I32TruncSatF32U, [ConstVal::F32(a)]) => { + let a = f32::from_bits(*a); + Some(ConstVal::I32(a.min(u32::MAX as f32) as u32)) + } + (Operator::I32TruncSatF64S, [ConstVal::F64(a)]) => { + let a = f64::from_bits(*a); + Some(ConstVal::I32( + a.min(i32::MAX as f64).max(i32::MIN as f64) as i32 as u32, + )) + } + (Operator::I32TruncSatF64U, [ConstVal::F64(a)]) => { + let a = f64::from_bits(*a); + Some(ConstVal::I32(a.min(u32::MAX as f64) as u32)) + } + + (Operator::I64TruncSatF32S, [ConstVal::F32(a)]) => { + let a = f32::from_bits(*a); + Some(ConstVal::I64( + a.min(i64::MAX as f32).max(i64::MIN as f32) as i64 as u64, + )) + } + (Operator::I64TruncSatF32U, [ConstVal::F32(a)]) => { + let a = f32::from_bits(*a); + Some(ConstVal::I64(a.min(u64::MAX as f32) as u64)) + } + (Operator::I64TruncSatF64S, [ConstVal::F64(a)]) => { + let a = f64::from_bits(*a); + Some(ConstVal::I64( + a.min(i64::MAX as f64).max(i64::MIN as f64) as i64 as u64, + )) + } + (Operator::I64TruncSatF64U, [ConstVal::F64(a)]) => { + let a = f64::from_bits(*a); + Some(ConstVal::I64(a.min(u64::MAX as f64) as u64)) + } + + (Operator::F32ConvertI32S, [ConstVal::I32(a)]) => { + Some(ConstVal::F32((*a as i32 as f32).to_bits())) + } + (Operator::F32ConvertI32U, [ConstVal::I32(a)]) => { + Some(ConstVal::F32((*a as f32).to_bits())) + } + (Operator::F32ConvertI64S, [ConstVal::I64(a)]) => { + Some(ConstVal::F32((*a as i64 as f32).to_bits())) + } + (Operator::F32ConvertI64U, [ConstVal::I64(a)]) => { + Some(ConstVal::F32((*a as f32).to_bits())) + } + + (Operator::F64ConvertI32S, [ConstVal::I32(a)]) => { + Some(ConstVal::F64((*a as i32 as f64).to_bits())) + } + (Operator::F64ConvertI32U, [ConstVal::I32(a)]) => { + Some(ConstVal::F64((*a as f64).to_bits())) + } + (Operator::F64ConvertI64S, [ConstVal::I64(a)]) => { + Some(ConstVal::F64((*a as i64 as f64).to_bits())) + } + (Operator::F64ConvertI64U, [ConstVal::I64(a)]) => { + Some(ConstVal::F64((*a as f64).to_bits())) + } + + (Operator::F32DemoteF64, [ConstVal::F64(a)]) => { + Some(ConstVal::F32((f64::from_bits(*a) as f32).to_bits())) + } + (Operator::F64PromoteF32, [ConstVal::F32(a)]) => { + Some(ConstVal::F64((f32::from_bits(*a) as f64).to_bits())) + } + + (Operator::F32ReinterpretI32, [ConstVal::I32(a)]) => Some(ConstVal::F32(*a)), + (Operator::F64ReinterpretI64, [ConstVal::I64(a)]) => Some(ConstVal::F64(*a)), + (Operator::I32ReinterpretF32, [ConstVal::F32(a)]) => Some(ConstVal::I32(*a)), + (Operator::I64ReinterpretF64, [ConstVal::F64(a)]) => Some(ConstVal::I64(*a)), + + (Operator::I32Extend8S, [ConstVal::I32(a)]) => Some(ConstVal::I32(*a as i8 as i32 as u32)), + (Operator::I32Extend16S, [ConstVal::I32(a)]) => { + Some(ConstVal::I32(*a as i16 as i32 as u32)) + } + (Operator::I64Extend8S, [ConstVal::I64(a)]) => Some(ConstVal::I64(*a as i8 as i64 as u64)), + (Operator::I64Extend16S, [ConstVal::I64(a)]) => { + Some(ConstVal::I64(*a as i16 as i64 as u64)) + } + (Operator::I64Extend32S, [ConstVal::I64(a)]) => { + Some(ConstVal::I64(*a as i32 as i64 as u64)) + } + (Operator::I64ExtendI32S, [ConstVal::I32(a)]) => { + Some(ConstVal::I64(*a as i32 as i64 as u64)) + } + (Operator::I64ExtendI32U, [ConstVal::I32(a)]) => Some(ConstVal::I64(*a as u64)), + + (Operator::Select, [x, y, ConstVal::I32(k)]) => Some(if *k != 0 { *x } else { *y }), + (Operator::TypedSelect { .. }, [x, y, ConstVal::I32(k)]) => { + Some(if *k != 0 { *x } else { *y }) + } + + (Operator::GlobalGet { global_index }, []) => { + ctx.map(|global| global.globals[*global_index]) + } + (Operator::GlobalSet { global_index }, [x]) => ctx.map(|global| { + global.globals[*global_index] = *x; + ConstVal::None + }), + + (Operator::TableGet { .. }, _) + | (Operator::TableSet { .. }, _) + | (Operator::TableGrow { .. }, _) => None, + + (Operator::TableSize { table_index }, []) => { + ctx.map(|global| ConstVal::I32(global.tables[*table_index].elements.len() as u32)) + } + + (Operator::MemorySize { mem }, []) => { + ctx.map(|global| ConstVal::I32((global.memories[*mem].data.len() / 0x1_0000) as u32)) + } + + (Operator::MemoryGrow { mem }, [ConstVal::I32(amount)]) => ctx.and_then(|global| { + let cur_pages = global.memories[*mem].data.len() / 0x1_0000; + let new_pages = cur_pages + (*amount as usize); + if new_pages > global.memories[*mem].max_pages { + None + } else { + global.memories[*mem].data.resize(new_pages * 0x1_0000, 0); + Some(ConstVal::I32(cur_pages as u32)) + } + }), + + (Operator::Nop, []) => Some(ConstVal::None), + (Operator::Unreachable, []) => None, + + (Operator::I32Load { memory }, [ConstVal::I32(addr)]) => ctx.map(|global| { + let addr = *addr + memory.offset; + ConstVal::I32(read_u32(&global.memories[memory.memory], addr)) + }), + (Operator::I64Load { memory }, [ConstVal::I32(addr)]) => ctx.map(|global| { + let addr = *addr + memory.offset; + ConstVal::I64(read_u64(&global.memories[memory.memory], addr)) + }), + (Operator::F32Load { memory }, [ConstVal::I32(addr)]) => ctx.map(|global| { + let addr = *addr + memory.offset; + ConstVal::F32(read_u32(&global.memories[memory.memory], addr)) + }), + (Operator::F64Load { memory }, [ConstVal::I32(addr)]) => ctx.map(|global| { + let addr = *addr + memory.offset; + ConstVal::F64(read_u64(&global.memories[memory.memory], addr)) + }), + (Operator::I32Load8S { memory }, [ConstVal::I32(addr)]) => ctx.map(|global| { + let addr = *addr + memory.offset; + ConstVal::I32(read_u8(&global.memories[memory.memory], addr) as i8 as i32 as u32) + }), + (Operator::I32Load8U { memory }, [ConstVal::I32(addr)]) => ctx.map(|global| { + let addr = *addr + memory.offset; + ConstVal::I32(read_u8(&global.memories[memory.memory], addr) as u32) + }), + (Operator::I32Load16S { memory }, [ConstVal::I32(addr)]) => ctx.map(|global| { + let addr = *addr + memory.offset; + ConstVal::I32(read_u16(&global.memories[memory.memory], addr) as i16 as i32 as u32) + }), + (Operator::I32Load16U { memory }, [ConstVal::I32(addr)]) => ctx.map(|global| { + let addr = *addr + memory.offset; + ConstVal::I32(read_u16(&global.memories[memory.memory], addr) as u32) + }), + (Operator::I64Load8S { memory }, [ConstVal::I32(addr)]) => ctx.map(|global| { + let addr = *addr + memory.offset; + ConstVal::I64(read_u8(&global.memories[memory.memory], addr) as i8 as i64 as u64) + }), + (Operator::I64Load8U { memory }, [ConstVal::I32(addr)]) => ctx.map(|global| { + let addr = *addr + memory.offset; + ConstVal::I64(read_u8(&global.memories[memory.memory], addr) as u64) + }), + (Operator::I64Load16S { memory }, [ConstVal::I32(addr)]) => ctx.map(|global| { + let addr = *addr + memory.offset; + ConstVal::I64(read_u16(&global.memories[memory.memory], addr) as i16 as i64 as u64) + }), + (Operator::I64Load16U { memory }, [ConstVal::I32(addr)]) => ctx.map(|global| { + let addr = *addr + memory.offset; + ConstVal::I64(read_u16(&global.memories[memory.memory], addr) as u64) + }), + (Operator::I64Load32S { memory }, [ConstVal::I32(addr)]) => ctx.map(|global| { + let addr = *addr + memory.offset; + ConstVal::I64(read_u32(&global.memories[memory.memory], addr) as i32 as i64 as u64) + }), + (Operator::I64Load32U { memory }, [ConstVal::I32(addr)]) => ctx.map(|global| { + let addr = *addr + memory.offset; + ConstVal::I64(read_u32(&global.memories[memory.memory], addr) as u64) + }), + (Operator::I32Store { memory }, [ConstVal::I32(addr), ConstVal::I32(data)]) => { + ctx.map(|global| { + let addr = *addr + memory.offset; + write_u32(&mut global.memories[memory.memory], addr, *data); + ConstVal::None + }) + } + (Operator::I64Store { memory }, [ConstVal::I32(addr), ConstVal::I64(data)]) => { + ctx.map(|global| { + let addr = *addr + memory.offset; + write_u64(&mut global.memories[memory.memory], addr, *data); + ConstVal::None + }) + } + (Operator::I32Store8 { memory }, [ConstVal::I32(addr), ConstVal::I32(data)]) => { + ctx.map(|global| { + let addr = *addr + memory.offset; + write_u8(&mut global.memories[memory.memory], addr, *data as u8); + ConstVal::None + }) + } + (Operator::I32Store16 { memory }, [ConstVal::I32(addr), ConstVal::I32(data)]) => { + ctx.map(|global| { + let addr = *addr + memory.offset; + write_u16(&mut global.memories[memory.memory], addr, *data as u16); + ConstVal::None + }) + } + (Operator::I64Store8 { memory }, [ConstVal::I32(addr), ConstVal::I64(data)]) => { + ctx.map(|global| { + let addr = *addr + memory.offset; + write_u8(&mut global.memories[memory.memory], addr, *data as u8); + ConstVal::None + }) + } + (Operator::I64Store16 { memory }, [ConstVal::I32(addr), ConstVal::I64(data)]) => { + ctx.map(|global| { + let addr = *addr + memory.offset; + write_u16(&mut global.memories[memory.memory], addr, *data as u16); + ConstVal::None + }) + } + (Operator::I64Store32 { memory }, [ConstVal::I32(addr), ConstVal::I64(data)]) => { + ctx.map(|global| { + let addr = *addr + memory.offset; + write_u32(&mut global.memories[memory.memory], addr, *data as u32); + ConstVal::None + }) + } + _ => None, + } +} + +fn read_u8(mem: &InterpMemory, addr: u32) -> u8 { + let addr = addr as usize; + mem.data[addr] +} + +fn read_u16(mem: &InterpMemory, addr: u32) -> u16 { + use std::convert::TryInto; + let addr = addr as usize; + u16::from_le_bytes(mem.data[addr..(addr + 2)].try_into().unwrap()) +} + +fn read_u32(mem: &InterpMemory, addr: u32) -> u32 { + use std::convert::TryInto; + let addr = addr as usize; + u32::from_le_bytes(mem.data[addr..(addr + 4)].try_into().unwrap()) +} + +fn read_u64(mem: &InterpMemory, addr: u32) -> u64 { + use std::convert::TryInto; + let addr = addr as usize; + u64::from_le_bytes(mem.data[addr..(addr + 8)].try_into().unwrap()) +} + +fn write_u8(mem: &mut InterpMemory, addr: u32, data: u8) { + let addr = addr as usize; + mem.data[addr] = data; +} + +fn write_u16(mem: &mut InterpMemory, addr: u32, data: u16) { + let addr = addr as usize; + mem.data[addr..(addr + 2)].copy_from_slice(&data.to_le_bytes()[..]); +} + +fn write_u32(mem: &mut InterpMemory, addr: u32, data: u32) { + let addr = addr as usize; + mem.data[addr..(addr + 4)].copy_from_slice(&data.to_le_bytes()[..]); +} + +fn write_u64(mem: &mut InterpMemory, addr: u32, data: u64) { + let addr = addr as usize; + mem.data[addr..(addr + 8)].copy_from_slice(&data.to_le_bytes()[..]); +} diff --git a/src/lib.rs b/src/lib.rs index eb0af2f..cfa9446 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -19,3 +19,6 @@ mod scoped_map; pub use errors::*; pub use ir::*; pub use ops::{Ieee32, Ieee64, Operator}; + +mod interp; +pub use interp::*; diff --git a/src/passes/basic_opt.rs b/src/passes/basic_opt.rs index 7b8b474..dd2e9d5 100644 --- a/src/passes/basic_opt.rs +++ b/src/passes/basic_opt.rs @@ -1,6 +1,7 @@ //! Basic optimizations: GVN and constant-propagation/folding. use crate::cfg::CFGInfo; +use crate::interp::{const_eval, ConstVal}; use crate::ir::*; use crate::passes::dom_pass::{dom_pass, DomtreePass}; use crate::scoped_map::ScopedMap; @@ -33,159 +34,6 @@ fn value_is_pure(value: Value, body: &FunctionBody) -> bool { } } -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] -enum ConstVal { - I32(u32), - I64(u64), - None, -} - -fn const_eval(op: &Operator, vals: &[ConstVal]) -> Option { - match (op, vals) { - (Operator::I32Const { value }, []) => Some(ConstVal::I32(*value as u32)), - (Operator::I64Const { value }, []) => Some(ConstVal::I64(*value as u64)), - (Operator::I32Eqz, [ConstVal::I32(a)]) => Some(ConstVal::I32(if *a == 0 { 1 } else { 0 })), - (Operator::I32Eq, [ConstVal::I32(a), ConstVal::I32(b)]) => { - Some(ConstVal::I32(if a == b { 1 } else { 0 })) - } - (Operator::I32Ne, [ConstVal::I32(a), ConstVal::I32(b)]) => { - Some(ConstVal::I32(if a != b { 1 } else { 0 })) - } - (Operator::I32LtS, [ConstVal::I32(a), ConstVal::I32(b)]) => { - Some(ConstVal::I32(if (*a as i32) < (*b as i32) { 1 } else { 0 })) - } - (Operator::I32LtU, [ConstVal::I32(a), ConstVal::I32(b)]) => { - Some(ConstVal::I32(if a < b { 1 } else { 0 })) - } - (Operator::I32GtS, [ConstVal::I32(a), ConstVal::I32(b)]) => { - Some(ConstVal::I32(if (*a as i32) > (*b as i32) { 1 } else { 0 })) - } - (Operator::I32GtU, [ConstVal::I32(a), ConstVal::I32(b)]) => { - Some(ConstVal::I32(if a > b { 1 } else { 0 })) - } - (Operator::I32LeS, [ConstVal::I32(a), ConstVal::I32(b)]) => { - Some(ConstVal::I32(if (*a as i32) <= (*b as i32) { - 1 - } else { - 0 - })) - } - (Operator::I32LeU, [ConstVal::I32(a), ConstVal::I32(b)]) => { - Some(ConstVal::I32(if a <= b { 1 } else { 0 })) - } - (Operator::I32GeS, [ConstVal::I32(a), ConstVal::I32(b)]) => { - Some(ConstVal::I32(if (*a as i32) >= (*b as i32) { - 1 - } else { - 0 - })) - } - (Operator::I32GeU, [ConstVal::I32(a), ConstVal::I32(b)]) => { - Some(ConstVal::I32(if a >= b { 1 } else { 0 })) - } - (Operator::I64Eqz, [ConstVal::I64(a)]) => Some(ConstVal::I32(if *a == 0 { 1 } else { 0 })), - (Operator::I64Eq, [ConstVal::I64(a), ConstVal::I64(b)]) => { - Some(ConstVal::I32(if a == b { 1 } else { 0 })) - } - (Operator::I64Ne, [ConstVal::I64(a), ConstVal::I64(b)]) => { - Some(ConstVal::I32(if a != b { 1 } else { 0 })) - } - (Operator::I64LtS, [ConstVal::I64(a), ConstVal::I64(b)]) => { - Some(ConstVal::I32(if (*a as i64) < (*b as i64) { 1 } else { 0 })) - } - (Operator::I64LtU, [ConstVal::I64(a), ConstVal::I64(b)]) => { - Some(ConstVal::I32(if a < b { 1 } else { 0 })) - } - (Operator::I64GtS, [ConstVal::I64(a), ConstVal::I64(b)]) => { - Some(ConstVal::I32(if (*a as i64) > (*b as i64) { 1 } else { 0 })) - } - (Operator::I64GtU, [ConstVal::I64(a), ConstVal::I64(b)]) => { - Some(ConstVal::I32(if a > b { 1 } else { 0 })) - } - (Operator::I64LeS, [ConstVal::I64(a), ConstVal::I64(b)]) => { - Some(ConstVal::I32(if (*a as i64) <= (*b as i64) { - 1 - } else { - 0 - })) - } - (Operator::I64LeU, [ConstVal::I64(a), ConstVal::I64(b)]) => { - Some(ConstVal::I32(if a <= b { 1 } else { 0 })) - } - (Operator::I64GeS, [ConstVal::I64(a), ConstVal::I64(b)]) => { - Some(ConstVal::I32(if (*a as i64) >= (*b as i64) { - 1 - } else { - 0 - })) - } - (Operator::I64GeU, [ConstVal::I64(a), ConstVal::I64(b)]) => { - Some(ConstVal::I32(if a >= b { 1 } else { 0 })) - } - - (Operator::I32Add, [ConstVal::I32(a), ConstVal::I32(b)]) => { - Some(ConstVal::I32(a.wrapping_add(*b))) - } - (Operator::I32Sub, [ConstVal::I32(a), ConstVal::I32(b)]) => { - Some(ConstVal::I32(a.wrapping_sub(*b))) - } - (Operator::I32Mul, [ConstVal::I32(a), ConstVal::I32(b)]) => { - Some(ConstVal::I32(a.wrapping_mul(*b))) - } - (Operator::I32And, [ConstVal::I32(a), ConstVal::I32(b)]) => Some(ConstVal::I32(a & b)), - (Operator::I32Or, [ConstVal::I32(a), ConstVal::I32(b)]) => Some(ConstVal::I32(a | b)), - (Operator::I32Xor, [ConstVal::I32(a), ConstVal::I32(b)]) => Some(ConstVal::I32(a ^ b)), - (Operator::I32Shl, [ConstVal::I32(a), ConstVal::I32(b)]) => { - Some(ConstVal::I32(a.wrapping_shl(*b))) - } - (Operator::I32ShrS, [ConstVal::I32(a), ConstVal::I32(b)]) => { - Some(ConstVal::I32((*a as i32).wrapping_shr(*b) as u32)) - } - - (Operator::I32ShrU, [ConstVal::I32(a), ConstVal::I32(b)]) => { - Some(ConstVal::I32(a.wrapping_shr(*b))) - } - - (Operator::I64Add, [ConstVal::I64(a), ConstVal::I64(b)]) => { - Some(ConstVal::I64(a.wrapping_add(*b))) - } - (Operator::I64Sub, [ConstVal::I64(a), ConstVal::I64(b)]) => { - Some(ConstVal::I64(a.wrapping_sub(*b))) - } - (Operator::I64Mul, [ConstVal::I64(a), ConstVal::I64(b)]) => { - Some(ConstVal::I64(a.wrapping_mul(*b))) - } - (Operator::I64And, [ConstVal::I64(a), ConstVal::I64(b)]) => Some(ConstVal::I64(a & b)), - (Operator::I64Or, [ConstVal::I64(a), ConstVal::I64(b)]) => Some(ConstVal::I64(a | b)), - (Operator::I64Xor, [ConstVal::I64(a), ConstVal::I64(b)]) => Some(ConstVal::I64(a ^ b)), - (Operator::I64Shl, [ConstVal::I64(a), ConstVal::I64(b)]) => { - Some(ConstVal::I64(a.wrapping_shl(*b as u32))) - } - (Operator::I64ShrS, [ConstVal::I64(a), ConstVal::I64(b)]) => { - Some(ConstVal::I64((*a as i64).wrapping_shr(*b as u32) as u64)) - } - - (Operator::I64ShrU, [ConstVal::I64(a), ConstVal::I64(b)]) => { - Some(ConstVal::I64(a.wrapping_shr(*b as u32))) - } - - (Operator::I32Extend8S, [ConstVal::I32(a)]) => Some(ConstVal::I32(*a as i8 as i32 as u32)), - (Operator::I32Extend16S, [ConstVal::I32(a)]) => { - Some(ConstVal::I32(*a as i16 as i32 as u32)) - } - (Operator::I64Extend8S, [ConstVal::I64(a)]) => Some(ConstVal::I64(*a as i8 as i64 as u64)), - (Operator::I64Extend16S, [ConstVal::I64(a)]) => { - Some(ConstVal::I64(*a as i16 as i64 as u64)) - } - (Operator::I64Extend32S, [ConstVal::I64(a)]) => { - Some(ConstVal::I64(*a as i32 as i64 as u64)) - } - (Operator::Select, [x, y, ConstVal::I32(k)]) => Some(if *k != 0 { *x } else { *y }), - - _ => None, - } -} - impl GVNPass { fn optimize(&mut self, block: Block, body: &mut FunctionBody) { let mut i = 0; @@ -201,15 +49,21 @@ impl GVNPass { .iter() .map(|&arg| match body.values[arg] { ValueDef::Operator(Operator::I32Const { value }, _, _) => { - ConstVal::I32(value as u32) + ConstVal::I32(value) } ValueDef::Operator(Operator::I64Const { value }, _, _) => { - ConstVal::I64(value as u64) + ConstVal::I64(value) + } + ValueDef::Operator(Operator::F32Const { value }, _, _) => { + ConstVal::F32(value) + } + ValueDef::Operator(Operator::F64Const { value }, _, _) => { + ConstVal::F64(value) } _ => ConstVal::None, }) .collect::>(); - let const_val = const_eval(op, &arg_values[..]); + let const_val = const_eval(op, &arg_values[..], None); match const_val { Some(ConstVal::I32(val)) => { value = ValueDef::Operator( @@ -227,6 +81,22 @@ impl GVNPass { ); body.values[inst] = value.clone(); } + Some(ConstVal::F32(val)) => { + value = ValueDef::Operator( + Operator::F32Const { value: val }, + vec![], + vec![Type::F32], + ); + body.values[inst] = value.clone(); + } + Some(ConstVal::F64(val)) => { + value = ValueDef::Operator( + Operator::F64Const { value: val }, + vec![], + vec![Type::F64], + ); + body.values[inst] = value.clone(); + } _ => {} } }