use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum WasmOp {
I32Add,
I32Sub,
I32Mul,
I32DivS,
I32DivU,
I32RemS,
I32RemU,
I32And,
I32Or,
I32Xor,
I32Shl,
I32ShrS,
I32ShrU,
I32Rotl, I32Rotr, I32Clz, I32Ctz, I32Popcnt,
I32Extend8S, I32Extend16S,
I32Eqz, I32Eq,
I32Ne,
I32LtS,
I32LtU,
I32LeS,
I32LeU,
I32GtS,
I32GtU,
I32GeS,
I32GeU,
I32Const(i32),
I32Load {
offset: u32,
align: u32,
},
I32Store {
offset: u32,
align: u32,
},
I32Load8S {
offset: u32,
align: u32,
}, I32Load8U {
offset: u32,
align: u32,
}, I32Load16S {
offset: u32,
align: u32,
}, I32Load16U {
offset: u32,
align: u32,
},
I32Store8 {
offset: u32,
align: u32,
}, I32Store16 {
offset: u32,
align: u32,
},
Block,
Loop,
Br(u32), BrIf(u32), BrTable {
targets: Vec<u32>,
default: u32,
},
Return,
Call(u32),
CallIndirect {
type_index: u32,
table_index: u32,
},
LocalGet(u32),
LocalSet(u32),
LocalTee(u32),
GlobalGet(u32),
GlobalSet(u32),
MemorySize(u32), MemoryGrow(u32),
MemoryCopy, MemoryFill,
MultiMemory {
memory: u32,
op: Box<WasmOp>,
},
Drop,
Select,
If,
Else,
End,
Unreachable,
Nop,
I64Add,
I64Sub,
I64Mul,
I64DivS,
I64DivU,
I64RemS,
I64RemU,
I64And,
I64Or,
I64Xor,
I64Shl,
I64ShrS,
I64ShrU,
I64Rotl,
I64Rotr,
I64Clz,
I64Ctz,
I64Popcnt,
I64Eqz,
I64Eq,
I64Ne,
I64LtS,
I64LtU,
I64LeS,
I64LeU,
I64GtS,
I64GtU,
I64GeS,
I64GeU,
I64Const(i64),
I64Load {
offset: u32,
align: u32,
},
I64Store {
offset: u32,
align: u32,
},
I64Load8S {
offset: u32,
align: u32,
},
I64Load8U {
offset: u32,
align: u32,
},
I64Load16S {
offset: u32,
align: u32,
},
I64Load16U {
offset: u32,
align: u32,
},
I64Load32S {
offset: u32,
align: u32,
},
I64Load32U {
offset: u32,
align: u32,
},
I64Store8 {
offset: u32,
align: u32,
},
I64Store16 {
offset: u32,
align: u32,
},
I64Store32 {
offset: u32,
align: u32,
},
I64ExtendI32S, I64ExtendI32U, I32WrapI64,
I64Extend8S, I64Extend16S, I64Extend32S,
F32Add,
F32Sub,
F32Mul,
F32Div,
F32Eq,
F32Ne,
F32Lt,
F32Le,
F32Gt,
F32Ge,
F32Abs,
F32Neg,
F32Ceil,
F32Floor,
F32Trunc,
F32Nearest,
F32Sqrt,
F32Min,
F32Max,
F32Copysign,
F32Const(f32),
F32Load {
offset: u32,
align: u32,
},
F32Store {
offset: u32,
align: u32,
},
F32ConvertI32S, F32ConvertI32U, F32ConvertI64S, F32ConvertI64U, F32DemoteF64, F32ReinterpretI32, I32ReinterpretF32, I32TruncF32S, I32TruncF32U,
I32TruncSatF32S, I32TruncSatF32U, I64TruncSatF32S, I64TruncSatF32U,
F64Add,
F64Sub,
F64Mul,
F64Div,
F64Eq,
F64Ne,
F64Lt,
F64Le,
F64Gt,
F64Ge,
F64Abs,
F64Neg,
F64Ceil,
F64Floor,
F64Trunc,
F64Nearest,
F64Sqrt,
F64Min,
F64Max,
F64Copysign,
F64Const(f64),
F64Load {
offset: u32,
align: u32,
},
F64Store {
offset: u32,
align: u32,
},
F64ConvertI32S, F64ConvertI32U, F64ConvertI64S, F64ConvertI64U, F64PromoteF32, F64ReinterpretI64, I64ReinterpretF64, I64TruncF64S, I64TruncF64U, I32TruncF64S, I32TruncF64U, I64TruncF32S, I64TruncF32U,
I32TruncSatF64S, I32TruncSatF64U, I64TruncSatF64S, I64TruncSatF64U,
V128Const([u8; 16]), V128Load {
offset: u32,
align: u32,
}, V128Store {
offset: u32,
align: u32,
},
V128And, V128Or, V128Xor, V128Not, V128AndNot,
I8x16Add, I8x16Sub, I8x16Neg, I8x16Eq, I8x16Ne, I8x16LtS, I8x16LtU, I8x16GtS, I8x16GtU, I8x16LeS, I8x16LeU, I8x16GeS, I8x16GeU, I8x16Splat, I8x16ExtractLaneS(u8), I8x16ExtractLaneU(u8), I8x16ReplaceLane(u8), I8x16Shuffle([u8; 16]), I8x16Swizzle,
I16x8Add, I16x8Sub, I16x8Mul, I16x8Neg, I16x8Eq, I16x8Ne, I16x8LtS, I16x8LtU, I16x8GtS, I16x8GtU, I16x8LeS, I16x8LeU, I16x8GeS, I16x8GeU, I16x8Splat, I16x8ExtractLaneS(u8), I16x8ExtractLaneU(u8), I16x8ReplaceLane(u8),
I32x4Add, I32x4Sub, I32x4Mul, I32x4Neg, I32x4Eq, I32x4Ne, I32x4LtS, I32x4LtU, I32x4GtS, I32x4GtU, I32x4LeS, I32x4LeU, I32x4GeS, I32x4GeU, I32x4Splat, I32x4ExtractLane(u8), I32x4ReplaceLane(u8),
I64x2Add, I64x2Sub, I64x2Mul, I64x2Neg, I64x2Eq, I64x2Ne, I64x2LtS, I64x2GtS, I64x2LeS, I64x2GeS, I64x2Splat, I64x2ExtractLane(u8), I64x2ReplaceLane(u8),
F32x4Add, F32x4Sub, F32x4Mul, F32x4Div, F32x4Abs, F32x4Neg, F32x4Sqrt, F32x4Eq, F32x4Ne, F32x4Lt, F32x4Le, F32x4Gt, F32x4Ge, F32x4Splat, F32x4ExtractLane(u8), F32x4ReplaceLane(u8), }
pub fn rewrite_memory_grow_zero(wasm_ops: &[WasmOp]) -> Vec<WasmOp> {
let mut out = Vec::with_capacity(wasm_ops.len());
let mut i = 0;
while i < wasm_ops.len() {
if matches!(wasm_ops[i], WasmOp::I32Const(0))
&& let Some(WasmOp::MemoryGrow(m)) = wasm_ops.get(i + 1)
{
out.push(WasmOp::MemorySize(*m));
i += 2;
} else {
out.push(wasm_ops[i].clone());
i += 1;
}
}
out
}
#[cfg(test)]
mod grow_zero_tests {
use super::*;
#[test]
fn folds_const_zero_grow_to_size() {
assert_eq!(
rewrite_memory_grow_zero(&[WasmOp::I32Const(0), WasmOp::MemoryGrow(0)]),
vec![WasmOp::MemorySize(0)]
);
}
#[test]
fn leaves_nonzero_grow_alone() {
assert_eq!(
rewrite_memory_grow_zero(&[WasmOp::I32Const(2), WasmOp::MemoryGrow(0)]),
vec![WasmOp::I32Const(2), WasmOp::MemoryGrow(0)]
);
}
#[test]
fn leaves_variable_grow_alone() {
assert_eq!(
rewrite_memory_grow_zero(&[WasmOp::LocalGet(0), WasmOp::MemoryGrow(0)]),
vec![WasmOp::LocalGet(0), WasmOp::MemoryGrow(0)]
);
}
#[test]
fn preserves_memory_index() {
assert_eq!(
rewrite_memory_grow_zero(&[WasmOp::I32Const(0), WasmOp::MemoryGrow(3)]),
vec![WasmOp::MemorySize(3)]
);
}
}