From 55e38af0edee983ac21443eef1e83caf1c63b2b0 Mon Sep 17 00:00:00 2001 From: Mark Poliakov Date: Fri, 8 May 2026 16:19:59 +0300 Subject: [PATCH] Fix incorrect offset calculation in label jumps/branches --- examples/factorial.lysp | 24 ++++++++++++++++ src/compile/block.rs | 14 +++++++-- src/compile/syntax/mod.rs | 1 - src/vm/machine.rs | 60 ++++++++++++++++++++++++++++++++++----- 4 files changed, 88 insertions(+), 11 deletions(-) create mode 100644 examples/factorial.lysp diff --git a/examples/factorial.lysp b/examples/factorial.lysp new file mode 100644 index 0000000..183d33a --- /dev/null +++ b/examples/factorial.lysp @@ -0,0 +1,24 @@ +;; vi:ft=lisp:sw=2:ts=2 + +(defun factorial (x) + (if (= x 0) + 1 + (* (factorial (- x 1)) x) + ) + ) + +(print 1 "\t" (factorial 1)) +(print 2 "\t" (factorial 2)) +(print 3 "\t" (factorial 3)) +(print 4 "\t" (factorial 4)) +(print 5 "\t" (factorial 5)) +(print 6 "\t" (factorial 6)) +(print 7 "\t" (factorial 7)) +(print 8 "\t" (factorial 8)) +(print 9 "\t" (factorial 9)) +(print 10 "\t" (factorial 10)) +(print 11 "\t" (factorial 11)) +(print 12 "\t" (factorial 12)) +(print 13 "\t" (factorial 13)) +(print 14 "\t" (factorial 14)) +(print 15 "\t" (factorial 15)) diff --git a/src/compile/block.rs b/src/compile/block.rs index 5e5ad85..96d850c 100644 --- a/src/compile/block.rs +++ b/src/compile/block.rs @@ -138,9 +138,11 @@ impl FunctionBlock { pub fn resolve_labels(self) -> CompiledFunction { let mut instructions = vec![]; - for emitted in self.instructions { + // eprintln!("RESOLVE LABELS"); + for (function_offset, emitted) in self.instructions.into_iter().enumerate() { match emitted { Emitted::Instruction(instruction) => { + // eprintln!("{function_offset}: {instruction:?}"); instructions.push(instruction); } Emitted::Branch(label) => { @@ -148,7 +150,11 @@ impl FunctionBlock { if address == usize::MAX { todo!() } - let offset = U::new(address as u32).unwrap(); + let diff = address.checked_signed_diff(function_offset).expect("TODO"); + // eprintln!( + // "{function_offset}: branch to label {label} (@ {address}) -> {diff:+}" + // ); + let offset = U::from_signed(diff as i64).expect("TODO"); instructions.push(Instruction::Branch(offset)); } Emitted::Jump(label) => { @@ -156,7 +162,9 @@ impl FunctionBlock { if address == usize::MAX { todo!() } - let offset = U::new(address as u32).unwrap(); + let diff = address.checked_signed_diff(function_offset).expect("TODO"); + // eprintln!("{function_offset}: jump to label {label} (@ {address}) -> {diff:+}"); + let offset = U::from_signed(diff as i64).expect("TODO"); instructions.push(Instruction::Jump(offset)); } } diff --git a/src/compile/syntax/mod.rs b/src/compile/syntax/mod.rs index f8a6c5e..c00a278 100644 --- a/src/compile/syntax/mod.rs +++ b/src/compile/syntax/mod.rs @@ -56,7 +56,6 @@ impl Expression { } fn parse_inner(value: &Value) -> Rc { - eprintln!("{}: parse_inner({value})", core::panic::Location::caller()); match value { Value::Nil => Rc::new(Self::Nil), Value::Boolean(value) => Rc::new(Self::BooleanLiteral(*value)), diff --git a/src/vm/machine.rs b/src/vm/machine.rs index 1e10452..18f4b8b 100644 --- a/src/vm/machine.rs +++ b/src/vm/machine.rs @@ -31,6 +31,7 @@ pub struct Machine { pub ip: Option, value_stack: Stack, pub call_stack: Stack, + pub trace_instructions: bool, // Top-level locals locals: HashMap, } @@ -49,6 +50,8 @@ impl Default for Machine { value_stack: Stack::new(1024), call_stack: Stack::new(32), locals: HashMap::new(), + + trace_instructions: false, } } } @@ -219,7 +222,7 @@ impl Machine { self.execute_builtin_native(environment, function, count) } - fn execute_branch(&mut self, offset: usize) -> Result { + fn execute_branch(&mut self, offset: isize) -> Result { let value = self.pop()?; let do_branch = !bool::try_from_value(&value).unwrap_or_default(); if do_branch { @@ -228,11 +231,11 @@ impl Machine { Ok(!do_branch) } - fn execute_jump(&mut self, offset: usize) -> Result<(), MachineError> { + fn execute_jump(&mut self, offset: isize) -> Result<(), MachineError> { let ip = self.ip.clone().unwrap(); self.ip = Some(InstructionPointer { module: ip.module, - address: offset, + address: ip.address.wrapping_add_signed(offset), }); Ok(()) } @@ -330,11 +333,54 @@ impl Machine { Ok(()) } + fn trace_instruction(&self, ip: &InstructionPointer) { + let code = ip.module.instruction(ip.address); + let Some(code) = code else { + eprintln!("{ip}: "); + return; + }; + eprint!("{ip}: {code:08x} "); + if let Ok(instruction) = Instruction::try_from(code) { + eprint!("{instruction:?}"); + match instruction { + Instruction::PushConstant(index) => { + if let Some(constant) = ip.module.constant(index) { + eprint!(" [-> {constant}]"); + } else { + eprint!(" [-> ]"); + } + } + Instruction::PushArgument(index) => { + if let Some(argument) = self + .call_stack + .current() + .and_then(|frame| frame.arguments.get(usize::from(index))) + { + eprint!(" [-> {argument}]"); + } else { + eprint!(" [-> ]") + } + } + _ => (), + } + } else { + eprint!(""); + } + eprintln!(); + } + pub fn execute_next( &mut self, environment: &mut Environment, ) -> Result { - let ip = self.ip.clone().unwrap(); + let ip = self + .ip + .clone() + .ok_or_else(|| self.error_at_ip(MachineErrorKind::UndefinedInstructionPointer))?; + + if self.trace_instructions { + self.trace_instruction(&ip); + } let instruction = ip.module.instruction(ip.address).ok_or_else(|| { self.error_at_ip(MachineErrorKind::InstructionOutOfBounds(ip.clone())) })?; @@ -362,7 +408,7 @@ impl Machine { self.pop()?; } Instruction::ExportMacro(index) => { - self.execute_export_macro(environment, index.into())?; + self.execute_export_macro(environment, index)?; } Instruction::GetGlobal => { self.execute_get_global(environment)?; @@ -388,11 +434,11 @@ impl Machine { self.execute_math(environment, math, count.into())?; } Instruction::Branch(offset) => { - advance = self.execute_branch(offset.into())?; + advance = self.execute_branch(offset.sign_extend_i64() as isize)?; } Instruction::Jump(offset) => { advance = false; - self.execute_jump(offset.into())?; + self.execute_jump(offset.sign_extend_i64() as isize)?; } } if advance {