Skip to content

Commit 45461e8

Browse files
Chore: Break up monolithic dispatch loop in 'vm/mod.rs'.
1 parent 51b56a9 commit 45461e8

File tree

1 file changed

+94
-69
lines changed

1 file changed

+94
-69
lines changed

compiler/src/modules/vm/mod.rs

Lines changed: 94 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,90 @@ impl<'a> VM<'a> {
5555
slots
5656
}
5757

58+
/*
59+
Budget-Checked Jump
60+
Decrements op budget, validates target, returns new ip.
61+
*/
62+
63+
#[inline]
64+
fn checked_jump(&mut self, target: usize, limit: usize) -> Result<usize, VmErr> {
65+
if self.budget == 0 { return Err(cold_budget()); }
66+
self.budget -= 1;
67+
if target > limit { return Err(VmErr::Runtime("jump target out of bounds".into())); }
68+
Ok(target)
69+
}
70+
71+
/*
72+
Iterator Frame Construction
73+
Converts a heap object to the appropriate IterFrame for ForIter dispatch.
74+
*/
75+
76+
fn make_iter_frame(&mut self, obj: Val) -> Result<IterFrame, VmErr> {
77+
if !obj.is_heap() { return Err(VmErr::Type("not iterable".into())); }
78+
Ok(match self.heap.get(obj) {
79+
HeapObj::Range(s, e, st) => IterFrame::Range { cur: *s, end: *e, step: *st },
80+
HeapObj::List(v) => IterFrame::Seq { items: v.borrow().clone(), idx: 0 },
81+
HeapObj::Tuple(v) => IterFrame::Seq { items: v.clone(), idx: 0 },
82+
HeapObj::Dict(p) => IterFrame::Seq { items: p.borrow().keys().copied().collect(), idx: 0 },
83+
HeapObj::Set(s) => IterFrame::Seq { items: s.borrow().clone(), idx: 0 },
84+
HeapObj::Str(s) => {
85+
let chars: Vec<char> = s.chars().collect();
86+
drop(s);
87+
let mut items = Vec::with_capacity(chars.len());
88+
for c in chars { items.push(self.heap.alloc(HeapObj::Str(c.to_string()))?); }
89+
IterFrame::Seq { items, idx: 0 }
90+
}
91+
_ => return Err(VmErr::Type("not iterable".into())),
92+
})
93+
}
94+
95+
/*
96+
Sequence Unpack
97+
Destructures list, tuple, or string into exactly `expected` stack values.
98+
*/
99+
100+
fn exec_unpack_seq(&mut self, expected: usize) -> Result<(), VmErr> {
101+
let obj = self.pop()?;
102+
if !obj.is_heap() { return Err(VmErr::Type("cannot unpack non-sequence".into())); }
103+
let items: Vec<Val> = match self.heap.get(obj) {
104+
HeapObj::List(v) => v.borrow().clone(),
105+
HeapObj::Tuple(v) => v.clone(),
106+
HeapObj::Str(s) => {
107+
let chars: Vec<char> = s.chars().collect();
108+
drop(s);
109+
if chars.len() != expected {
110+
return Err(VmErr::Value(format!("expected {} values to unpack, got {}", expected, chars.len())));
111+
}
112+
let mut out = Vec::with_capacity(expected);
113+
for c in chars { out.push(self.heap.alloc(HeapObj::Str(c.to_string()))?); }
114+
out
115+
}
116+
_ => return Err(VmErr::Type("unpack".into())),
117+
};
118+
if items.len() != expected {
119+
return Err(VmErr::Value(format!("expected {} values to unpack, got {}", expected, items.len())));
120+
}
121+
for item in items.into_iter().rev() { self.push(item); }
122+
Ok(())
123+
}
124+
125+
/*
126+
SSA Phi Propagation
127+
Merges two SSA branches into target slot and back-propagates through prev_slots chain.
128+
*/
129+
130+
fn exec_phi(op: u16, rip: usize, phi_map: &[usize], slots: &mut Vec<Option<Val>>, prev_slots: &[Option<u16>], phi_sources: &[(u16, u16)]) {
131+
let target = op as usize;
132+
let (ia, ib) = phi_sources[phi_map[rip]];
133+
let val = slots[ia as usize].or(slots[ib as usize]).unwrap_or(Val::none());
134+
slots[target] = Some(val);
135+
let mut cur = target;
136+
while let Some(prev) = prev_slots.get(cur).and_then(|p| *p) {
137+
slots[prev as usize] = Some(val);
138+
cur = prev as usize;
139+
}
140+
}
141+
58142
pub fn with_limits(chunk: &'a SSAChunk, limits: Limits) -> Self {
59143
let mut vm = Self {
60144
stack: Vec::with_capacity(256),
@@ -363,23 +447,9 @@ impl<'a> VM<'a> {
363447

364448
OpCode::JumpIfFalse => {
365449
let v = self.pop()?;
366-
if !self.truthy(v) {
367-
if self.budget == 0 { return Err(cold_budget()); }
368-
self.budget -= 1;
369-
let target = op as usize;
370-
if target > chunk.instructions.len() { return Err(VmErr::Runtime("jump target out of bounds".into())); }
371-
ip = target;
372-
}
373-
}
374-
OpCode::Jump => {
375-
if self.budget == 0 { return Err(cold_budget()); }
376-
self.budget -= 1;
377-
let target = op as usize;
378-
if target > chunk.instructions.len() {
379-
return Err(VmErr::Runtime("jump target out of bounds".into()));
380-
}
381-
ip = target;
450+
if !self.truthy(v) { ip = self.checked_jump(op as usize, n)?; }
382451
}
452+
OpCode::Jump => { ip = self.checked_jump(op as usize, n)?; }
383453
OpCode::PopTop => { self.pop()?; }
384454
OpCode::Dup2 => {
385455
let b = self.pop()?;
@@ -405,11 +475,9 @@ impl<'a> VM<'a> {
405475

406476
OpCode::BuildList => { let v = self.pop_n(op as usize)?; let val = self.heap.alloc(HeapObj::List(Rc::new(RefCell::new(v))))?; self.push(val); }
407477
OpCode::BuildTuple => { let v = self.pop_n(op as usize)?; let val = self.heap.alloc(HeapObj::Tuple(v))?; self.push(val); }
408-
OpCode::BuildDict => {
409-
let mut pairs: Vec<(Val, Val)> = Vec::with_capacity(op as usize);
410-
for _ in 0..op { let v = self.pop()?; let k = self.pop()?; pairs.push((k, v)); }
411-
pairs.reverse();
412-
let dm = DictMap::from_pairs(pairs);
478+
OpCode::BuildDict => {
479+
let flat = self.pop_n(op as usize * 2)?;
480+
let dm = DictMap::from_pairs(flat.chunks(2).map(|c| (c[0], c[1])).collect());
413481
let val = self.heap.alloc(HeapObj::Dict(Rc::new(RefCell::new(dm))))?; self.push(val);
414482
}
415483
OpCode::BuildString => {
@@ -421,25 +489,7 @@ impl<'a> VM<'a> {
421489
OpCode::BuildSlice => { self.build_slice(op)?; }
422490
OpCode::GetItem => { self.get_item()?; }
423491
OpCode::StoreItem => { self.store_item()?; }
424-
OpCode::UnpackSequence => {
425-
let obj = self.pop()?; let expected = op as usize;
426-
if !obj.is_heap() { return Err(VmErr::Type("cannot unpack non-sequence".into())); }
427-
let items: Vec<Val> = match self.heap.get(obj) {
428-
HeapObj::List(v) => v.borrow().clone(),
429-
HeapObj::Tuple(v) => v.clone(),
430-
HeapObj::Str(s) => {
431-
let chars: Vec<char> = s.chars().collect();
432-
if chars.len() != expected { return Err(VmErr::Value(format!("expected {} values to unpack, got {}", expected, chars.len()))); }
433-
let chars = chars; drop(s);
434-
let mut out = Vec::with_capacity(chars.len());
435-
for c in chars { out.push(self.heap.alloc(HeapObj::Str(c.to_string()))?); }
436-
out
437-
}
438-
_ => return Err(VmErr::Type("unpack".into())),
439-
};
440-
if items.len() != expected { return Err(VmErr::Value(format!("expected {} values to unpack, got {}", expected, items.len()))); }
441-
for item in items.into_iter().rev() { self.push(item); }
442-
}
492+
OpCode::UnpackSequence => { self.exec_unpack_seq(op as usize)?; }
443493
OpCode::UnpackEx => { self.unpack_ex(op)?; }
444494
OpCode::FormatValue => {
445495
if op == 1 { self.pop()?; }
@@ -451,21 +501,7 @@ impl<'a> VM<'a> {
451501

452502
OpCode::GetIter => {
453503
let obj = self.pop()?;
454-
if !obj.is_heap() { return Err(VmErr::Type("not iterable".into())); }
455-
let frame = match self.heap.get(obj) {
456-
HeapObj::Range(s, e, st) => IterFrame::Range { cur: *s, end: *e, step: *st },
457-
HeapObj::List(v) => IterFrame::Seq { items: v.borrow().clone(), idx: 0 },
458-
HeapObj::Tuple(v) => IterFrame::Seq { items: v.clone(), idx: 0 },
459-
HeapObj::Dict(p) => IterFrame::Seq { items: p.borrow().keys().copied().collect(), idx: 0 },
460-
HeapObj::Set(s) => IterFrame::Seq { items: s.borrow().clone(), idx: 0 },
461-
HeapObj::Str(s) => {
462-
let chars: Vec<char> = s.chars().collect(); drop(s);
463-
let mut items = Vec::with_capacity(chars.len());
464-
for c in chars { items.push(self.heap.alloc(HeapObj::Str(c.to_string()))?); }
465-
IterFrame::Seq { items, idx: 0 }
466-
}
467-
_ => return Err(VmErr::Type("not iterable".into())),
468-
};
504+
let frame = self.make_iter_frame(obj)?;
469505
self.iter_stack.push(frame);
470506
}
471507
OpCode::ForIter => {
@@ -476,26 +512,15 @@ impl<'a> VM<'a> {
476512
Some(item) => self.push(item),
477513
None => {
478514
self.iter_stack.pop();
479-
let target = op as usize;
480-
if target > chunk.instructions.len() { return Err(VmErr::Runtime("for iter target out of bounds".into())); }
481-
ip = target;
515+
if op as usize > n { return Err(VmErr::Runtime("jump target out of bounds".into())); }
516+
ip = op as usize;
482517
}
483518
}
484519
}
485520

486521
// SSA Phi
487522

488-
OpCode::Phi => {
489-
let target = op as usize;
490-
let (ia, ib) = chunk.phi_sources[phi_map[rip]];
491-
let val = slots[ia as usize].or(slots[ib as usize]).unwrap_or(Val::none());
492-
slots[target] = Some(val);
493-
let mut cur = target;
494-
while let Some(prev) = prev_slots.get(cur).and_then(|p| *p) {
495-
slots[prev as usize] = Some(val);
496-
cur = prev as usize;
497-
}
498-
}
523+
OpCode::Phi => { Self::exec_phi(op, rip, &phi_map, slots, prev_slots, &chunk.phi_sources); }
499524

500525
// Functions
501526

0 commit comments

Comments
 (0)