use fix_codegen::OperandType; use fix_common::StringId; use num_enum::TryFromPrimitive; use string_interner::Symbol as _; use crate::{OperandData, VmRuntimeCtx}; pub(crate) struct BytecodeReader<'a> { bytecode: &'a [u8], pc: usize, inst_start_pc: usize, } impl<'a> BytecodeReader<'a> { #[cfg_attr(feature = "tailcall", allow(dead_code))] pub(crate) fn new(bytecode: &'a [u8], pc: usize) -> Self { Self { bytecode, pc, inst_start_pc: pc, } } #[inline(always)] #[cfg_attr(not(feature = "tailcall"), allow(dead_code))] pub(crate) fn from_after_op(bytecode: &'a [u8], inst_start_pc: usize) -> Self { Self { bytecode, pc: inst_start_pc + 1, inst_start_pc, } } #[inline(always)] #[cfg_attr(debug_assertions, track_caller)] fn read_array(&mut self) -> [u8; N] { let ret = self.bytecode[self.pc..self.pc + N] .try_into() .expect("read_array failed"); self.pc += N; ret } #[inline(always)] #[cfg_attr(feature = "tailcall", allow(dead_code))] pub(crate) fn read_op(&mut self) -> fix_codegen::Op { use fix_codegen::Op; self.inst_start_pc = self.pc; let byte = self.bytecode[self.pc]; if !likely_stable::likely((0..Op::Illegal as u8).contains(&byte)) { panic!("unknown opcode: {byte:#04x}") } self.pc += 1; unsafe { std::mem::transmute::(byte) } } #[inline(always)] pub(crate) fn read_u8(&mut self) -> u8 { let val = self.bytecode[self.pc]; self.pc += 1; val } #[inline(always)] pub(crate) fn read_u16(&mut self) -> u16 { u16::from_le_bytes(self.read_array()) } #[inline(always)] pub(crate) fn read_u32(&mut self) -> u32 { u32::from_le_bytes(self.read_array()) } #[inline(always)] pub(crate) fn read_i32(&mut self) -> i32 { i32::from_le_bytes(self.read_array()) } #[inline(always)] pub(crate) fn read_i64(&mut self) -> i64 { i64::from_le_bytes(self.read_array()) } #[inline(always)] pub(crate) fn read_f64(&mut self) -> f64 { f64::from_le_bytes(self.read_array()) } #[inline(always)] pub(crate) fn read_string_id(&mut self) -> StringId { let raw = self.read_u32(); #[allow(clippy::unwrap_used)] StringId(string_interner::symbol::SymbolU32::try_from_usize(raw as usize).unwrap()) } #[inline(always)] pub(crate) fn read_operand_data(&mut self, ctx: &C) -> OperandData { let tag = self.read_u8(); let Ok(ty) = OperandType::try_from_primitive(tag) .map_err(|err| panic!("unknown operand tag: {:#04x}", err.number)); match ty { OperandType::Const => { let id = self.read_u32(); OperandData::Const(ctx.get_const(id)) } OperandType::Local => { let layer = self.read_u8(); let idx = self.read_u32(); OperandData::Local { layer, idx } } OperandType::Builtins => OperandData::Builtins, OperandType::BigInt => { let val = self.read_i64(); OperandData::BigInt(val) } } } #[inline(always)] pub(crate) fn read_attr_key_data(&mut self, ctx: &C) -> crate::AttrKeyData { use fix_codegen::AttrKeyType; let tag = self.read_u8(); let ty = AttrKeyType::try_from_primitive(tag) .unwrap_or_else(|err| panic!("unknown key tag: {:#04x}", err.number)); match ty { AttrKeyType::Static => crate::AttrKeyData::Static(self.read_string_id()), AttrKeyType::Dynamic => crate::AttrKeyData::Dynamic(self.read_operand_data(ctx)), } } pub(crate) fn pc(&self) -> usize { self.pc } pub(crate) fn set_pc(&mut self, pc: usize) { self.pc = pc; } pub(crate) fn inst_start_pc(&self) -> usize { self.inst_start_pc } }