1use std::collections::{BTreeSet, HashMap};
6
7use super::functions::{estimate_max_stack_depth, opcode_kind};
8
9#[derive(Clone, Debug, Default)]
11#[allow(dead_code)]
12pub struct ChunkStats {
13 pub total_instructions: usize,
15 pub branch_count: usize,
17 pub arith_count: usize,
19 pub mem_count: usize,
21 pub call_count: usize,
23 pub max_stack_depth: usize,
25}
26impl ChunkStats {
27 pub fn compute(chunk: &BytecodeChunk) -> Self {
29 let mut stats = ChunkStats::default();
30 stats.total_instructions = chunk.opcodes.len();
31 stats.max_stack_depth = estimate_max_stack_depth(chunk);
32 for op in &chunk.opcodes {
33 match op {
34 Opcode::Jump(_) | Opcode::JumpIf(_) | Opcode::JumpIfNot(_) => {
35 stats.branch_count += 1;
36 }
37 Opcode::Add
38 | Opcode::Sub
39 | Opcode::Mul
40 | Opcode::Div
41 | Opcode::Mod
42 | Opcode::Eq
43 | Opcode::Lt
44 | Opcode::Le
45 | Opcode::Not
46 | Opcode::And
47 | Opcode::Or => {
48 stats.arith_count += 1;
49 }
50 Opcode::Load(_) | Opcode::Store(_) | Opcode::LoadGlobal(_) => {
51 stats.mem_count += 1;
52 }
53 Opcode::Call(_) | Opcode::Return => {
54 stats.call_count += 1;
55 }
56 _ => {}
57 }
58 }
59 stats
60 }
61}
62#[derive(Debug, Clone)]
64#[allow(dead_code)]
65pub struct CallFrame {
66 pub return_ip: usize,
68 pub locals_base: usize,
70 pub fn_name: String,
72}
73impl CallFrame {
74 pub fn new(return_ip: usize, locals_base: usize, fn_name: impl Into<String>) -> Self {
76 CallFrame {
77 return_ip,
78 locals_base,
79 fn_name: fn_name.into(),
80 }
81 }
82}
83#[derive(Clone, Debug)]
85#[allow(dead_code)]
86pub struct InlineCacheEntry {
87 pub call_ip: usize,
89 pub hit_count: u64,
91 pub last_target: Option<u32>,
93 pub is_monomorphic: bool,
95}
96impl InlineCacheEntry {
97 pub fn new(call_ip: usize) -> Self {
99 InlineCacheEntry {
100 call_ip,
101 hit_count: 0,
102 last_target: None,
103 is_monomorphic: true,
104 }
105 }
106 pub fn record_call(&mut self, target_fn: u32) {
108 self.hit_count += 1;
109 match self.last_target {
110 None => {
111 self.last_target = Some(target_fn);
112 }
113 Some(prev) if prev != target_fn => {
114 self.is_monomorphic = false;
115 }
116 _ => {}
117 }
118 }
119}
120pub struct ChunkBuilder {
125 chunk: BytecodeChunk,
126}
127impl ChunkBuilder {
128 pub fn new(name: &str) -> Self {
130 ChunkBuilder {
131 chunk: BytecodeChunk::new(name),
132 }
133 }
134 pub fn push_nat(mut self, n: u64) -> Self {
136 self.chunk.push_op(Opcode::Push(n));
137 self
138 }
139 pub fn push_bool(mut self, b: bool) -> Self {
141 self.chunk.push_op(Opcode::PushBool(b));
142 self
143 }
144 pub fn push_str(mut self, s: &str) -> Self {
146 self.chunk.push_op(Opcode::PushStr(s.to_string()));
147 self
148 }
149 pub fn push_nil(mut self) -> Self {
151 self.chunk.push_op(Opcode::PushNil);
152 self
153 }
154 pub fn add(mut self) -> Self {
156 self.chunk.push_op(Opcode::Add);
157 self
158 }
159 pub fn sub(mut self) -> Self {
161 self.chunk.push_op(Opcode::Sub);
162 self
163 }
164 pub fn mul(mut self) -> Self {
166 self.chunk.push_op(Opcode::Mul);
167 self
168 }
169 pub fn div(mut self) -> Self {
171 self.chunk.push_op(Opcode::Div);
172 self
173 }
174 pub fn modulo(mut self) -> Self {
176 self.chunk.push_op(Opcode::Mod);
177 self
178 }
179 pub fn eq(mut self) -> Self {
181 self.chunk.push_op(Opcode::Eq);
182 self
183 }
184 pub fn lt(mut self) -> Self {
186 self.chunk.push_op(Opcode::Lt);
187 self
188 }
189 pub fn le(mut self) -> Self {
191 self.chunk.push_op(Opcode::Le);
192 self
193 }
194 pub fn not(mut self) -> Self {
196 self.chunk.push_op(Opcode::Not);
197 self
198 }
199 pub fn and(mut self) -> Self {
201 self.chunk.push_op(Opcode::And);
202 self
203 }
204 pub fn or(mut self) -> Self {
206 self.chunk.push_op(Opcode::Or);
207 self
208 }
209 pub fn dup(mut self) -> Self {
211 self.chunk.push_op(Opcode::Dup);
212 self
213 }
214 pub fn pop(mut self) -> Self {
216 self.chunk.push_op(Opcode::Pop);
217 self
218 }
219 pub fn swap(mut self) -> Self {
221 self.chunk.push_op(Opcode::Swap);
222 self
223 }
224 pub fn load(mut self, idx: u32) -> Self {
226 self.chunk.push_op(Opcode::Load(idx));
227 self
228 }
229 pub fn store(mut self, idx: u32) -> Self {
231 self.chunk.push_op(Opcode::Store(idx));
232 self
233 }
234 pub fn load_global(mut self, name: &str) -> Self {
236 self.chunk.push_op(Opcode::LoadGlobal(name.to_string()));
237 self
238 }
239 pub fn jump(mut self, offset: i32) -> Self {
241 self.chunk.push_op(Opcode::Jump(offset));
242 self
243 }
244 pub fn jump_if(mut self, offset: i32) -> Self {
246 self.chunk.push_op(Opcode::JumpIf(offset));
247 self
248 }
249 pub fn jump_if_not(mut self, offset: i32) -> Self {
251 self.chunk.push_op(Opcode::JumpIfNot(offset));
252 self
253 }
254 pub fn call(mut self, pos: u32) -> Self {
256 self.chunk.push_op(Opcode::Call(pos));
257 self
258 }
259 pub fn ret(mut self) -> Self {
261 self.chunk.push_op(Opcode::Return);
262 self
263 }
264 pub fn make_closure(mut self, n: u32) -> Self {
266 self.chunk.push_op(Opcode::MakeClosure(n));
267 self
268 }
269 pub fn apply(mut self) -> Self {
271 self.chunk.push_op(Opcode::Apply);
272 self
273 }
274 pub fn halt(mut self) -> Self {
276 self.chunk.push_op(Opcode::Halt);
277 self
278 }
279 pub fn build(self) -> BytecodeChunk {
281 self.chunk
282 }
283 pub fn current_ip(&self) -> usize {
285 self.chunk.opcodes.len()
286 }
287}
288#[derive(Debug, Clone, PartialEq)]
290#[allow(dead_code)]
291pub enum CallResult {
292 Exact { fn_pos: u32, args: Vec<StackValue> },
294 Partial {
296 captured: Vec<StackValue>,
297 remaining_arity: u32,
298 },
299 Over {
301 fn_pos: u32,
302 first_args: Vec<StackValue>,
303 rest_args: Vec<StackValue>,
304 },
305}
306pub struct DeadCodeEliminator;
311impl DeadCodeEliminator {
312 pub fn eliminate(chunk: &BytecodeChunk) -> BytecodeChunk {
314 let n = chunk.opcodes.len();
315 let mut reachable = vec![false; n];
316 let mut worklist = vec![0usize];
317 while let Some(ip) = worklist.pop() {
318 if ip >= n || reachable[ip] {
319 continue;
320 }
321 reachable[ip] = true;
322 let op = &chunk.opcodes[ip];
323 match op {
324 Opcode::Halt | Opcode::Return => {}
325 Opcode::Jump(off) => {
326 let target = (ip as i64 + 1 + *off as i64) as usize;
327 if target < n {
328 worklist.push(target);
329 }
330 }
331 Opcode::JumpIf(off) | Opcode::JumpIfNot(off) => {
332 let target = (ip as i64 + 1 + *off as i64) as usize;
333 if target < n {
334 worklist.push(target);
335 }
336 worklist.push(ip + 1);
337 }
338 _ => {
339 worklist.push(ip + 1);
340 }
341 }
342 }
343 let mut out = BytecodeChunk::new(&chunk.name);
344 for (i, op) in chunk.opcodes.iter().enumerate() {
345 if reachable[i] {
346 out.push_op(op.clone());
347 }
348 }
349 out
350 }
351}
352#[derive(Default, Debug)]
354#[allow(dead_code)]
355pub struct InlineCache {
356 entries: std::collections::HashMap<usize, InlineCacheEntry>,
357}
358impl InlineCache {
359 pub fn new() -> Self {
361 InlineCache::default()
362 }
363 pub fn record(&mut self, call_ip: usize, target_fn: u32) {
365 self.entries
366 .entry(call_ip)
367 .or_insert_with(|| InlineCacheEntry::new(call_ip))
368 .record_call(target_fn);
369 }
370 pub fn entry(&self, call_ip: usize) -> Option<&InlineCacheEntry> {
372 self.entries.get(&call_ip)
373 }
374 pub fn len(&self) -> usize {
376 self.entries.len()
377 }
378 pub fn is_empty(&self) -> bool {
380 self.entries.is_empty()
381 }
382 pub fn monomorphic_sites(&self) -> Vec<usize> {
384 self.entries
385 .values()
386 .filter(|e| e.is_monomorphic && e.last_target.is_some())
387 .map(|e| e.call_ip)
388 .collect()
389 }
390}
391#[derive(Default, Clone, Debug)]
393#[allow(dead_code)]
394pub struct OpcodeProfile {
395 counts: std::collections::HashMap<String, u64>,
397 pub total_executed: u64,
399}
400impl OpcodeProfile {
401 pub fn new() -> Self {
403 OpcodeProfile::default()
404 }
405 pub fn record(&mut self, op: &Opcode) {
407 let key = opcode_kind(op);
408 *self.counts.entry(key).or_insert(0) += 1;
409 self.total_executed += 1;
410 }
411 pub fn count(&self, kind: &str) -> u64 {
413 self.counts.get(kind).copied().unwrap_or(0)
414 }
415 pub fn top_opcodes(&self) -> Vec<(String, u64)> {
417 let mut v: Vec<_> = self.counts.iter().map(|(k, v)| (k.clone(), *v)).collect();
418 v.sort_by(|a, b| b.1.cmp(&a.1));
419 v
420 }
421 pub fn reset(&mut self) {
423 self.counts.clear();
424 self.total_executed = 0;
425 }
426}
427pub struct FramedInterpreter {
429 pub stack: Vec<StackValue>,
431 pub locals: Vec<StackValue>,
433 pub frames: Vec<CallFrame>,
435 pub ip: usize,
437 pub max_depth: usize,
439}
440impl FramedInterpreter {
441 pub fn new() -> Self {
443 FramedInterpreter {
444 stack: Vec::new(),
445 locals: Vec::new(),
446 frames: Vec::new(),
447 ip: 0,
448 max_depth: 256,
449 }
450 }
451 pub fn with_max_depth(mut self, d: usize) -> Self {
453 self.max_depth = d;
454 self
455 }
456 pub fn push_frame(&mut self, return_ip: usize, fn_name: &str) -> Result<(), String> {
458 if self.max_depth > 0 && self.frames.len() >= self.max_depth {
459 return Err(format!(
460 "call stack overflow (max depth {})",
461 self.max_depth
462 ));
463 }
464 let base = self.locals.len();
465 self.frames.push(CallFrame::new(return_ip, base, fn_name));
466 Ok(())
467 }
468 pub fn pop_frame(&mut self) -> Option<usize> {
470 let frame = self.frames.pop()?;
471 self.locals.truncate(frame.locals_base);
472 Some(frame.return_ip)
473 }
474 pub fn depth(&self) -> usize {
476 self.frames.len()
477 }
478 pub fn frame(&self, depth: usize) -> Option<&CallFrame> {
480 self.frames.get(self.frames.len().saturating_sub(1 + depth))
481 }
482 pub fn stack_trace(&self) -> String {
484 let mut out = String::new();
485 for (i, frame) in self.frames.iter().rev().enumerate() {
486 out.push_str(&format!(
487 " #{}: {} (return @{})\n",
488 i, frame.fn_name, frame.return_ip
489 ));
490 }
491 out
492 }
493 pub fn reset(&mut self) {
495 self.stack.clear();
496 self.locals.clear();
497 self.frames.clear();
498 self.ip = 0;
499 }
500}
501pub struct BytecodeChunk {
503 pub opcodes: Vec<Opcode>,
504 pub name: String,
505}
506impl BytecodeChunk {
507 pub fn new(name: &str) -> Self {
509 BytecodeChunk {
510 opcodes: Vec::new(),
511 name: name.to_string(),
512 }
513 }
514 pub fn push_op(&mut self, op: Opcode) {
516 self.opcodes.push(op);
517 }
518 pub fn len(&self) -> usize {
520 self.opcodes.len()
521 }
522 pub fn is_empty(&self) -> bool {
524 self.opcodes.is_empty()
525 }
526}
527#[derive(Clone, Debug)]
531#[allow(dead_code)]
532pub struct OpcodeInfo {
533 pub tag: u8,
535 pub mnemonic: &'static str,
537 pub byte_size: usize,
539 pub is_branch: bool,
541 pub is_terminator: bool,
543}
544#[derive(Clone, Debug)]
546#[allow(dead_code)]
547pub struct BasicBlock {
548 pub start: usize,
550 pub end: usize,
552 pub successors: Vec<usize>,
554}
555impl BasicBlock {
556 pub fn len(&self) -> usize {
558 self.end - self.start
559 }
560 pub fn is_empty(&self) -> bool {
562 self.len() == 0
563 }
564}
565pub struct ConstantFolder;
568impl ConstantFolder {
569 pub fn fold(chunk: &BytecodeChunk) -> BytecodeChunk {
571 let folded = Self::fold_ops(&chunk.opcodes);
572 let mut out = BytecodeChunk::new(&chunk.name);
573 for op in folded {
574 out.push_op(op);
575 }
576 out
577 }
578 fn fold_ops(ops: &[Opcode]) -> Vec<Opcode> {
579 let mut result: Vec<Opcode> = Vec::new();
580 let mut const_stack: Vec<Option<StackValue>> = Vec::new();
581 for op in ops {
582 match op {
583 Opcode::Push(n) => {
584 result.push(op.clone());
585 const_stack.push(Some(StackValue::Nat(*n)));
586 }
587 Opcode::PushBool(b) => {
588 result.push(op.clone());
589 const_stack.push(Some(StackValue::Bool(*b)));
590 }
591 Opcode::PushStr(s) => {
592 result.push(op.clone());
593 const_stack.push(Some(StackValue::Str(s.clone())));
594 }
595 Opcode::PushNil => {
596 result.push(op.clone());
597 const_stack.push(Some(StackValue::Nil));
598 }
599 Opcode::Add => {
600 let b = const_stack.pop().flatten();
601 let a = const_stack.pop().flatten();
602 if let (Some(StackValue::Nat(av)), Some(StackValue::Nat(bv))) = (a, b) {
603 let len = result.len();
604 if len >= 2 {
605 result.truncate(len - 2);
606 }
607 let folded = av.wrapping_add(bv);
608 result.push(Opcode::Push(folded));
609 const_stack.push(Some(StackValue::Nat(folded)));
610 } else {
611 result.push(op.clone());
612 const_stack.push(None);
613 }
614 }
615 Opcode::Mul => {
616 let b = const_stack.pop().flatten();
617 let a = const_stack.pop().flatten();
618 if let (Some(StackValue::Nat(av)), Some(StackValue::Nat(bv))) = (a, b) {
619 let len = result.len();
620 if len >= 2 {
621 result.truncate(len - 2);
622 }
623 let folded = av.wrapping_mul(bv);
624 result.push(Opcode::Push(folded));
625 const_stack.push(Some(StackValue::Nat(folded)));
626 } else {
627 result.push(op.clone());
628 const_stack.push(None);
629 }
630 }
631 Opcode::Sub => {
632 let b = const_stack.pop().flatten();
633 let a = const_stack.pop().flatten();
634 if let (Some(StackValue::Nat(av)), Some(StackValue::Nat(bv))) = (a, b) {
635 let len = result.len();
636 if len >= 2 {
637 result.truncate(len - 2);
638 }
639 let folded = av.wrapping_sub(bv);
640 result.push(Opcode::Push(folded));
641 const_stack.push(Some(StackValue::Nat(folded)));
642 } else {
643 result.push(op.clone());
644 const_stack.push(None);
645 }
646 }
647 Opcode::Not => {
648 let a = const_stack.pop().flatten();
649 if let Some(StackValue::Bool(bv)) = a {
650 let len = result.len();
651 if len >= 1 {
652 result.truncate(len - 1);
653 }
654 result.push(Opcode::PushBool(!bv));
655 const_stack.push(Some(StackValue::Bool(!bv)));
656 } else {
657 result.push(op.clone());
658 const_stack.push(None);
659 }
660 }
661 Opcode::Pop => {
662 const_stack.pop();
663 result.push(op.clone());
664 }
665 Opcode::Dup => {
666 let top = const_stack.last().cloned().flatten();
667 result.push(op.clone());
668 const_stack.push(top.map(Some).unwrap_or(None));
669 }
670 Opcode::Swap => {
671 let len = const_stack.len();
672 if len >= 2 {
673 const_stack.swap(len - 1, len - 2);
674 }
675 result.push(op.clone());
676 }
677 _ => {
678 result.push(op.clone());
679 const_stack.push(None);
680 }
681 }
682 }
683 result
684 }
685}
686#[derive(Clone, Debug, Default)]
688#[allow(dead_code)]
689pub struct LivenessInfo {
690 pub live_before: Vec<std::collections::BTreeSet<u32>>,
693}
694pub struct ProfilingInterpreter {
696 pub interp: Interpreter,
698 pub profile: OpcodeProfile,
700}
701impl ProfilingInterpreter {
702 pub fn new() -> Self {
704 ProfilingInterpreter {
705 interp: Interpreter::new(),
706 profile: OpcodeProfile::new(),
707 }
708 }
709 pub fn execute_chunk(&mut self, chunk: &BytecodeChunk) -> Result<StackValue, String> {
711 self.interp.ip = 0;
712 loop {
713 if self.interp.ip >= chunk.opcodes.len() {
714 break;
715 }
716 let op = chunk.opcodes[self.interp.ip].clone();
717 self.interp.ip += 1;
718 self.profile.record(&op);
719 let cont = self.interp.execute_op(&op, chunk)?;
720 if !cont {
721 break;
722 }
723 }
724 self.interp
725 .pop()
726 .ok_or_else(|| "stack underflow at end of chunk".to_string())
727 }
728}
729pub struct PeepholeOptimizer {
734 pub passes: usize,
736}
737impl PeepholeOptimizer {
738 pub fn new(passes: usize) -> Self {
740 PeepholeOptimizer {
741 passes: passes.max(1),
742 }
743 }
744 pub fn optimize(&self, chunk: &BytecodeChunk) -> BytecodeChunk {
746 let mut ops = chunk.opcodes.clone();
747 for _ in 0..self.passes {
748 ops = Self::run_pass(&ops);
749 }
750 let mut out = BytecodeChunk::new(&chunk.name);
751 for op in ops {
752 out.push_op(op);
753 }
754 out
755 }
756 fn run_pass(ops: &[Opcode]) -> Vec<Opcode> {
757 let mut result = Vec::with_capacity(ops.len());
758 let mut i = 0;
759 while i < ops.len() {
760 if i + 1 < ops.len() {
761 if let (Opcode::Push(_), Opcode::Pop) = (&ops[i], &ops[i + 1]) {
762 i += 2;
763 continue;
764 }
765 }
766 if i + 2 < ops.len() {
767 if let (Opcode::PushBool(a), Opcode::PushBool(b), Opcode::And) =
768 (&ops[i], &ops[i + 1], &ops[i + 2])
769 {
770 result.push(Opcode::PushBool(*a && *b));
771 i += 3;
772 continue;
773 }
774 }
775 if i + 2 < ops.len() {
776 if let (Opcode::PushBool(a), Opcode::PushBool(b), Opcode::Or) =
777 (&ops[i], &ops[i + 1], &ops[i + 2])
778 {
779 result.push(Opcode::PushBool(*a || *b));
780 i += 3;
781 continue;
782 }
783 }
784 if i + 1 < ops.len() {
785 if let (Opcode::PushBool(b), Opcode::Not) = (&ops[i], &ops[i + 1]) {
786 result.push(Opcode::PushBool(!*b));
787 i += 2;
788 continue;
789 }
790 }
791 if i + 2 < ops.len() {
792 if let (Opcode::Push(a), Opcode::Push(b), Opcode::Add) =
793 (&ops[i], &ops[i + 1], &ops[i + 2])
794 {
795 result.push(Opcode::Push(a.wrapping_add(*b)));
796 i += 3;
797 continue;
798 }
799 }
800 if i + 2 < ops.len() {
801 if let (Opcode::Push(a), Opcode::Push(b), Opcode::Mul) =
802 (&ops[i], &ops[i + 1], &ops[i + 2])
803 {
804 result.push(Opcode::Push(a.wrapping_mul(*b)));
805 i += 3;
806 continue;
807 }
808 }
809 if i + 1 < ops.len() {
810 if let (Opcode::Dup, Opcode::Pop) = (&ops[i], &ops[i + 1]) {
811 i += 2;
812 continue;
813 }
814 }
815 result.push(ops[i].clone());
816 i += 1;
817 }
818 result
819 }
820}
821pub struct BytecodeCompiler;
823impl BytecodeCompiler {
824 pub fn new() -> Self {
826 BytecodeCompiler
827 }
828 pub fn compile_nat(n: u64) -> BytecodeChunk {
830 let mut chunk = BytecodeChunk::new("nat");
831 chunk.push_op(Opcode::Push(n));
832 chunk.push_op(Opcode::Halt);
833 chunk
834 }
835 pub fn compile_add(a: u64, b: u64) -> BytecodeChunk {
837 let mut chunk = BytecodeChunk::new("add");
838 chunk.push_op(Opcode::Push(a));
839 chunk.push_op(Opcode::Push(b));
840 chunk.push_op(Opcode::Add);
841 chunk.push_op(Opcode::Halt);
842 chunk
843 }
844 pub fn compile_if(cond: bool, then_val: u64, else_val: u64) -> BytecodeChunk {
846 let mut chunk = BytecodeChunk::new("if");
847 chunk.push_op(Opcode::PushBool(cond));
848 chunk.push_op(Opcode::JumpIfNot(2));
849 chunk.push_op(Opcode::Push(then_val));
850 chunk.push_op(Opcode::Jump(1));
851 chunk.push_op(Opcode::Push(else_val));
852 chunk.push_op(Opcode::Halt);
853 chunk
854 }
855}
856#[derive(Debug, Clone, PartialEq)]
858pub enum StackValue {
859 Nat(u64),
861 Int(i64),
863 Bool(bool),
865 Str(String),
867 Closure {
869 code: Vec<Opcode>,
870 env: Vec<StackValue>,
871 },
872 Nil,
874}
875#[derive(Debug, Clone, PartialEq)]
877pub enum Opcode {
878 Push(u64),
880 PushBool(bool),
882 PushStr(String),
884 PushNil,
886 Pop,
888 Dup,
890 Swap,
892 Add,
894 Sub,
896 Mul,
898 Div,
900 Mod,
902 Eq,
904 Lt,
906 Le,
908 Not,
910 And,
912 Or,
914 Jump(i32),
916 JumpIf(i32),
918 JumpIfNot(i32),
920 Call(u32),
922 Return,
924 Load(u32),
926 Store(u32),
928 LoadGlobal(String),
930 MakeClosure(u32),
932 Apply,
934 Halt,
936}
937pub struct Interpreter {
939 pub stack: Vec<StackValue>,
940 pub locals: Vec<StackValue>,
941 pub ip: usize,
942 pub call_stack: Vec<usize>,
943}
944impl Interpreter {
945 pub fn new() -> Self {
947 Interpreter {
948 stack: Vec::new(),
949 locals: Vec::with_capacity(64),
950 ip: 0,
951 call_stack: Vec::new(),
952 }
953 }
954 pub fn push(&mut self, v: StackValue) {
956 self.stack.push(v);
957 }
958 pub fn pop(&mut self) -> Option<StackValue> {
960 self.stack.pop()
961 }
962 pub fn peek(&self) -> Option<&StackValue> {
964 self.stack.last()
965 }
966 pub fn execute_chunk(&mut self, chunk: &BytecodeChunk) -> Result<StackValue, String> {
968 self.ip = 0;
969 loop {
970 if self.ip >= chunk.opcodes.len() {
971 break;
972 }
973 let op = chunk.opcodes[self.ip].clone();
974 self.ip += 1;
975 let cont = self.execute_op(&op, chunk)?;
976 if !cont {
977 break;
978 }
979 }
980 self.pop()
981 .ok_or_else(|| "stack underflow at end of chunk".to_string())
982 }
983 pub fn execute_op(&mut self, op: &Opcode, _chunk: &BytecodeChunk) -> Result<bool, String> {
987 match op {
988 Opcode::Push(n) => {
989 self.stack.push(StackValue::Nat(*n));
990 }
991 Opcode::PushBool(b) => {
992 self.stack.push(StackValue::Bool(*b));
993 }
994 Opcode::PushStr(s) => {
995 self.stack.push(StackValue::Str(s.clone()));
996 }
997 Opcode::PushNil => {
998 self.stack.push(StackValue::Nil);
999 }
1000 Opcode::Pop => {
1001 self.stack.pop();
1002 }
1003 Opcode::Dup => {
1004 let top = self.stack.last().ok_or("Dup: stack underflow")?.clone();
1005 self.stack.push(top);
1006 }
1007 Opcode::Swap => {
1008 let len = self.stack.len();
1009 if len < 2 {
1010 return Err("Swap: stack underflow".to_string());
1011 }
1012 self.stack.swap(len - 1, len - 2);
1013 }
1014 Opcode::Add => {
1015 let b = self.pop_nat("Add")?;
1016 let a = self.pop_nat("Add")?;
1017 self.stack.push(StackValue::Nat(a.wrapping_add(b)));
1018 }
1019 Opcode::Sub => {
1020 let b = self.pop_nat("Sub")?;
1021 let a = self.pop_nat("Sub")?;
1022 self.stack.push(StackValue::Nat(a.wrapping_sub(b)));
1023 }
1024 Opcode::Mul => {
1025 let b = self.pop_nat("Mul")?;
1026 let a = self.pop_nat("Mul")?;
1027 self.stack.push(StackValue::Nat(a.wrapping_mul(b)));
1028 }
1029 Opcode::Div => {
1030 let b = self.pop_nat("Div")?;
1031 let a = self.pop_nat("Div")?;
1032 if b == 0 {
1033 return Err("division by zero".to_string());
1034 }
1035 self.stack.push(StackValue::Nat(a / b));
1036 }
1037 Opcode::Mod => {
1038 let b = self.pop_nat("Mod")?;
1039 let a = self.pop_nat("Mod")?;
1040 if b == 0 {
1041 return Err("modulo by zero".to_string());
1042 }
1043 self.stack.push(StackValue::Nat(a % b));
1044 }
1045 Opcode::Eq => {
1046 let b = self.pop().ok_or("Eq: stack underflow")?;
1047 let a = self.pop().ok_or("Eq: stack underflow")?;
1048 self.stack.push(StackValue::Bool(a == b));
1049 }
1050 Opcode::Lt => {
1051 let b = self.pop_nat("Lt")?;
1052 let a = self.pop_nat("Lt")?;
1053 self.stack.push(StackValue::Bool(a < b));
1054 }
1055 Opcode::Le => {
1056 let b = self.pop_nat("Le")?;
1057 let a = self.pop_nat("Le")?;
1058 self.stack.push(StackValue::Bool(a <= b));
1059 }
1060 Opcode::Not => {
1061 let v = self.pop_bool("Not")?;
1062 self.stack.push(StackValue::Bool(!v));
1063 }
1064 Opcode::And => {
1065 let b = self.pop_bool("And")?;
1066 let a = self.pop_bool("And")?;
1067 self.stack.push(StackValue::Bool(a && b));
1068 }
1069 Opcode::Or => {
1070 let b = self.pop_bool("Or")?;
1071 let a = self.pop_bool("Or")?;
1072 self.stack.push(StackValue::Bool(a || b));
1073 }
1074 Opcode::Jump(offset) => {
1075 let new_ip = self.ip as i64 + *offset as i64;
1076 if new_ip < 0 {
1077 return Err(format!("Jump: negative IP {}", new_ip));
1078 }
1079 self.ip = new_ip as usize;
1080 }
1081 Opcode::JumpIf(offset) => {
1082 let cond = self.pop_bool("JumpIf")?;
1083 if cond {
1084 let new_ip = self.ip as i64 + *offset as i64;
1085 if new_ip < 0 {
1086 return Err(format!("JumpIf: negative IP {}", new_ip));
1087 }
1088 self.ip = new_ip as usize;
1089 }
1090 }
1091 Opcode::JumpIfNot(offset) => {
1092 let cond = self.pop_bool("JumpIfNot")?;
1093 if !cond {
1094 let new_ip = self.ip as i64 + *offset as i64;
1095 if new_ip < 0 {
1096 return Err(format!("JumpIfNot: negative IP {}", new_ip));
1097 }
1098 self.ip = new_ip as usize;
1099 }
1100 }
1101 Opcode::Call(pos) => {
1102 self.call_stack.push(self.ip);
1103 self.ip = *pos as usize;
1104 }
1105 Opcode::Return => {
1106 if let Some(ret_addr) = self.call_stack.pop() {
1107 self.ip = ret_addr;
1108 } else {
1109 return Ok(false);
1110 }
1111 }
1112 Opcode::Load(idx) => {
1113 let idx = *idx as usize;
1114 let v = self
1115 .locals
1116 .get(idx)
1117 .ok_or_else(|| format!("Load: local {} not found", idx))?
1118 .clone();
1119 self.stack.push(v);
1120 }
1121 Opcode::Store(idx) => {
1122 let idx = *idx as usize;
1123 let v = self.stack.last().ok_or("Store: stack underflow")?.clone();
1124 while self.locals.len() <= idx {
1125 self.locals.push(StackValue::Nil);
1126 }
1127 self.locals[idx] = v;
1128 }
1129 Opcode::LoadGlobal(name) => {
1130 self.stack
1131 .push(StackValue::Str(format!("<global:{}>", name)));
1132 }
1133 Opcode::MakeClosure(n_captured) => {
1134 let n = *n_captured as usize;
1135 if self.stack.len() < n {
1136 return Err(format!(
1137 "MakeClosure: need {} captures, have {}",
1138 n,
1139 self.stack.len()
1140 ));
1141 }
1142 let mut env = Vec::with_capacity(n);
1143 for _ in 0..n {
1144 env.push(self.pop().expect(
1145 "stack has at least n elements as verified by the length check above",
1146 ));
1147 }
1148 env.reverse();
1149 self.stack.push(StackValue::Closure {
1150 code: Vec::new(),
1151 env,
1152 });
1153 }
1154 Opcode::Apply => {
1155 let _arg = self.pop().ok_or("Apply: stack underflow (arg)")?;
1156 let _closure = self.pop().ok_or("Apply: stack underflow (closure)")?;
1157 self.stack.push(StackValue::Nil);
1158 }
1159 Opcode::Halt => {
1160 return Ok(false);
1161 }
1162 }
1163 Ok(true)
1164 }
1165 fn pop_nat(&mut self, ctx: &str) -> Result<u64, String> {
1166 match self.pop() {
1167 Some(StackValue::Nat(n)) => Ok(n),
1168 Some(other) => Err(format!("{}: expected Nat, got {:?}", ctx, other)),
1169 None => Err(format!("{}: stack underflow", ctx)),
1170 }
1171 }
1172 fn pop_bool(&mut self, ctx: &str) -> Result<bool, String> {
1173 match self.pop() {
1174 Some(StackValue::Bool(b)) => Ok(b),
1175 Some(other) => Err(format!("{}: expected Bool, got {:?}", ctx, other)),
1176 None => Err(format!("{}: stack underflow", ctx)),
1177 }
1178 }
1179}
1180#[derive(Clone, Debug, PartialEq, Eq)]
1186pub struct EncodedInstruction {
1187 pub bytes: Vec<u8>,
1189}
1190impl EncodedInstruction {
1191 pub fn encode(op: &Opcode) -> Self {
1193 let mut bytes = Vec::new();
1194 match op {
1195 Opcode::Push(n) => {
1196 bytes.push(0x01);
1197 bytes.extend_from_slice(&n.to_le_bytes());
1198 }
1199 Opcode::PushBool(b) => {
1200 bytes.push(0x02);
1201 bytes.push(*b as u8);
1202 }
1203 Opcode::PushStr(s) => {
1204 bytes.push(0x03);
1205 let len = s.len() as u32;
1206 bytes.extend_from_slice(&len.to_le_bytes());
1207 bytes.extend_from_slice(s.as_bytes());
1208 }
1209 Opcode::PushNil => bytes.push(0x04),
1210 Opcode::Pop => bytes.push(0x10),
1211 Opcode::Dup => bytes.push(0x11),
1212 Opcode::Swap => bytes.push(0x12),
1213 Opcode::Add => bytes.push(0x20),
1214 Opcode::Sub => bytes.push(0x21),
1215 Opcode::Mul => bytes.push(0x22),
1216 Opcode::Div => bytes.push(0x23),
1217 Opcode::Mod => bytes.push(0x24),
1218 Opcode::Eq => bytes.push(0x30),
1219 Opcode::Lt => bytes.push(0x31),
1220 Opcode::Le => bytes.push(0x32),
1221 Opcode::Not => bytes.push(0x40),
1222 Opcode::And => bytes.push(0x41),
1223 Opcode::Or => bytes.push(0x42),
1224 Opcode::Jump(off) => {
1225 bytes.push(0x50);
1226 bytes.extend_from_slice(&off.to_le_bytes());
1227 }
1228 Opcode::JumpIf(off) => {
1229 bytes.push(0x51);
1230 bytes.extend_from_slice(&off.to_le_bytes());
1231 }
1232 Opcode::JumpIfNot(off) => {
1233 bytes.push(0x52);
1234 bytes.extend_from_slice(&off.to_le_bytes());
1235 }
1236 Opcode::Call(pos) => {
1237 bytes.push(0x60);
1238 bytes.extend_from_slice(&pos.to_le_bytes());
1239 }
1240 Opcode::Return => bytes.push(0x61),
1241 Opcode::Load(idx) => {
1242 bytes.push(0x70);
1243 bytes.extend_from_slice(&idx.to_le_bytes());
1244 }
1245 Opcode::Store(idx) => {
1246 bytes.push(0x71);
1247 bytes.extend_from_slice(&idx.to_le_bytes());
1248 }
1249 Opcode::LoadGlobal(name) => {
1250 bytes.push(0x72);
1251 let len = name.len() as u32;
1252 bytes.extend_from_slice(&len.to_le_bytes());
1253 bytes.extend_from_slice(name.as_bytes());
1254 }
1255 Opcode::MakeClosure(n) => {
1256 bytes.push(0x80);
1257 bytes.extend_from_slice(&n.to_le_bytes());
1258 }
1259 Opcode::Apply => bytes.push(0x81),
1260 Opcode::Halt => bytes.push(0xFF),
1261 }
1262 EncodedInstruction { bytes }
1263 }
1264 pub fn decode(data: &[u8]) -> Option<(Opcode, usize)> {
1267 if data.is_empty() {
1268 return None;
1269 }
1270 let tag = data[0];
1271 match tag {
1272 0x01 => {
1273 if data.len() < 9 {
1274 return None;
1275 }
1276 let n = u64::from_le_bytes(data[1..9].try_into().ok()?);
1277 Some((Opcode::Push(n), 9))
1278 }
1279 0x02 => {
1280 if data.len() < 2 {
1281 return None;
1282 }
1283 Some((Opcode::PushBool(data[1] != 0), 2))
1284 }
1285 0x03 => {
1286 if data.len() < 5 {
1287 return None;
1288 }
1289 let len = u32::from_le_bytes(data[1..5].try_into().ok()?) as usize;
1290 if data.len() < 5 + len {
1291 return None;
1292 }
1293 let s = std::str::from_utf8(&data[5..5 + len]).ok()?.to_string();
1294 Some((Opcode::PushStr(s), 5 + len))
1295 }
1296 0x04 => Some((Opcode::PushNil, 1)),
1297 0x10 => Some((Opcode::Pop, 1)),
1298 0x11 => Some((Opcode::Dup, 1)),
1299 0x12 => Some((Opcode::Swap, 1)),
1300 0x20 => Some((Opcode::Add, 1)),
1301 0x21 => Some((Opcode::Sub, 1)),
1302 0x22 => Some((Opcode::Mul, 1)),
1303 0x23 => Some((Opcode::Div, 1)),
1304 0x24 => Some((Opcode::Mod, 1)),
1305 0x30 => Some((Opcode::Eq, 1)),
1306 0x31 => Some((Opcode::Lt, 1)),
1307 0x32 => Some((Opcode::Le, 1)),
1308 0x40 => Some((Opcode::Not, 1)),
1309 0x41 => Some((Opcode::And, 1)),
1310 0x42 => Some((Opcode::Or, 1)),
1311 0x50 => {
1312 if data.len() < 5 {
1313 return None;
1314 }
1315 let off = i32::from_le_bytes(data[1..5].try_into().ok()?);
1316 Some((Opcode::Jump(off), 5))
1317 }
1318 0x51 => {
1319 if data.len() < 5 {
1320 return None;
1321 }
1322 let off = i32::from_le_bytes(data[1..5].try_into().ok()?);
1323 Some((Opcode::JumpIf(off), 5))
1324 }
1325 0x52 => {
1326 if data.len() < 5 {
1327 return None;
1328 }
1329 let off = i32::from_le_bytes(data[1..5].try_into().ok()?);
1330 Some((Opcode::JumpIfNot(off), 5))
1331 }
1332 0x60 => {
1333 if data.len() < 5 {
1334 return None;
1335 }
1336 let pos = u32::from_le_bytes(data[1..5].try_into().ok()?);
1337 Some((Opcode::Call(pos), 5))
1338 }
1339 0x61 => Some((Opcode::Return, 1)),
1340 0x70 => {
1341 if data.len() < 5 {
1342 return None;
1343 }
1344 let idx = u32::from_le_bytes(data[1..5].try_into().ok()?);
1345 Some((Opcode::Load(idx), 5))
1346 }
1347 0x71 => {
1348 if data.len() < 5 {
1349 return None;
1350 }
1351 let idx = u32::from_le_bytes(data[1..5].try_into().ok()?);
1352 Some((Opcode::Store(idx), 5))
1353 }
1354 0x72 => {
1355 if data.len() < 5 {
1356 return None;
1357 }
1358 let len = u32::from_le_bytes(data[1..5].try_into().ok()?) as usize;
1359 if data.len() < 5 + len {
1360 return None;
1361 }
1362 let s = std::str::from_utf8(&data[5..5 + len]).ok()?.to_string();
1363 Some((Opcode::LoadGlobal(s), 5 + len))
1364 }
1365 0x80 => {
1366 if data.len() < 5 {
1367 return None;
1368 }
1369 let n = u32::from_le_bytes(data[1..5].try_into().ok()?);
1370 Some((Opcode::MakeClosure(n), 5))
1371 }
1372 0x81 => Some((Opcode::Apply, 1)),
1373 0xFF => Some((Opcode::Halt, 1)),
1374 _ => None,
1375 }
1376 }
1377}