summaryrefslogtreecommitdiffstats
path: root/third_party/rust/wasm-smith/src/core/code_builder/no_traps.rs
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/rust/wasm-smith/src/core/code_builder/no_traps.rs')
-rw-r--r--third_party/rust/wasm-smith/src/core/code_builder/no_traps.rs644
1 files changed, 644 insertions, 0 deletions
diff --git a/third_party/rust/wasm-smith/src/core/code_builder/no_traps.rs b/third_party/rust/wasm-smith/src/core/code_builder/no_traps.rs
new file mode 100644
index 0000000000..a1232b6922
--- /dev/null
+++ b/third_party/rust/wasm-smith/src/core/code_builder/no_traps.rs
@@ -0,0 +1,644 @@
+use crate::core::*;
+use wasm_encoder::{BlockType, Instruction, ValType};
+
+use super::CodeBuilder;
+
+// For loads, we dynamically check whether the load will
+// trap, and if it will then we generate a dummy value to
+// use instead.
+pub(crate) fn load<'a>(
+ inst: Instruction<'a>,
+ module: &Module,
+ builder: &mut CodeBuilder,
+ insts: &mut Vec<Instruction<'a>>,
+) {
+ let memarg = get_memarg(&inst);
+ let memory = &module.memories[memarg.memory_index as usize];
+ let address_type = if memory.memory64 {
+ ValType::I64
+ } else {
+ ValType::I32
+ };
+ // Add a temporary local to hold this load's address.
+ let address_local = builder.alloc_local(address_type);
+
+ // Add a temporary local to hold the result of this load.
+ let load_type = type_of_memory_access(&inst);
+ let result_local = builder.alloc_local(load_type);
+
+ // [address:address_type]
+ insts.push(Instruction::LocalSet(address_local));
+ // []
+ insts.push(Instruction::Block(wasm_encoder::BlockType::Empty));
+ {
+ // []
+ insts.push(Instruction::Block(wasm_encoder::BlockType::Empty));
+ {
+ // []
+ insts.push(Instruction::MemorySize(memarg.memory_index));
+ // [mem_size_in_pages:address_type]
+ insts.push(int_const_inst(address_type, 65_536));
+ // [mem_size_in_pages:address_type wasm_page_size:address_type]
+ insts.push(int_mul_inst(address_type));
+ // [mem_size_in_bytes:address_type]
+ insts.push(int_const_inst(
+ address_type,
+ (memarg.offset + size_of_type_in_memory(load_type)) as i64,
+ ));
+ // [mem_size_in_bytes:address_type offset_and_size:address_type]
+ insts.push(Instruction::LocalGet(address_local));
+ // [mem_size_in_bytes:address_type offset_and_size:address_type address:address_type]
+ insts.push(int_add_inst(address_type));
+ // [mem_size_in_bytes:address_type highest_byte_accessed:address_type]
+ insts.push(int_le_u_inst(address_type));
+ // [load_will_trap:i32]
+ insts.push(Instruction::BrIf(0));
+ // []
+ insts.push(Instruction::LocalGet(address_local));
+ // [address:address_type]
+ insts.push(int_const_inst(address_type, 0));
+ // [address:address_type 0:address_type]
+ insts.push(int_le_s_inst(address_type));
+ // [load_will_trap:i32]
+ insts.push(Instruction::BrIf(0));
+ // []
+ insts.push(Instruction::LocalGet(address_local));
+ // [address:address_type]
+ insts.push(inst);
+ // [result:load_type]
+ insts.push(Instruction::LocalSet(result_local));
+ // []
+ insts.push(Instruction::Br(1));
+ // <unreachable>
+ }
+ // []
+ insts.push(Instruction::End);
+ // []
+ insts.push(dummy_value_inst(load_type));
+ // [dummy_value:load_type]
+ insts.push(Instruction::LocalSet(result_local));
+ // []
+ }
+ // []
+ insts.push(Instruction::End);
+ // []
+ insts.push(Instruction::LocalGet(result_local));
+ // [result:load_type]
+}
+
+// Stores are similar to loads: we check whether the store
+// will trap, and if it will then we just drop the value.
+pub(crate) fn store<'a>(
+ inst: Instruction<'a>,
+ module: &Module,
+ builder: &mut CodeBuilder,
+ insts: &mut Vec<Instruction<'a>>,
+) {
+ let memarg = get_memarg(&inst);
+ let memory = &module.memories[memarg.memory_index as usize];
+ let address_type = if memory.memory64 {
+ ValType::I64
+ } else {
+ ValType::I32
+ };
+
+ // Add a temporary local to hold this store's address.
+ let address_local = builder.alloc_local(address_type);
+
+ // Add a temporary local to hold the value to store.
+ let store_type = type_of_memory_access(&inst);
+ let value_local = builder.alloc_local(store_type);
+
+ // [address:address_type value:store_type]
+ insts.push(Instruction::LocalSet(value_local));
+ // [address:address_type]
+ insts.push(Instruction::LocalSet(address_local));
+ // []
+ insts.push(Instruction::MemorySize(memarg.memory_index));
+ // [mem_size_in_pages:address_type]
+ insts.push(int_const_inst(address_type, 65_536));
+ // [mem_size_in_pages:address_type wasm_page_size:address_type]
+ insts.push(int_mul_inst(address_type));
+ // [mem_size_in_bytes:address_type]
+ insts.push(int_const_inst(
+ address_type,
+ (memarg.offset + size_of_type_in_memory(store_type)) as i64,
+ ));
+ // [mem_size_in_bytes:address_type offset_and_size:address_type]
+ insts.push(Instruction::LocalGet(address_local));
+ // [mem_size_in_bytes:address_type offset_and_size:address_type address:address_type]
+ insts.push(int_add_inst(address_type));
+ // [mem_size_in_bytes:address_type highest_byte_accessed:address_type]
+ insts.push(int_le_u_inst(address_type));
+ // [store_will_trap:i32]
+ insts.push(Instruction::If(BlockType::Empty));
+ insts.push(Instruction::Else);
+ {
+ // []
+ insts.push(Instruction::LocalGet(address_local));
+ // [address:address_type]
+ insts.push(int_const_inst(address_type, 0));
+ // [address:address_type 0:address_type]
+ insts.push(int_le_s_inst(address_type));
+ // [load_will_trap:i32]
+ insts.push(Instruction::If(BlockType::Empty));
+ insts.push(Instruction::Else);
+ {
+ // []
+ insts.push(Instruction::LocalGet(address_local));
+ // [address:address_type]
+ insts.push(Instruction::LocalGet(value_local));
+ // [address:address_type value:store_type]
+ insts.push(inst);
+ // []
+ }
+ insts.push(Instruction::End);
+ }
+ // []
+ insts.push(Instruction::End);
+}
+
+// Unsigned integer division and remainder will trap when
+// the divisor is 0. To avoid the trap, we will set any 0
+// divisors to 1 prior to the operation.
+//
+// The code below is equivalent to this expression:
+//
+// local.set $temp_divisor
+// (select (i32.eqz (local.get $temp_divisor) (i32.const 1) (local.get $temp_divisor))
+pub(crate) fn unsigned_div_rem<'a>(
+ inst: Instruction<'a>,
+ builder: &mut CodeBuilder,
+ insts: &mut Vec<Instruction<'a>>,
+) {
+ let op_type = type_of_integer_operation(&inst);
+ let temp_divisor = builder.alloc_local(op_type);
+
+ // [dividend:op_type divisor:op_type]
+ insts.push(Instruction::LocalSet(temp_divisor));
+ // [dividend:op_type]
+ insts.push(int_const_inst(op_type, 1));
+ // [dividend:op_type 1:op_type]
+ insts.push(Instruction::LocalGet(temp_divisor));
+ // [dividend:op_type 1:op_type divisor:op_type]
+ insts.push(Instruction::LocalGet(temp_divisor));
+ // [dividend:op_type 1:op_type divisor:op_type divisor:op_type]
+ insts.push(eqz_inst(op_type));
+ // [dividend:op_type 1:op_type divisor:op_type is_zero:i32]
+ insts.push(Instruction::Select);
+ // [dividend:op_type divisor:op_type]
+ insts.push(inst);
+ // [result:op_type]
+}
+
+pub(crate) fn trunc<'a>(
+ inst: Instruction<'a>,
+ builder: &mut CodeBuilder,
+ insts: &mut Vec<Instruction<'a>>,
+) {
+ // If NaN or ±inf, replace with dummy value. Our method of checking for NaN
+ // is to use `ne` because NaN is the only value that is not equal to itself
+ let conv_type = type_of_float_conversion(&inst);
+ let temp_float = builder.alloc_local(conv_type);
+ // [input:conv_type]
+ insts.push(Instruction::LocalTee(temp_float));
+ // [input:conv_type]
+ insts.push(Instruction::LocalGet(temp_float));
+ // [input:conv_type input:conv_type]
+ insts.push(ne_inst(conv_type));
+ // [is_nan:i32]
+ insts.push(Instruction::LocalGet(temp_float));
+ // [is_nan:i32 input:conv_type]
+ insts.push(flt_inf_const_inst(conv_type));
+ // [is_nan:i32 input:conv_type inf:conv_type]
+ insts.push(eq_inst(conv_type));
+ // [is_nan:i32 is_inf:i32]
+ insts.push(Instruction::LocalGet(temp_float));
+ // [is_nan:i32 is_inf:i32 input:conv_type]
+ insts.push(flt_neg_inf_const_inst(conv_type));
+ // [is_nan:i32 is_inf:i32 input:conv_type neg_inf:conv_type]
+ insts.push(eq_inst(conv_type));
+ // [is_nan:i32 is_inf:i32 is_neg_inf:i32]
+ insts.push(Instruction::I32Or);
+ // [is_nan:i32 is_±inf:i32]
+ insts.push(Instruction::I32Or);
+ // [is_nan_or_inf:i32]
+ insts.push(Instruction::If(BlockType::Empty));
+ {
+ // []
+ insts.push(dummy_value_inst(conv_type));
+ // [0:conv_type]
+ insts.push(Instruction::LocalSet(temp_float));
+ // []
+ }
+ insts.push(Instruction::End);
+ // []
+ insts.push(Instruction::LocalGet(temp_float));
+ // [input_or_0:conv_type]
+
+ // first ensure that it is >= the min value of our target type
+ insts.push(min_input_const_for_trunc(&inst));
+ // [input_or_0:conv_type min_value_of_target_type:conv_type]
+ insts.push(flt_lt_inst(conv_type));
+ // [input_lt_min:i32]
+ insts.push(Instruction::If(BlockType::Empty));
+ {
+ // []
+ insts.push(min_input_const_for_trunc(&inst));
+ // [min_value_of_target_type:conv_type]
+ insts.push(Instruction::LocalSet(temp_float));
+ }
+ insts.push(Instruction::End);
+ // []
+ insts.push(Instruction::LocalGet(temp_float));
+ // [coerced_input:conv_type]
+
+ // next ensure that it is <= the max value of our target type
+ insts.push(max_input_const_for_trunc(&inst));
+ // [input_or_0:conv_type max_value_of_target_type:conv_type]
+ insts.push(flt_gt_inst(conv_type));
+ // [input_gt_min:i32]
+ insts.push(Instruction::If(BlockType::Empty));
+ {
+ // []
+ insts.push(max_input_const_for_trunc(&inst));
+ // [max_value_of_target_type:conv_type]
+ insts.push(Instruction::LocalSet(temp_float));
+ }
+ insts.push(Instruction::End);
+ // []
+ insts.push(Instruction::LocalGet(temp_float));
+ // [coerced_input:conv_type]
+ insts.push(inst);
+}
+
+// Signed division and remainder will trap in the following instances:
+// - The divisor is 0
+// - The result of the division is 2^(n-1)
+pub(crate) fn signed_div_rem<'a>(
+ inst: Instruction<'a>,
+ builder: &mut CodeBuilder,
+ insts: &mut Vec<Instruction<'a>>,
+) {
+ // If divisor is 0, replace with 1
+ let op_type = type_of_integer_operation(&inst);
+ let temp_divisor = builder.alloc_local(op_type);
+ // [dividend:op_type divisor:op_type]
+ insts.push(Instruction::LocalSet(temp_divisor));
+ // [dividend:op_type]
+ insts.push(int_const_inst(op_type, 1));
+ // [dividend:op_type 1:op_type]
+ insts.push(Instruction::LocalGet(temp_divisor));
+ // [dividend:op_type 1:op_type divisor:op_type]
+ insts.push(Instruction::LocalGet(temp_divisor));
+ // [dividend:op_type 1:op_type divisor:op_type divisor:op_type]
+ insts.push(eqz_inst(op_type));
+ // [dividend:op_type 1:op_type divisor:op_type is_zero:i32]
+ insts.push(Instruction::Select);
+ // [dividend:op_type divisor:op_type]
+ // If dividend and divisor are -int.max and -1, replace
+ // divisor with 1.
+ let temp_dividend = builder.alloc_local(op_type);
+ insts.push(Instruction::LocalSet(temp_divisor));
+ // [dividend:op_type]
+ insts.push(Instruction::LocalSet(temp_dividend));
+ // []
+ insts.push(Instruction::Block(wasm_encoder::BlockType::Empty));
+ {
+ insts.push(Instruction::Block(wasm_encoder::BlockType::Empty));
+ {
+ // []
+ insts.push(Instruction::LocalGet(temp_dividend));
+ // [dividend:op_type]
+ insts.push(Instruction::LocalGet(temp_divisor));
+ // [dividend:op_type divisor:op_type]
+ insts.push(Instruction::LocalSet(temp_divisor));
+ // [dividend:op_type]
+ insts.push(Instruction::LocalTee(temp_dividend));
+ // [dividend:op_type]
+ insts.push(int_min_const_inst(op_type));
+ // [dividend:op_type int_min:op_type]
+ insts.push(ne_inst(op_type));
+ // [not_int_min:i32]
+ insts.push(Instruction::BrIf(0));
+ // []
+ insts.push(Instruction::LocalGet(temp_divisor));
+ // [divisor:op_type]
+ insts.push(int_const_inst(op_type, -1));
+ // [divisor:op_type -1:op_type]
+ insts.push(ne_inst(op_type));
+ // [not_neg_one:i32]
+ insts.push(Instruction::BrIf(0));
+ // []
+ insts.push(int_const_inst(op_type, 1));
+ // [divisor:op_type]
+ insts.push(Instruction::LocalSet(temp_divisor));
+ // []
+ insts.push(Instruction::Br(1));
+ }
+ // []
+ insts.push(Instruction::End);
+ }
+ // []
+ insts.push(Instruction::End);
+ // []
+ insts.push(Instruction::LocalGet(temp_dividend));
+ // [dividend:op_type]
+ insts.push(Instruction::LocalGet(temp_divisor));
+ // [dividend:op_type divisor:op_type]
+ insts.push(inst);
+}
+
+fn get_memarg(inst: &Instruction) -> wasm_encoder::MemArg {
+ match *inst {
+ Instruction::I32Load(memarg)
+ | Instruction::I64Load(memarg)
+ | Instruction::F32Load(memarg)
+ | Instruction::F64Load(memarg)
+ | Instruction::I32Load8S(memarg)
+ | Instruction::I32Load8U(memarg)
+ | Instruction::I32Load16S(memarg)
+ | Instruction::I32Load16U(memarg)
+ | Instruction::I64Load8S(memarg)
+ | Instruction::I64Load8U(memarg)
+ | Instruction::I64Load16S(memarg)
+ | Instruction::I64Load16U(memarg)
+ | Instruction::I64Load32S(memarg)
+ | Instruction::I64Load32U(memarg)
+ | Instruction::V128Load(memarg)
+ | Instruction::V128Load8x8S(memarg)
+ | Instruction::V128Load8x8U(memarg)
+ | Instruction::V128Load16x4S(memarg)
+ | Instruction::V128Load16x4U(memarg)
+ | Instruction::V128Load32x2S(memarg)
+ | Instruction::V128Load32x2U(memarg)
+ | Instruction::V128Load8Splat(memarg)
+ | Instruction::V128Load16Splat(memarg)
+ | Instruction::V128Load32Splat(memarg)
+ | Instruction::V128Load64Splat(memarg)
+ | Instruction::V128Load32Zero(memarg)
+ | Instruction::V128Load64Zero(memarg)
+ | Instruction::I32Store(memarg)
+ | Instruction::I64Store(memarg)
+ | Instruction::F32Store(memarg)
+ | Instruction::F64Store(memarg)
+ | Instruction::I32Store8(memarg)
+ | Instruction::I32Store16(memarg)
+ | Instruction::I64Store8(memarg)
+ | Instruction::I64Store16(memarg)
+ | Instruction::I64Store32(memarg)
+ | Instruction::V128Store(memarg) => memarg,
+ _ => unreachable!(),
+ }
+}
+
+fn dummy_value_inst<'a>(ty: ValType) -> Instruction<'a> {
+ match ty {
+ ValType::I32 => Instruction::I32Const(0),
+ ValType::I64 => Instruction::I64Const(0),
+ ValType::F32 => Instruction::F32Const(0.0),
+ ValType::F64 => Instruction::F64Const(0.0),
+ ValType::V128 => Instruction::V128Const(0),
+ ValType::Ref(ty) => {
+ assert!(ty.nullable);
+ Instruction::RefNull(ty.heap_type)
+ }
+ }
+}
+
+fn eq_inst<'a>(ty: ValType) -> Instruction<'a> {
+ match ty {
+ ValType::F32 => Instruction::F32Eq,
+ ValType::F64 => Instruction::F64Eq,
+ ValType::I32 => Instruction::I32Eq,
+ ValType::I64 => Instruction::I64Eq,
+ _ => panic!("not a numeric type"),
+ }
+}
+
+fn eqz_inst<'a>(ty: ValType) -> Instruction<'a> {
+ match ty {
+ ValType::I32 => Instruction::I32Eqz,
+ ValType::I64 => Instruction::I64Eqz,
+ _ => panic!("not an integer type"),
+ }
+}
+
+fn type_of_integer_operation(inst: &Instruction) -> ValType {
+ match inst {
+ Instruction::I32DivU
+ | Instruction::I32DivS
+ | Instruction::I32RemU
+ | Instruction::I32RemS => ValType::I32,
+ Instruction::I64RemU
+ | Instruction::I64DivU
+ | Instruction::I64DivS
+ | Instruction::I64RemS => ValType::I64,
+ _ => panic!("not integer division or remainder"),
+ }
+}
+
+fn type_of_float_conversion(inst: &Instruction) -> ValType {
+ match inst {
+ Instruction::I32TruncF32S
+ | Instruction::I32TruncF32U
+ | Instruction::I64TruncF32S
+ | Instruction::I64TruncF32U => ValType::F32,
+ Instruction::I32TruncF64S
+ | Instruction::I32TruncF64U
+ | Instruction::I64TruncF64S
+ | Instruction::I64TruncF64U => ValType::F64,
+ _ => panic!("not a float -> integer conversion"),
+ }
+}
+
+fn min_input_const_for_trunc<'a>(inst: &Instruction) -> Instruction<'a> {
+ // This is the minimum float value that is representable as an i64
+ let min_f64 = -9_223_372_036_854_775_000f64;
+ let min_f32 = -9_223_372_000_000_000_000f32;
+
+ // This is the minimum float value that is representable as as i32
+ let min_f32_as_i32 = -2_147_483_500f32;
+ match inst {
+ Instruction::I32TruncF32S => Instruction::F32Const(min_f32_as_i32),
+ Instruction::I32TruncF32U => Instruction::F32Const(0.0),
+ Instruction::I64TruncF32S => Instruction::F32Const(min_f32),
+ Instruction::I64TruncF32U => Instruction::F32Const(0.0),
+ Instruction::I32TruncF64S => Instruction::F64Const(i32::MIN as f64),
+ Instruction::I32TruncF64U => Instruction::F64Const(0.0),
+ Instruction::I64TruncF64S => Instruction::F64Const(min_f64),
+ Instruction::I64TruncF64U => Instruction::F64Const(0.0),
+ _ => panic!("not a trunc instruction"),
+ }
+}
+
+fn max_input_const_for_trunc<'a>(inst: &Instruction) -> Instruction<'a> {
+ // This is the maximum float value that is representable as as i64
+ let max_f64_as_i64 = 9_223_372_036_854_775_000f64;
+ let max_f32_as_i64 = 9_223_371_500_000_000_000f32;
+
+ // This is the maximum float value that is representable as as i32
+ let max_f32_as_i32 = 2_147_483_500f32;
+ match inst {
+ Instruction::I32TruncF32S | Instruction::I32TruncF32U => {
+ Instruction::F32Const(max_f32_as_i32)
+ }
+ Instruction::I64TruncF32S | Instruction::I64TruncF32U => {
+ Instruction::F32Const(max_f32_as_i64)
+ }
+ Instruction::I32TruncF64S | Instruction::I32TruncF64U => {
+ Instruction::F64Const(i32::MAX as f64)
+ }
+ Instruction::I64TruncF64S | Instruction::I64TruncF64U => {
+ Instruction::F64Const(max_f64_as_i64)
+ }
+ _ => panic!("not a trunc instruction"),
+ }
+}
+
+fn type_of_memory_access(inst: &Instruction) -> ValType {
+ match inst {
+ Instruction::I32Load(_)
+ | Instruction::I32Load8S(_)
+ | Instruction::I32Load8U(_)
+ | Instruction::I32Load16S(_)
+ | Instruction::I32Load16U(_)
+ | Instruction::I32Store(_)
+ | Instruction::I32Store8(_)
+ | Instruction::I32Store16(_) => ValType::I32,
+
+ Instruction::I64Load(_)
+ | Instruction::I64Load8S(_)
+ | Instruction::I64Load8U(_)
+ | Instruction::I64Load16S(_)
+ | Instruction::I64Load16U(_)
+ | Instruction::I64Load32S(_)
+ | Instruction::I64Load32U(_)
+ | Instruction::I64Store(_)
+ | Instruction::I64Store8(_)
+ | Instruction::I64Store16(_)
+ | Instruction::I64Store32(_) => ValType::I64,
+
+ Instruction::F32Load(_) | Instruction::F32Store(_) => ValType::F32,
+
+ Instruction::F64Load(_) | Instruction::F64Store(_) => ValType::F64,
+
+ Instruction::V128Load { .. }
+ | Instruction::V128Load8x8S { .. }
+ | Instruction::V128Load8x8U { .. }
+ | Instruction::V128Load16x4S { .. }
+ | Instruction::V128Load16x4U { .. }
+ | Instruction::V128Load32x2S { .. }
+ | Instruction::V128Load32x2U { .. }
+ | Instruction::V128Load8Splat { .. }
+ | Instruction::V128Load16Splat { .. }
+ | Instruction::V128Load32Splat { .. }
+ | Instruction::V128Load64Splat { .. }
+ | Instruction::V128Load32Zero { .. }
+ | Instruction::V128Load64Zero { .. }
+ | Instruction::V128Store { .. } => ValType::V128,
+
+ _ => panic!("not a memory access instruction"),
+ }
+}
+
+fn int_min_const_inst<'a>(ty: ValType) -> Instruction<'a> {
+ match ty {
+ ValType::I32 => Instruction::I32Const(i32::MIN),
+ ValType::I64 => Instruction::I64Const(i64::MIN),
+ _ => panic!("not an int type"),
+ }
+}
+
+fn int_const_inst<'a>(ty: ValType, x: i64) -> Instruction<'a> {
+ match ty {
+ ValType::I32 => Instruction::I32Const(x as i32),
+ ValType::I64 => Instruction::I64Const(x),
+ _ => panic!("not an int type"),
+ }
+}
+
+fn int_mul_inst<'a>(ty: ValType) -> Instruction<'a> {
+ match ty {
+ ValType::I32 => Instruction::I32Mul,
+ ValType::I64 => Instruction::I64Mul,
+ _ => panic!("not an int type"),
+ }
+}
+
+fn int_add_inst<'a>(ty: ValType) -> Instruction<'a> {
+ match ty {
+ ValType::I32 => Instruction::I32Add,
+ ValType::I64 => Instruction::I64Add,
+ _ => panic!("not an int type"),
+ }
+}
+
+fn int_le_u_inst<'a>(ty: ValType) -> Instruction<'a> {
+ match ty {
+ ValType::I32 => Instruction::I32LeU,
+ ValType::I64 => Instruction::I64LeU,
+ _ => panic!("not an int type"),
+ }
+}
+
+fn int_le_s_inst<'a>(ty: ValType) -> Instruction<'a> {
+ match ty {
+ ValType::I32 => Instruction::I32LeS,
+ ValType::I64 => Instruction::I64LeS,
+ _ => panic!("not an int type"),
+ }
+}
+
+fn ne_inst<'a>(ty: ValType) -> Instruction<'a> {
+ match ty {
+ ValType::I32 => Instruction::I32Ne,
+ ValType::I64 => Instruction::I64Ne,
+ ValType::F32 => Instruction::F32Ne,
+ ValType::F64 => Instruction::F64Ne,
+ _ => panic!("not a numeric type"),
+ }
+}
+
+fn flt_lt_inst<'a>(ty: ValType) -> Instruction<'a> {
+ match ty {
+ ValType::F32 => Instruction::F32Lt,
+ ValType::F64 => Instruction::F64Lt,
+ _ => panic!("not a float type"),
+ }
+}
+
+fn flt_gt_inst<'a>(ty: ValType) -> Instruction<'a> {
+ match ty {
+ ValType::F32 => Instruction::F32Gt,
+ ValType::F64 => Instruction::F64Gt,
+ _ => panic!("not a float type"),
+ }
+}
+
+fn flt_inf_const_inst<'a>(ty: ValType) -> Instruction<'a> {
+ match ty {
+ ValType::F32 => Instruction::F32Const(f32::INFINITY),
+ ValType::F64 => Instruction::F64Const(f64::INFINITY),
+ _ => panic!("not a float type"),
+ }
+}
+
+fn flt_neg_inf_const_inst<'a>(ty: ValType) -> Instruction<'a> {
+ match ty {
+ ValType::F32 => Instruction::F32Const(f32::NEG_INFINITY),
+ ValType::F64 => Instruction::F64Const(f64::NEG_INFINITY),
+ _ => panic!("not a float type"),
+ }
+}
+
+fn size_of_type_in_memory(ty: ValType) -> u64 {
+ match ty {
+ ValType::I32 => 4,
+ ValType::I64 => 8,
+ ValType::F32 => 4,
+ ValType::F64 => 8,
+ ValType::V128 => 16,
+ ValType::Ref(_) => panic!("not a memory type"),
+ }
+}