diff --git a/.jules/bolt.md b/.jules/bolt.md index 3faa4acb..c33a233e 100644 --- a/.jules/bolt.md +++ b/.jules/bolt.md @@ -70,3 +70,7 @@ ## 2024-05-24 - Web Property Extraction Optimization **Learning:** Extracting properties from the `RuntimeValue` DSL property lists in `stdlib/src/web/renderer.rs` and `stdlib/src/web/db.rs` was inefficient because the code called `.iter().find()` to resolve `text`, `id`, and `name` attributes, which creates unnecessary iterator overhead and closure allocation. **Action:** Replace `.iter().find()` calls with a single-pass `for p in &dsl.properties` loop and `if` statements on the property names, stopping early when found, which significantly reduces execution overhead. + +## 2026-09-30 - ValueStack duplication optimization +**Learning:** In VM execution loop, handling stack duplication (`Opcode::Dup`) via separate `stack.peek()?` and `stack.push()` calls incurs redundant stack bounds checks and error handling overhead. Encapsulating this in a single `stack.dup()` method simplifies the execution path. +**Action:** Always provide dedicated single-pass methods on collection wrappers (such as `ValueStack::dup`) when an operation involves reading and duplicating the top element on the same structure. diff --git a/runtime/vm/src/executor.rs b/runtime/vm/src/executor.rs index ace9bb00..01b63b29 100644 --- a/runtime/vm/src/executor.rs +++ b/runtime/vm/src/executor.rs @@ -149,8 +149,7 @@ impl VM { } Opcode::Dup => { - let val = self.stack.peek()?.clone(); - self.stack.push(val)?; + self.stack.dup()?; } Opcode::Add => { diff --git a/runtime/vm/src/stack.rs b/runtime/vm/src/stack.rs index f064ad7f..51bf7c3f 100644 --- a/runtime/vm/src/stack.rs +++ b/runtime/vm/src/stack.rs @@ -30,6 +30,16 @@ impl ValueStack { self.data.pop().ok_or(VMError::StackUnderflow) } + /// Duplicates the top value on the stack. + pub fn dup(&mut self) -> Result<(), VMError> { + let val = self.data.last().ok_or(VMError::StackUnderflow)?; + if self.data.len() >= self.max_size { + return Err(VMError::StackOverflow); + } + self.data.push(val.clone()); + Ok(()) + } + /// Returns a reference to the top value. pub fn peek(&self) -> Result<&RuntimeValue, VMError> { self.data.last().ok_or(VMError::StackUnderflow) @@ -80,3 +90,23 @@ impl ValueStack { &self.data } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_stack_dup() { + let mut stack = ValueStack::new(2); + assert_eq!(stack.dup(), Err(VMError::StackUnderflow)); + + stack.push(RuntimeValue::Int(42)).unwrap(); + assert_eq!(stack.len(), 1); + + stack.dup().unwrap(); + assert_eq!(stack.len(), 2); + assert_eq!(stack.peek().unwrap(), &RuntimeValue::Int(42)); + + assert_eq!(stack.dup(), Err(VMError::StackOverflow)); + } +}