use crate::vm::instruction::Reg;
pub(crate) const I64: u8 = 0x7E;
pub(crate) const I32: u8 = 0x7F;
pub(crate) const F64: u8 = 0x7C;
pub(crate) const VOID_BLOCKTYPE: u8 = 0x40;
pub(crate) fn leb_u32(out: &mut Vec<u8>, mut v: u32) {
loop {
let mut byte = (v & 0x7f) as u8;
v >>= 7;
if v != 0 {
byte |= 0x80;
}
out.push(byte);
if v == 0 {
break;
}
}
}
pub(crate) fn leb_i64(out: &mut Vec<u8>, mut v: i64) {
loop {
let byte = (v & 0x7f) as u8;
v >>= 7; let sign = byte & 0x40 != 0;
if (v == 0 && !sign) || (v == -1 && sign) {
out.push(byte);
break;
}
out.push(byte | 0x80);
}
}
pub(crate) fn section(out: &mut Vec<u8>, id: u8, content: &[u8]) {
out.push(id);
leb_u32(out, content.len() as u32);
out.extend_from_slice(content);
}
pub(crate) fn local_get(out: &mut Vec<u8>, idx: u32) {
out.push(0x20);
leb_u32(out, idx);
}
pub(crate) fn local_set(out: &mut Vec<u8>, idx: u32) {
out.push(0x21);
leb_u32(out, idx);
}
pub(crate) fn local_tee(out: &mut Vec<u8>, idx: u32) {
out.push(0x22);
leb_u32(out, idx);
}
pub(crate) fn global_get(out: &mut Vec<u8>, idx: u32) {
out.push(0x23);
leb_u32(out, idx);
}
pub(crate) fn global_set(out: &mut Vec<u8>, idx: u32) {
out.push(0x24);
leb_u32(out, idx);
}
pub(crate) fn leb_i32(out: &mut Vec<u8>, mut v: i32) {
loop {
let byte = (v & 0x7f) as u8;
v >>= 7; let sign = byte & 0x40 != 0;
if (v == 0 && !sign) || (v == -1 && sign) {
out.push(byte);
break;
}
out.push(byte | 0x80);
}
}
pub(crate) fn i32_const(out: &mut Vec<u8>, v: i32) {
out.push(0x41);
leb_i32(out, v);
}
fn mem_op(out: &mut Vec<u8>, opcode: u8, align: u32, offset: u32) {
out.push(opcode);
leb_u32(out, align);
leb_u32(out, offset);
}
pub(crate) fn i32_load(out: &mut Vec<u8>, offset: u32) {
mem_op(out, 0x28, 2, offset);
}
pub(crate) fn i32_store(out: &mut Vec<u8>, offset: u32) {
mem_op(out, 0x36, 2, offset);
}
pub(crate) fn i64_load(out: &mut Vec<u8>, offset: u32) {
mem_op(out, 0x29, 3, offset);
}
pub(crate) fn i64_store(out: &mut Vec<u8>, offset: u32) {
mem_op(out, 0x37, 3, offset);
}
pub(crate) fn f64_load(out: &mut Vec<u8>, offset: u32) {
mem_op(out, 0x2B, 3, offset);
}
pub(crate) fn f64_store(out: &mut Vec<u8>, offset: u32) {
mem_op(out, 0x39, 3, offset);
}
pub(crate) fn i32_load8_u(out: &mut Vec<u8>, offset: u32) {
mem_op(out, 0x2D, 0, offset);
}
pub(crate) fn i32_store8(out: &mut Vec<u8>, offset: u32) {
mem_op(out, 0x3A, 0, offset);
}
pub(crate) fn arith(out: &mut Vec<u8>, opcode: u8, dst: Reg, lhs: Reg, rhs: Reg) {
local_get(out, lhs as u32);
local_get(out, rhs as u32);
out.push(opcode);
local_set(out, dst as u32);
}
pub(crate) fn compare(out: &mut Vec<u8>, cmp_opcode: u8, dst: Reg, lhs: Reg, rhs: Reg) {
local_get(out, lhs as u32);
local_get(out, rhs as u32);
out.push(cmp_opcode);
out.push(0xAD); local_set(out, dst as u32);
}
pub(crate) fn emit_checked_addsub(out: &mut Vec<u8>, is_sub: bool, dst: Reg, lhs: Reg, rhs: Reg) {
local_get(out, lhs as u32);
local_get(out, rhs as u32);
out.push(if is_sub { 0x7D } else { 0x7C }); local_set(out, dst as u32);
if is_sub {
local_get(out, lhs as u32);
local_get(out, rhs as u32);
out.push(0x85); local_get(out, lhs as u32);
local_get(out, dst as u32);
out.push(0x85); } else {
local_get(out, lhs as u32);
local_get(out, dst as u32);
out.push(0x85); local_get(out, rhs as u32);
local_get(out, dst as u32);
out.push(0x85); }
out.push(0x83); out.push(0x42);
out.push(0x00); out.push(0x53); out.push(0x04);
out.push(0x40); out.push(0x00); out.push(0x0B); }
pub(crate) fn emit_checked_mul(out: &mut Vec<u8>, dst: Reg, lhs: Reg, rhs: Reg) {
local_get(out, lhs as u32);
local_get(out, rhs as u32);
out.push(0x7E); local_set(out, dst as u32);
local_get(out, lhs as u32);
out.push(0x50); out.push(0x04);
out.push(0x40); out.push(0x05); local_get(out, dst as u32);
local_get(out, lhs as u32);
out.push(0x7F); local_get(out, rhs as u32);
out.push(0x52); out.push(0x04);
out.push(0x40); out.push(0x00); out.push(0x0B); out.push(0x0B); }
pub(crate) fn emit_cond_jump(
code: &mut Vec<u8>,
cond: Reg,
jump_when_false: bool,
target_block: usize,
fallthrough_block: usize,
pc_local: u32,
loop_depth: u32,
) {
code.push(0x41); leb_u32(code, target_block as u32);
code.push(0x41); leb_u32(code, fallthrough_block as u32);
local_get(code, cond as u32);
if jump_when_false {
code.push(0x50); } else {
code.push(0x50); code.push(0x45); }
code.push(0x1B); local_set(code, pc_local);
code.push(0x0C); leb_u32(code, loop_depth);
}