use crate::core::*;
use wasm_encoder::{BlockType, Instruction, ValType};
use super::CodeBuilder;
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
};
let address_local = builder.alloc_local(address_type);
let load_type = type_of_memory_access(&inst);
let result_local = builder.alloc_local(load_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));
insts.push(int_const_inst(address_type, 65_536));
insts.push(int_mul_inst(address_type));
insts.push(int_const_inst(
address_type,
(memarg.offset + size_of_type_in_memory(load_type)) as i64,
));
insts.push(Instruction::LocalGet(address_local));
insts.push(int_add_inst(address_type));
insts.push(int_le_u_inst(address_type));
insts.push(Instruction::BrIf(0));
insts.push(Instruction::LocalGet(address_local));
insts.push(int_const_inst(address_type, 0));
insts.push(int_le_s_inst(address_type));
insts.push(Instruction::BrIf(0));
insts.push(Instruction::LocalGet(address_local));
insts.push(inst);
insts.push(Instruction::LocalSet(result_local));
insts.push(Instruction::Br(1));
}
insts.push(Instruction::End);
insts.push(dummy_value_inst(load_type));
insts.push(Instruction::LocalSet(result_local));
}
insts.push(Instruction::End);
insts.push(Instruction::LocalGet(result_local));
}
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
};
let address_local = builder.alloc_local(address_type);
let store_type = type_of_memory_access(&inst);
let value_local = builder.alloc_local(store_type);
insts.push(Instruction::LocalSet(value_local));
insts.push(Instruction::LocalSet(address_local));
insts.push(Instruction::MemorySize(memarg.memory_index));
insts.push(int_const_inst(address_type, 65_536));
insts.push(int_mul_inst(address_type));
insts.push(int_const_inst(
address_type,
(memarg.offset + size_of_type_in_memory(store_type)) as i64,
));
insts.push(Instruction::LocalGet(address_local));
insts.push(int_add_inst(address_type));
insts.push(int_le_u_inst(address_type));
insts.push(Instruction::If(BlockType::Empty));
insts.push(Instruction::Else);
{
insts.push(Instruction::LocalGet(address_local));
insts.push(int_const_inst(address_type, 0));
insts.push(int_le_s_inst(address_type));
insts.push(Instruction::If(BlockType::Empty));
insts.push(Instruction::Else);
{
insts.push(Instruction::LocalGet(address_local));
insts.push(Instruction::LocalGet(value_local));
insts.push(inst);
}
insts.push(Instruction::End);
}
insts.push(Instruction::End);
}
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);
insts.push(Instruction::LocalSet(temp_divisor));
insts.push(int_const_inst(op_type, 1));
insts.push(Instruction::LocalGet(temp_divisor));
insts.push(Instruction::LocalGet(temp_divisor));
insts.push(eqz_inst(op_type));
insts.push(Instruction::Select);
insts.push(inst);
}
pub(crate) fn trunc<'a>(
inst: Instruction<'a>,
builder: &mut CodeBuilder,
insts: &mut Vec<Instruction<'a>>,
) {
let conv_type = type_of_float_conversion(&inst);
let temp_float = builder.alloc_local(conv_type);
insts.push(Instruction::LocalTee(temp_float));
insts.push(Instruction::LocalGet(temp_float));
insts.push(ne_inst(conv_type));
insts.push(Instruction::LocalGet(temp_float));
insts.push(flt_inf_const_inst(conv_type));
insts.push(eq_inst(conv_type));
insts.push(Instruction::LocalGet(temp_float));
insts.push(flt_neg_inf_const_inst(conv_type));
insts.push(eq_inst(conv_type));
insts.push(Instruction::I32Or);
insts.push(Instruction::I32Or);
insts.push(Instruction::If(BlockType::Empty));
{
insts.push(dummy_value_inst(conv_type));
insts.push(Instruction::LocalSet(temp_float));
}
insts.push(Instruction::End);
insts.push(Instruction::LocalGet(temp_float));
insts.push(min_input_const_for_trunc(&inst));
insts.push(flt_lt_inst(conv_type));
insts.push(Instruction::If(BlockType::Empty));
{
insts.push(min_input_const_for_trunc(&inst));
insts.push(Instruction::LocalSet(temp_float));
}
insts.push(Instruction::End);
insts.push(Instruction::LocalGet(temp_float));
insts.push(max_input_const_for_trunc(&inst));
insts.push(flt_gt_inst(conv_type));
insts.push(Instruction::If(BlockType::Empty));
{
insts.push(max_input_const_for_trunc(&inst));
insts.push(Instruction::LocalSet(temp_float));
}
insts.push(Instruction::End);
insts.push(Instruction::LocalGet(temp_float));
insts.push(inst);
}
pub(crate) fn signed_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);
insts.push(Instruction::LocalSet(temp_divisor));
insts.push(int_const_inst(op_type, 1));
insts.push(Instruction::LocalGet(temp_divisor));
insts.push(Instruction::LocalGet(temp_divisor));
insts.push(eqz_inst(op_type));
insts.push(Instruction::Select);
let temp_dividend = builder.alloc_local(op_type);
insts.push(Instruction::LocalSet(temp_divisor));
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));
insts.push(Instruction::LocalGet(temp_divisor));
insts.push(Instruction::LocalSet(temp_divisor));
insts.push(Instruction::LocalTee(temp_dividend));
insts.push(int_min_const_inst(op_type));
insts.push(ne_inst(op_type));
insts.push(Instruction::BrIf(0));
insts.push(Instruction::LocalGet(temp_divisor));
insts.push(int_const_inst(op_type, -1));
insts.push(ne_inst(op_type));
insts.push(Instruction::BrIf(0));
insts.push(int_const_inst(op_type, 1));
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));
insts.push(Instruction::LocalGet(temp_divisor));
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> {
let min_f64 = -9_223_372_036_854_775_000f64;
let min_f32 = -9_223_372_000_000_000_000f32;
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> {
let max_f64_as_i64 = 9_223_372_036_854_775_000f64;
let max_f32_as_i64 = 9_223_371_500_000_000_000f32;
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"),
}
}