treewide: enforce strict clippy check

This commit is contained in:
2026-08-22 18:41:23 +08:00
parent 5f494983b8
commit cb38031f85
51 changed files with 1237 additions and 568 deletions
+3
View File
@@ -9,3 +9,6 @@ num_enum = { workspace = true }
string-interner = { workspace = true }
fix-lang = { path = "../fix-lang" }
[lints]
workspace = true
+79 -152
View File
@@ -2,7 +2,7 @@ use std::fmt::Write;
use colored::Colorize as _;
use crate::{Continuation, InstructionPtr, Op, OperandType};
use crate::{BytecodeReader, Continuation, InstructionPtr, Op};
pub trait DisassemblerContext {
fn resolve_string(&self, id: u32) -> &str;
@@ -10,99 +10,15 @@ pub trait DisassemblerContext {
}
pub struct Disassembler<'a, Ctx> {
code: &'a [u8],
reader: BytecodeReader<'a>,
ctx: &'a Ctx,
pc: usize,
}
impl<'a, Ctx: DisassemblerContext> Disassembler<'a, Ctx> {
pub fn new(ip: InstructionPtr, ctx: &'a Ctx) -> Self {
Self {
code: ctx.get_code(),
reader: BytecodeReader::new(ctx.get_code(), ip.0),
ctx,
pc: ip.0,
}
}
#[inline(always)]
fn read_u8(&mut self) -> u8 {
let b = self.code[self.pc];
self.pc += 1;
b
}
#[inline(always)]
fn read_u16(&mut self) -> u16 {
let bytes = self.code[self.pc..self.pc + 2]
.try_into()
.expect("no enough bytes");
self.pc += 2;
u16::from_le_bytes(bytes)
}
#[inline(always)]
fn read_u32(&mut self) -> u32 {
let bytes = self.code[self.pc..self.pc + 4]
.try_into()
.expect("no enough bytes");
self.pc += 4;
u32::from_le_bytes(bytes)
}
#[inline(always)]
fn read_i32(&mut self) -> i32 {
let bytes = self.code[self.pc..self.pc + 4]
.try_into()
.expect("no enough bytes");
self.pc += 4;
i32::from_le_bytes(bytes)
}
#[inline(always)]
fn read_i64(&mut self) -> i64 {
let bytes = self.code[self.pc..self.pc + 8]
.try_into()
.expect("no enough bytes");
self.pc += 8;
i64::from_le_bytes(bytes)
}
#[inline(always)]
fn read_f64(&mut self) -> f64 {
let bytes = self.code[self.pc..self.pc + 8]
.try_into()
.expect("no enough bytes");
self.pc += 8;
f64::from_le_bytes(bytes)
}
#[inline(always)]
fn read_operand_data(&mut self) {
use OperandType::*;
let tag = self.read_u8();
let ty = OperandType::try_from(tag).expect("invalid operand type");
match ty {
Const => {
self.read_u32();
}
BigInt => {
self.read_i64();
}
Local => {
self.read_u8();
self.read_u32();
}
BuiltinConst => {
self.read_u32();
}
Builtins => {}
ReplBinding => {
self.read_u32();
}
ScopedImportBinding => {
self.read_u32();
self.read_u32();
}
}
}
@@ -114,6 +30,11 @@ impl<'a, Ctx: DisassemblerContext> Disassembler<'a, Ctx> {
self.disassemble_impl(true)
}
#[expect(
clippy::let_underscore_must_use,
clippy::indexing_slicing,
reason = "disassembler operates on well-formed bytecode; writes target an in-memory String via fmt::Write, which is infallible"
)]
fn disassemble_impl(&mut self, color: bool) -> String {
let mut out = String::new();
if color {
@@ -122,24 +43,24 @@ impl<'a, Ctx: DisassemblerContext> Disassembler<'a, Ctx> {
out,
"{} {}",
"Length:".white(),
format!("{} bytes", self.code.len()).cyan()
format!("{} bytes", self.reader.len()).cyan()
);
} else {
let _ = writeln!(out, "=== Bytecode Disassembly ===");
let _ = writeln!(out, "Length: {} bytes", self.code.len());
let _ = writeln!(out, "Length: {} bytes", self.reader.len());
}
while self.pc < self.code.len() {
let start_pos = self.pc;
let op_byte = self.read_u8();
while self.reader.pc() < self.reader.len() {
let start_pos = self.reader.pc();
let op_byte = self.reader.read_u8();
let (mnemonic, args) = self.decode_instruction(op_byte, start_pos);
let bytes_slice = &self.code[start_pos + 1..self.pc];
let bytes_slice = &self.reader[start_pos + 1..self.reader.pc()];
let mut chunks = bytes_slice.chunks(4);
let first_chunk = chunks.next().unwrap_or(&[]);
let bytes_str = {
let mut temp = format!("{:02x}", self.code[start_pos]);
let mut temp = format!("{:02x}", self.reader[start_pos]);
for b in first_chunk {
let _ = write!(&mut temp, " {:02x}", b);
}
@@ -199,31 +120,36 @@ impl<'a, Ctx: DisassemblerContext> Disassembler<'a, Ctx> {
out
}
#[expect(
clippy::let_underscore_must_use,
clippy::cast_sign_loss,
reason = "disassembler operates on well-formed bytecode; jump targets are valid non-negative PCs and writes to String are infallible"
)]
fn decode_instruction(&mut self, op_byte: u8, current_pc: usize) -> (&'static str, String) {
let op = Op::try_from(op_byte).expect("invalid op code");
match op {
Op::PushSmi => {
let val = self.read_i32();
let val = self.reader.read_i32();
("PushSmi", format!("{}", val))
}
Op::PushBigInt => {
let val = self.read_i64();
let val = self.reader.read_i64();
("PushBigInt", format!("{}", val))
}
Op::PushFloat => {
let val = self.read_f64();
let val = self.reader.read_f64();
("PushFloat", format!("{}", val))
}
Op::PushString => {
let idx = self.read_u32();
let idx = self.reader.read_u32();
let s = self.ctx.resolve_string(idx);
let len = s.len();
let mut s_fmt = format!("{:?}", s);
if s_fmt.len() > 60 {
s_fmt.truncate(57);
#[allow(clippy::unwrap_used)]
write!(s_fmt, "...\" (total {len} bytes)").unwrap();
write!(s_fmt, "...\" (total {len} bytes)")
.expect("writing to String is infallible");
}
("PushString", format!("@{} {}", idx, s_fmt))
}
@@ -232,38 +158,38 @@ impl<'a, Ctx: DisassemblerContext> Disassembler<'a, Ctx> {
Op::PushFalse => ("PushFalse", String::new()),
Op::LoadLocal => {
let idx = self.read_u32();
let idx = self.reader.read_u32();
("LoadLocal", format!("[{}]", idx))
}
Op::LoadOuter => {
let depth = self.read_u8();
let idx = self.read_u32();
let depth = self.reader.read_u8();
let idx = self.reader.read_u32();
("LoadOuter", format!("depth={} [{}]", depth, idx))
}
Op::StoreLocal => {
let idx = self.read_u32();
let idx = self.reader.read_u32();
("StoreLocal", format!("[{}]", idx))
}
Op::AllocLocals => {
let count = self.read_u32();
let count = self.reader.read_u32();
("AllocLocals", format!("count={}", count))
}
Op::MakeThunk => {
let offset = self.read_u32();
let offset = self.reader.read_u32();
("MakeThunk", format!("-> {:04x}", offset))
}
Op::MakeClosure => {
let offset = self.read_u32();
let slots = self.read_u32();
let offset = self.reader.read_u32();
let slots = self.reader.read_u32();
("MakeClosure", format!("-> {:04x} slots={}", offset, slots))
}
Op::MakePatternClosure => {
let offset = self.read_u32();
let slots = self.read_u32();
let req_count = self.read_u16();
let opt_count = self.read_u16();
let ellipsis = self.read_u8() != 0;
let offset = self.reader.read_u32();
let slots = self.reader.read_u32();
let req_count = self.reader.read_u16();
let opt_count = self.reader.read_u16();
let ellipsis = self.reader.read_u8() != 0;
let mut arg_str = format!(
"-> {:04x} slots={} req={} opt={} ...={})",
@@ -272,18 +198,18 @@ impl<'a, Ctx: DisassemblerContext> Disassembler<'a, Ctx> {
arg_str.push_str(" Args=[");
for _ in 0..req_count {
let idx = self.read_u32();
let idx = self.reader.read_u32();
arg_str.push_str(&format!("Req({}) ", self.ctx.resolve_string(idx)));
}
for _ in 0..opt_count {
let idx = self.read_u32();
let idx = self.reader.read_u32();
arg_str.push_str(&format!("Opt({}) ", self.ctx.resolve_string(idx)));
}
let total_args = req_count + opt_count;
for _ in 0..total_args {
let _name_idx = self.read_u32();
let _span_id = self.read_u32();
let _name_idx = self.reader.read_u32();
let _span_id = self.reader.read_u32();
}
arg_str.push(']');
@@ -291,31 +217,32 @@ impl<'a, Ctx: DisassemblerContext> Disassembler<'a, Ctx> {
}
Op::Call => {
self.read_operand_data();
let _ = self.reader.read_operand_data();
("Call", "arg=?".into())
}
Op::DispatchCont => {
let phase = Continuation::try_from(self.read_u8()).expect("invalid primop phase");
let phase =
Continuation::try_from(self.reader.read_u8()).expect("invalid primop phase");
("DispatchPrimOp", format!("phase={phase:?}"))
}
Op::MakeAttrs => {
let static_count = self.read_u32();
let dynamic_count = self.read_u32();
let static_count = self.reader.read_u32();
let dynamic_count = self.reader.read_u32();
let mut args = format!("static={} dynamic={}", static_count, dynamic_count);
for _ in 0..static_count {
let key_id = self.read_u32();
let key_id = self.reader.read_u32();
let _ = write!(args, " [{}={}", self.ctx.resolve_string(key_id), key_id);
self.read_operand_data();
let _span_id = self.read_u32();
let _ = self.reader.read_operand_data();
let _span_id = self.reader.read_u32();
args.push(']');
}
for _ in 0..dynamic_count {
let _ = write!(args, " [dyn");
self.read_operand_data();
let _span_id = self.read_u32();
let _ = self.reader.read_operand_data();
let _span_id = self.reader.read_u32();
args.push(']');
}
@@ -324,31 +251,31 @@ impl<'a, Ctx: DisassemblerContext> Disassembler<'a, Ctx> {
Op::MakeEmptyAttrs => ("MakeEmptyAttrs", String::new()),
Op::SelectStatic => {
let span_id = self.read_u32();
let key_id = self.read_u32();
let span_id = self.reader.read_u32();
let key_id = self.reader.read_u32();
(
"SelectStatic",
format!("key={} span={}", self.ctx.resolve_string(key_id), span_id),
)
}
Op::SelectDynamic => {
let span_id = self.read_u32();
let span_id = self.reader.read_u32();
("SelectDynamic", format!("span={}", span_id))
}
Op::HasAttrPathStatic => {
let span_id = self.read_u32();
let key_id = self.read_u32();
let span_id = self.reader.read_u32();
let key_id = self.reader.read_u32();
(
"HasAttrPathStatic",
format!("key={} span={}", self.ctx.resolve_string(key_id), span_id),
)
}
Op::HasAttrPathDynamic => {
let span_id = self.read_u32();
let span_id = self.reader.read_u32();
("HasAttrPathDynamic", format!("span={}", span_id))
}
Op::HasAttrStatic => {
let key_id = self.read_u32();
let key_id = self.reader.read_u32();
(
"HasAttrStatic",
format!("key={}", self.ctx.resolve_string(key_id)),
@@ -357,7 +284,7 @@ impl<'a, Ctx: DisassemblerContext> Disassembler<'a, Ctx> {
Op::HasAttrDynamic => ("HasAttrDynamic", String::new()),
Op::HasAttrResolve => ("HasAttrResolve", String::new()),
Op::JumpIfSelectSucceeded => {
let offset = self.read_i32();
let offset = self.reader.read_i32();
let target = (current_pc as isize + 1 + 4 + offset as isize) as usize;
(
"JumpIfSelectSucceeded",
@@ -365,7 +292,7 @@ impl<'a, Ctx: DisassemblerContext> Disassembler<'a, Ctx> {
)
}
Op::JumpIfSelectFailed => {
let offset = self.read_i32();
let offset = self.reader.read_i32();
let target = (current_pc as isize + 1 + 4 + offset as isize) as usize;
(
"JumpIfSelectFailed",
@@ -374,9 +301,9 @@ impl<'a, Ctx: DisassemblerContext> Disassembler<'a, Ctx> {
}
Op::MakeList => {
let count = self.read_u32();
let count = self.reader.read_u32();
for _ in 0..count {
self.read_operand_data();
let _ = self.reader.read_operand_data();
}
("MakeList", format!("size={}", count))
}
@@ -398,7 +325,7 @@ impl<'a, Ctx: DisassemblerContext> Disassembler<'a, Ctx> {
Op::OpNot => ("OpNot", String::new()),
Op::JumpIfFalse => {
let offset = self.read_i32();
let offset = self.reader.read_i32();
let target = (current_pc as isize + 1 + 4 + offset as isize) as usize;
(
"JumpIfFalse",
@@ -406,55 +333,55 @@ impl<'a, Ctx: DisassemblerContext> Disassembler<'a, Ctx> {
)
}
Op::JumpIfTrue => {
let offset = self.read_i32();
let offset = self.reader.read_i32();
let target = (current_pc as isize + 1 + 4 + offset as isize) as usize;
("JumpIfTrue", format!("-> {:04x} offset={}", target, offset))
}
Op::Jump => {
let offset = self.read_i32();
let offset = self.reader.read_i32();
let target = (current_pc as isize + 1 + 4 + offset as isize) as usize;
("Jump", format!("-> {:04x} offset={}", target, offset))
}
Op::ConcatStrings => {
let count = self.read_u16();
let force = self.read_u8();
let count = self.reader.read_u16();
let force = self.reader.read_u8();
("ConcatStrings", format!("count={} force={}", count, force))
}
Op::CoerceToString => ("CoerceToString", String::new()),
Op::ResolvePath => {
let dir_id = self.read_u32();
let dir_id = self.reader.read_u32();
let dir = self.ctx.resolve_string(dir_id);
("ResolvePath", format!("dir={:?}", dir))
}
Op::Assert => {
let raw_idx = self.read_u32();
let span_id = self.read_u32();
let raw_idx = self.reader.read_u32();
let span_id = self.reader.read_u32();
("Assert", format!("text_id={} span={}", raw_idx, span_id))
}
Op::LookupWith => {
let idx = self.read_u32();
let idx = self.reader.read_u32();
let name = self.ctx.resolve_string(idx);
let n = self.read_u8();
let n = self.reader.read_u8();
for _ in 0..n {
self.read_operand_data();
let _ = self.reader.read_operand_data();
}
("LookupWith", format!("sym={:?} n={}", name, n))
}
Op::LoadBuiltins => ("LoadBuiltins", String::new()),
Op::LoadBuiltin => {
let id = self.read_u8();
let id = self.reader.read_u8();
("LoadBuiltin", format!("id={}", id))
}
Op::LoadReplBinding => {
let idx = self.read_u32();
let idx = self.reader.read_u32();
let name = self.ctx.resolve_string(idx);
("LoadReplBinding", format!("{:?}", name))
}
Op::LoadScopedBinding => {
let slot = self.read_u32();
let idx = self.read_u32();
let slot = self.reader.read_u32();
let idx = self.reader.read_u32();
let name = self.ctx.resolve_string(idx);
("LoadScopedBinding", format!("slot={} {:?}", slot, name))
}
+162 -90
View File
@@ -1,4 +1,7 @@
#![allow(dead_code)]
#![allow(
dead_code,
reason = "crate is under active development; some opcodes and helpers are not yet wired up"
)]
use fix_lang::{BuiltinId, StringId};
use num_enum::TryFromPrimitive;
@@ -10,8 +13,7 @@ pub mod disassembler;
pub struct InstructionPtr(pub usize);
#[repr(u8)]
#[derive(Debug, Clone, Copy, TryFromPrimitive)]
#[allow(clippy::enum_variant_names)]
#[derive(Debug, Clone, Copy)]
pub enum Op {
PushSmi,
PushBigInt,
@@ -88,6 +90,21 @@ pub enum Op {
Illegal,
}
impl TryFrom<u8> for Op {
type Error = u8;
#[inline(always)]
fn try_from(value: u8) -> Result<Self, Self::Error> {
if (0..Self::Illegal as u8).contains(&value) {
// SAFETY: `value` is in `0..Illegal`, which are exactly the valid
// discriminants of this `#[repr(u8)]` enum, so the transmute is sound.
Ok(unsafe { std::mem::transmute::<u8, Self>(value) })
} else {
Err(value)
}
}
}
#[repr(u8)]
#[derive(Debug, Clone, Copy, TryFromPrimitive)]
pub enum OperandType {
@@ -106,11 +123,7 @@ pub enum Const {
Bool(bool),
String(StringId),
Path(StringId),
PrimOp {
id: BuiltinId,
arity: u8,
dispatch_ip: u32,
},
PrimOp(BuiltinId),
Null,
}
@@ -286,8 +299,11 @@ pub enum Continuation {
impl TryFrom<u8> for Continuation {
type Error = u8;
#[inline(always)]
fn try_from(value: u8) -> Result<Self, Self::Error> {
if (0..Self::Illegal as u8).contains(&value) {
// SAFETY: `value` is in `0..Illegal`, which are exactly the valid
// discriminants of this `#[repr(u8)]` enum, so the transmute is sound.
Ok(unsafe { std::mem::transmute::<u8, Self>(value) })
} else {
Err(value)
@@ -296,7 +312,7 @@ impl TryFrom<u8> for Continuation {
}
impl Continuation {
pub fn entry_for_builtin(id: BuiltinId) -> Self {
pub const fn entry_for_builtin(id: BuiltinId) -> Self {
use BuiltinId::*;
match id {
Abort => Self::PAbort,
@@ -405,7 +421,7 @@ impl Continuation {
}
}
pub fn ip(self) -> u32 {
pub const fn ip(self) -> u32 {
self as u32 * 2
}
}
@@ -416,7 +432,129 @@ pub struct BytecodeReader<'a> {
inst_start_pc: usize,
}
impl std::ops::Deref for BytecodeReader<'_> {
type Target = [u8];
fn deref(&self) -> &Self::Target {
self.bytecode
}
}
pub trait FromBytecode {
#[must_use]
fn read(reader: &mut BytecodeReader<'_>) -> Self;
}
impl<const N: usize> FromBytecode for [u8; N] {
#[inline(always)]
#[expect(
clippy::indexing_slicing,
reason = "reads well-formed bytecode; an out-of-range PC indicates a codegen bug and should panic loudly"
)]
fn read(reader: &mut BytecodeReader<'_>) -> Self {
let ret = reader.bytecode[reader.pc..reader.pc + N]
.try_into()
.expect("read_array failed");
reader.pc += N;
ret
}
}
impl FromBytecode for u8 {
#[inline(always)]
fn read(reader: &mut BytecodeReader<'_>) -> Self {
let [byte] = reader.read();
byte
}
}
macro_rules! numbers_impl {
( $($ty:ty),* ) => {
$(
impl FromBytecode for $ty {
#[inline(always)]
fn read(reader: &mut BytecodeReader<'_>) -> Self {
<$ty>::from_le_bytes(FromBytecode::read(reader))
}
}
)*
};
}
numbers_impl!(u16, u32, i32, i64, f64);
impl FromBytecode for Op {
#[inline(always)]
#[expect(
clippy::panic,
reason = "reads well-formed bytecode; an unknown opcode indicates a codegen bug and should panic loudly"
)]
fn read(reader: &mut BytecodeReader<'_>) -> Self {
reader.inst_start_pc = reader.pc;
let byte: u8 = reader.read();
byte.try_into().unwrap_or_else(|byte| {
std::hint::cold_path();
panic!("unknown opcode: {byte:#04x}")
})
}
}
impl FromBytecode for StringId {
#[inline(always)]
fn read(reader: &mut BytecodeReader<'_>) -> Self {
let raw: u32 = reader.read();
StringId(
string_interner::symbol::SymbolU32::try_from_usize(raw as usize)
.expect("raw is a valid interner symbol index"),
)
}
}
impl FromBytecode for OperandData {
#[inline(always)]
#[expect(
clippy::panic,
reason = "an unknown operand tag in well-formed bytecode indicates a codegen bug and should panic loudly"
)]
fn read(reader: &mut BytecodeReader<'_>) -> Self {
let tag = reader.read();
let ty = OperandType::try_from_primitive(tag).unwrap_or_else(|err| {
std::hint::cold_path();
panic!("unknown operand tag: {:#04x}", err.number)
});
match ty {
OperandType::Const => OperandData::Const(reader.read()),
OperandType::BigInt => OperandData::BigInt(reader.read()),
OperandType::Local => {
let layer = reader.read();
let idx = reader.read();
OperandData::Local { layer, idx }
}
OperandType::BuiltinConst => OperandData::BuiltinConst(reader.read()),
OperandType::Builtins => OperandData::Builtins,
OperandType::ReplBinding => OperandData::ReplBinding(reader.read()),
OperandType::ScopedImportBinding => {
let slot_id = reader.read();
let name = reader.read();
OperandData::ScopedImportBinding { slot_id, name }
}
}
}
}
macro_rules! read_aliases {
{ $($alias:ident -> $ty:ty),*$(,)? } => {
$(
#[inline(always)]
#[must_use]
pub fn $alias(&mut self) -> $ty {
<$ty>::read(self)
}
)*
};
}
impl<'a> BytecodeReader<'a> {
#[must_use]
pub fn new(bytecode: &'a [u8], pc: usize) -> Self {
Self {
bytecode,
@@ -426,6 +564,7 @@ impl<'a> BytecodeReader<'a> {
}
#[inline(always)]
#[must_use]
pub fn from_after_op(bytecode: &'a [u8], inst_start_pc: usize) -> Self {
Self {
bytecode,
@@ -435,88 +574,21 @@ impl<'a> BytecodeReader<'a> {
}
#[inline(always)]
#[cfg_attr(debug_assertions, track_caller)]
fn read_array<const N: usize>(&mut self) -> [u8; N] {
let ret = self.bytecode[self.pc..self.pc + N]
.try_into()
.expect("read_array failed");
self.pc += N;
ret
#[must_use]
pub fn read<T: FromBytecode>(&mut self) -> T {
T::read(self)
}
#[inline(always)]
pub fn read_op(&mut self) -> Op {
self.inst_start_pc = self.pc;
let byte = self.bytecode[self.pc];
if !(0..Op::Illegal as u8).contains(&byte) {
std::hint::cold_path();
panic!("unknown opcode: {byte:#04x}")
}
self.pc += 1;
unsafe { std::mem::transmute::<u8, Op>(byte) }
}
#[inline(always)]
pub fn read_u8(&mut self) -> u8 {
let val = self.bytecode[self.pc];
self.pc += 1;
val
}
#[inline(always)]
pub fn read_u16(&mut self) -> u16 {
u16::from_le_bytes(self.read_array())
}
#[inline(always)]
pub fn read_u32(&mut self) -> u32 {
u32::from_le_bytes(self.read_array())
}
#[inline(always)]
pub fn read_i32(&mut self) -> i32 {
i32::from_le_bytes(self.read_array())
}
#[inline(always)]
pub fn read_i64(&mut self) -> i64 {
i64::from_le_bytes(self.read_array())
}
#[inline(always)]
pub fn read_f64(&mut self) -> f64 {
f64::from_le_bytes(self.read_array())
}
#[inline(always)]
pub 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 fn read_operand_data(&mut self) -> 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 => OperandData::Const(self.read_u32()),
OperandType::BigInt => OperandData::BigInt(self.read_i64()),
OperandType::Local => {
let layer = self.read_u8();
let idx = self.read_u32();
OperandData::Local { layer, idx }
}
OperandType::BuiltinConst => OperandData::BuiltinConst(self.read_string_id()),
OperandType::Builtins => OperandData::Builtins,
OperandType::ReplBinding => OperandData::ReplBinding(self.read_string_id()),
OperandType::ScopedImportBinding => {
let slot_id = self.read_u32();
let name = self.read_string_id();
OperandData::ScopedImportBinding { slot_id, name }
}
}
read_aliases! {
read_op -> Op,
read_u8 -> u8,
read_u16 -> u16,
read_u32 -> u32,
read_i32 -> i32,
read_i64 -> i64,
read_f64 -> f64,
read_string_id -> StringId,
read_operand_data -> OperandData,
}
pub fn pc(&self) -> usize {