Skip to content

Commit 77b84e1

Browse files
authored
refactor: VM receives bootstrap address as runtime parameter (#260)
1 parent 0bee91f commit 77b84e1

16 files changed

Lines changed: 95 additions & 100 deletions

File tree

crates/plotnik-cli/src/commands/exec.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ pub fn run(args: ExecArgs) {
3838
});
3939

4040
let vm = VM::new(&tree, trivia_types, FuelLimits::default());
41-
let effects = match vm.execute(&module, &entrypoint) {
41+
let effects = match vm.execute(&module, 0, &entrypoint) {
4242
Ok(effects) => effects,
4343
Err(RuntimeError::NoMatch) => {
4444
std::process::exit(1);

crates/plotnik-cli/src/commands/trace.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ pub fn run(args: TraceArgs) {
4848
let colors = Colors::new(args.color);
4949
let mut tracer = PrintTracer::new(&source_code, &module, args.verbosity, colors);
5050

51-
let effects = match vm.execute_with(&module, &entrypoint, &mut tracer) {
51+
let effects = match vm.execute_with(&module, 0, &entrypoint, &mut tracer) {
5252
Ok(effects) => {
5353
tracer.print();
5454
effects

crates/plotnik-lib/src/bytecode/dump.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ use std::fmt::Write as _;
88
use crate::colors::Colors;
99

1010
use super::format::{LineBuilder, Symbol, format_effect, nav_symbol_epsilon, width_for_count};
11-
use super::ids::{QTypeId, StepId};
11+
use super::ids::QTypeId;
12+
use super::instructions::StepId;
1213
use super::module::{Instruction, Module};
1314
use super::type_meta::TypeKind;
1415
use super::{Call, Match, Return, Trampoline};
@@ -73,12 +74,12 @@ impl DumpContext {
7374
let node_fields = module.node_fields();
7475

7576
let mut step_labels = BTreeMap::new();
76-
// Preamble always starts at step 0
77+
// Preamble always at step 0 (first in layout)
7778
step_labels.insert(0, "_ObjWrap".to_string());
7879
for i in 0..entrypoints.len() {
7980
let ep = entrypoints.get(i);
8081
let name = strings.get(ep.name).to_string();
81-
step_labels.insert(ep.target.get(), name);
82+
step_labels.insert(ep.target, name);
8283
}
8384

8485
let mut node_type_names = BTreeMap::new();
@@ -319,7 +320,7 @@ fn dump_entrypoints(out: &mut String, module: &Module, ctx: &DumpContext) {
319320
.map(|i| {
320321
let ep = entrypoints.get(i);
321322
let name = strings.get(ep.name);
322-
(name, ep.target.0, ep.result_type.0)
323+
(name, ep.target, ep.result_type.0)
323324
})
324325
.collect();
325326
entries.sort_by_key(|(name, _, _)| *name);

crates/plotnik-lib/src/bytecode/entrypoint.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
11
//! Entrypoint section types.
22
3-
use super::{QTypeId, StepId, StringId};
3+
use super::instructions::StepAddr;
4+
use super::{QTypeId, StringId};
45

56
/// Named query definition entry point (8 bytes).
67
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
78
#[repr(C)]
89
pub struct Entrypoint {
910
/// Definition name.
1011
pub name: StringId,
11-
/// Starting instruction (StepId).
12-
pub target: StepId,
12+
/// Starting instruction address.
13+
pub target: StepAddr,
1314
/// Result type.
1415
pub result_type: QTypeId,
1516
pub(crate) _pad: u16,

crates/plotnik-lib/src/bytecode/ids.rs

Lines changed: 0 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -2,35 +2,6 @@
22
33
use std::num::NonZeroU16;
44

5-
use super::constants::STEP_SIZE;
6-
7-
/// Index into the Transitions section (8-byte steps).
8-
///
9-
/// Step 0 is a valid address (preamble starts there).
10-
/// In successor fields, raw value 0 means "terminal" — this sentinel
11-
/// is handled by decoding logic, not by the type.
12-
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
13-
#[repr(transparent)]
14-
pub struct StepId(pub u16);
15-
16-
impl StepId {
17-
#[inline]
18-
pub fn new(n: u16) -> Self {
19-
Self(n)
20-
}
21-
22-
/// Get the raw u16 value.
23-
#[inline]
24-
pub fn get(self) -> u16 {
25-
self.0
26-
}
27-
28-
#[inline]
29-
pub fn byte_offset(self) -> usize {
30-
self.0 as usize * STEP_SIZE
31-
}
32-
}
33-
345
/// Index into the String Table.
356
///
367
/// Uses NonZeroU16 to make StringId(0) unrepresentable - index 0 is
@@ -58,15 +29,3 @@ impl StringId {
5829
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Default)]
5930
#[repr(transparent)]
6031
pub struct QTypeId(pub u16);
61-
62-
#[cfg(test)]
63-
mod tests {
64-
use super::*;
65-
66-
#[test]
67-
fn step_id_byte_offset() {
68-
assert_eq!(StepId::new(0).byte_offset(), 0);
69-
assert_eq!(StepId::new(1).byte_offset(), 8);
70-
assert_eq!(StepId::new(10).byte_offset(), 80);
71-
}
72-
}

crates/plotnik-lib/src/bytecode/instructions.rs

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,37 @@ use std::num::NonZeroU16;
77

88
use super::constants::{SECTION_ALIGN, STEP_SIZE};
99
use super::effects::EffectOp;
10-
use super::ids::StepId;
1110
use super::nav::Nav;
1211

12+
/// Step address in bytecode (raw u16).
13+
///
14+
/// Used for layout addresses, entrypoint targets, bootstrap parameter, etc.
15+
/// For decoded instruction successors (where 0 = terminal), use [`StepId`] instead.
16+
pub type StepAddr = u16;
17+
18+
/// Successor step address in decoded instructions.
19+
///
20+
/// Uses NonZeroU16 because raw 0 means "terminal" (no successor).
21+
/// This type is only for decoded instruction successors - use raw `u16`
22+
/// for addresses in layout, entrypoints, and VM internals.
23+
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
24+
#[repr(transparent)]
25+
pub struct StepId(pub NonZeroU16);
26+
27+
impl StepId {
28+
/// Create a new StepId. Panics if n == 0.
29+
#[inline]
30+
pub fn new(n: u16) -> Self {
31+
Self(NonZeroU16::new(n).expect("StepId cannot be 0"))
32+
}
33+
34+
/// Get the raw u16 value.
35+
#[inline]
36+
pub fn get(self) -> u16 {
37+
self.0.get()
38+
}
39+
}
40+
1341
/// Read `count` little-endian u16 values from bytes starting at `offset`.
1442
/// Advances `offset` by `count * 2`.
1543
#[inline]
@@ -178,7 +206,7 @@ impl Match {
178206
let successors = if next_raw == 0 {
179207
vec![] // terminal
180208
} else {
181-
vec![StepId(next_raw)]
209+
vec![StepId::new(next_raw)]
182210
};
183211

184212
Self {
@@ -417,7 +445,7 @@ impl<'a> MatchView<'a> {
417445
if self.is_match8 {
418446
debug_assert!(idx == 0);
419447
debug_assert!(self.match8_next != 0, "terminal has no successors");
420-
StepId(self.match8_next)
448+
StepId::new(self.match8_next)
421449
} else {
422450
let offset = self.succ_offset() + idx * 2;
423451
StepId::new(u16::from_le_bytes([

crates/plotnik-lib/src/bytecode/instructions_tests.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,8 @@
33
use std::num::NonZeroU16;
44

55
use super::effects::{EffectOp, EffectOpcode};
6-
use super::ids::StepId;
76
use super::instructions::{
8-
Call, Match, MatchView, Opcode, Return, align_to_section, select_match_opcode,
7+
Call, Match, MatchView, Opcode, Return, StepId, align_to_section, select_match_opcode,
98
};
109
use super::nav::Nav;
1110

crates/plotnik-lib/src/bytecode/ir.rs

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,25 @@
11
//! Instruction IR with symbolic labels.
22
//!
33
//! Pre-layout instructions use `Label` for symbolic references.
4-
//! After layout, labels are resolved to `StepId` for serialization.
4+
//! After layout, labels are resolved to step addresses (u16) for serialization.
55
//! Member indices use deferred resolution via `MemberRef`.
66
77
use std::collections::BTreeMap;
88
use std::num::NonZeroU16;
99

1010
use super::effects::{EffectOp, EffectOpcode};
11-
use super::ids::StepId;
12-
use super::instructions::{Call, Match, Return, Trampoline, select_match_opcode};
11+
use super::instructions::{Call, Match, Return, StepAddr, StepId, Trampoline, select_match_opcode};
1312
use super::nav::Nav;
1413
use crate::analyze::type_check::TypeId;
1514

16-
/// Symbolic reference, resolved to StepId at layout time.
15+
/// Symbolic reference, resolved to step address at layout time.
1716
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
1817
pub struct Label(pub u32);
1918

2019
impl Label {
21-
/// Resolve this label to a StepId using the layout mapping.
20+
/// Resolve this label to a step address using the layout mapping.
2221
#[inline]
23-
pub fn resolve(self, map: &BTreeMap<Label, StepId>) -> StepId {
22+
pub fn resolve(self, map: &BTreeMap<Label, StepAddr>) -> StepAddr {
2423
*map.get(&self).expect("label not in layout")
2524
}
2625
}
@@ -197,7 +196,7 @@ impl Instruction {
197196
/// - `get_member_base`: maps parent TypeId to member base index
198197
pub fn resolve<F, G>(
199198
&self,
200-
map: &BTreeMap<Label, StepId>,
199+
map: &BTreeMap<Label, StepAddr>,
201200
lookup_member: F,
202201
get_member_base: G,
203202
) -> Vec<u8>
@@ -263,15 +262,19 @@ impl MatchIR {
263262
/// - `get_member_base`: maps parent TypeId to member base index
264263
pub fn resolve<F, G>(
265264
&self,
266-
map: &BTreeMap<Label, StepId>,
265+
map: &BTreeMap<Label, StepAddr>,
267266
lookup_member: F,
268267
get_member_base: G,
269268
) -> Vec<u8>
270269
where
271270
F: Fn(plotnik_core::Symbol, TypeId) -> Option<u16>,
272271
G: Fn(TypeId) -> Option<u16>,
273272
{
274-
let successors: Vec<StepId> = self.successors.iter().map(|&l| l.resolve(map)).collect();
273+
let successors: Vec<StepId> = self
274+
.successors
275+
.iter()
276+
.map(|&l| StepId::new(l.resolve(map)))
277+
.collect();
275278

276279
// Resolve effect member references to absolute indices
277280
let pre_effects: Vec<EffectOp> = self
@@ -323,13 +326,13 @@ pub struct CallIR {
323326

324327
impl CallIR {
325328
/// Resolve labels and serialize to bytecode bytes.
326-
pub fn resolve(&self, map: &BTreeMap<Label, StepId>) -> [u8; 8] {
329+
pub fn resolve(&self, map: &BTreeMap<Label, StepAddr>) -> [u8; 8] {
327330
let c = Call {
328331
segment: 0,
329332
nav: self.nav,
330333
node_field: self.node_field,
331-
next: self.next.resolve(map),
332-
target: self.target.resolve(map),
334+
next: StepId::new(self.next.resolve(map)),
335+
target: StepId::new(self.target.resolve(map)),
333336
};
334337
c.to_bytes()
335338
}
@@ -364,20 +367,20 @@ pub struct TrampolineIR {
364367

365368
impl TrampolineIR {
366369
/// Resolve labels and serialize to bytecode bytes.
367-
pub fn resolve(&self, map: &BTreeMap<Label, StepId>) -> [u8; 8] {
370+
pub fn resolve(&self, map: &BTreeMap<Label, StepAddr>) -> [u8; 8] {
368371
let t = Trampoline {
369372
segment: 0,
370-
next: self.next.resolve(map),
373+
next: StepId::new(self.next.resolve(map)),
371374
};
372375
t.to_bytes()
373376
}
374377
}
375378

376-
/// Result of layout: maps labels to step IDs.
379+
/// Result of layout: maps labels to step addresses.
377380
#[derive(Clone, Debug)]
378381
pub struct LayoutResult {
379-
/// Mapping from symbolic labels to concrete step IDs.
380-
pub label_to_step: BTreeMap<Label, StepId>,
382+
/// Mapping from symbolic labels to concrete step addresses (raw u16).
383+
pub label_to_step: BTreeMap<Label, StepAddr>,
381384
/// Total number of steps (for header).
382385
pub total_steps: u16,
383386
}
@@ -464,7 +467,7 @@ mod tests {
464467
};
465468

466469
let mut map = BTreeMap::new();
467-
map.insert(Label(0), StepId::new(1));
470+
map.insert(Label(0), 1u16);
468471

469472
let bytes = m.resolve(&map, |_, _| None, |_| None);
470473
assert_eq!(bytes.len(), 8);

crates/plotnik-lib/src/bytecode/mod.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ mod type_meta;
1818

1919
pub use constants::{MAGIC, SECTION_ALIGN, STEP_SIZE, VERSION};
2020

21-
pub use ids::{QTypeId, StepId, StringId};
21+
pub use ids::{QTypeId, StringId};
2222

2323
pub use header::{Header, flags};
2424

@@ -33,7 +33,8 @@ pub use nav::Nav;
3333
pub use effects::{EffectOp, EffectOpcode};
3434

3535
pub use instructions::{
36-
Call, Match, MatchView, Opcode, Return, Trampoline, align_to_section, select_match_opcode,
36+
Call, Match, MatchView, Opcode, Return, StepAddr, StepId, Trampoline, align_to_section,
37+
select_match_opcode,
3738
};
3839

3940
pub use module::{

crates/plotnik-lib/src/bytecode/module.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use std::ops::Deref;
88
use std::path::Path;
99

1010
use super::header::Header;
11-
use super::ids::{QTypeId, StepId, StringId};
11+
use super::ids::{QTypeId, StringId};
1212
use super::instructions::{Call, Match, MatchView, Opcode, Return, Trampoline};
1313
use super::sections::{FieldSymbol, NodeSymbol, TriviaEntry};
1414
use super::type_meta::{TypeDef, TypeMember, TypeMetaHeader, TypeName};
@@ -521,7 +521,7 @@ impl<'a> EntrypointsView<'a> {
521521
let offset = idx * 8;
522522
Entrypoint {
523523
name: StringId::new(read_u16_le(self.bytes, offset)),
524-
target: StepId::new(read_u16_le(self.bytes, offset + 2)),
524+
target: read_u16_le(self.bytes, offset + 2),
525525
result_type: QTypeId(read_u16_le(self.bytes, offset + 4)),
526526
_pad: 0,
527527
}

0 commit comments

Comments
 (0)