From 419d391b11b19ca792fe3c3a52369efbbe17efac Mon Sep 17 00:00:00 2001 From: Sergei Zharinov Date: Wed, 31 Dec 2025 09:15:40 -0300 Subject: [PATCH 1/2] feat: add bytecode IR with symbolic labels --- crates/plotnik-lib/src/bytecode/ir.rs | 315 ++++++++++++++++++ crates/plotnik-lib/src/bytecode/layout.rs | 184 ++++++++++ .../plotnik-lib/src/bytecode/layout_tests.rs | 243 ++++++++++++++ crates/plotnik-lib/src/bytecode/mod.rs | 4 + 4 files changed, 746 insertions(+) create mode 100644 crates/plotnik-lib/src/bytecode/ir.rs create mode 100644 crates/plotnik-lib/src/bytecode/layout.rs create mode 100644 crates/plotnik-lib/src/bytecode/layout_tests.rs diff --git a/crates/plotnik-lib/src/bytecode/ir.rs b/crates/plotnik-lib/src/bytecode/ir.rs new file mode 100644 index 00000000..e42485dc --- /dev/null +++ b/crates/plotnik-lib/src/bytecode/ir.rs @@ -0,0 +1,315 @@ +//! Instruction IR with symbolic labels. +//! +//! Pre-layout instructions use `Label` for symbolic references. +//! After layout, labels are resolved to `StepId` for serialization. + +use std::collections::BTreeMap; +use std::num::NonZeroU16; + +use super::effects::EffectOp; +use super::ids::StepId; +use super::instructions::{Call, Match, Return, select_match_opcode}; +use super::nav::Nav; + +/// Symbolic reference, resolved to StepId at layout time. +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] +pub struct Label(pub u32); + +impl Label { + /// Sentinel for terminal (accept) state. + pub const ACCEPT: Label = Label(u32::MAX); + + #[inline] + pub fn is_accept(self) -> bool { + self.0 == u32::MAX + } + + /// Resolve this label to a StepId using the layout mapping. + #[inline] + pub fn resolve(self, map: &BTreeMap) -> StepId { + if self.is_accept() { + return StepId::ACCEPT; + } + *map.get(&self).unwrap_or(&StepId::ACCEPT) + } +} + +/// Pre-layout instruction with symbolic references. +#[derive(Clone, Debug)] +pub enum Instruction { + Match(MatchIR), + Call(CallIR), + Return(ReturnIR), +} + +impl Instruction { + /// Get the label where this instruction lives. + #[inline] + pub fn label(&self) -> Label { + match self { + Self::Match(m) => m.label, + Self::Call(c) => c.label, + Self::Return(r) => r.label, + } + } + + /// Compute instruction size in bytes (8, 16, 24, 32, 48, or 64). + pub fn size(&self) -> usize { + match self { + Self::Match(m) => m.size(), + Self::Call(_) | Self::Return(_) => 8, + } + } + + /// Get all successor labels (for graph building). + pub fn successors(&self) -> Vec