diff --git a/dm-std-lib/src/lib.rs b/dm-std-lib/src/lib.rs index 49b300e..f104817 100644 --- a/dm-std-lib/src/lib.rs +++ b/dm-std-lib/src/lib.rs @@ -1,5 +1,5 @@ +use dvm_lib::vm::DvmContext; use dvm_lib::vm::value::Value; -use dvm_lib::vm::{DvmContext, DvmState}; use std::error::Error; use std::fmt::{Debug, Display, Formatter}; use std::rc::Rc; @@ -35,11 +35,7 @@ impl Display for StdCoreError { impl Error for StdCoreError {} -pub fn std_core_println( - _context: &DvmContext, - _state: &DvmState, - args: &[Value], -) -> Result> { +pub fn std_core_println(args: &[Value]) -> Result> { let maybe_to_print = args.get(0); match maybe_to_print { None => Err(Box::new(StdCoreError::new("Missing to_print arg"))), diff --git a/dm/src/main.rs b/dm/src/main.rs index 4907871..070b7e7 100644 --- a/dm/src/main.rs +++ b/dm/src/main.rs @@ -9,7 +9,8 @@ use dmc_lib::diagnostic::Diagnostic; use dmc_lib::parser::parse_compilation_unit; use dmc_lib::symbol_table::SymbolTable; use dvm_lib::vm::constant::{Constant, StringConstant}; -use dvm_lib::vm::{DvmContext, DvmState, call}; +use dvm_lib::vm::value::Value; +use dvm_lib::vm::{CallStack, DvmContext, call}; use std::path::PathBuf; use std::rc::Rc; @@ -54,7 +55,7 @@ fn main() { if args.show_asm { for asm_function in &asm_functions { - println!("{:?}", asm_function); + println!("{:#?}", asm_function); } } @@ -75,9 +76,16 @@ fn main() { ))); } - let mut dvm_state = DvmState::new(); + let mut registers: Vec = vec![]; + let mut call_stack = CallStack::new(); - let result = call(&dvm_context, &mut dvm_state, "main", vec![]); + let result = call( + &dvm_context, + &mut registers, + &mut call_stack, + "main", + vec![], + ); println!("{:?}", result); } diff --git a/dmc-lib/src/asm/asm_instruction.rs b/dmc-lib/src/asm/asm_instruction.rs index 349c8c9..9ab2ce7 100644 --- a/dmc-lib/src/asm/asm_instruction.rs +++ b/dmc-lib/src/asm/asm_instruction.rs @@ -107,15 +107,19 @@ impl Pop { #[derive(Debug)] pub struct InvokeStatic { name: String, + arg_count: usize, } impl InvokeStatic { - pub fn new(name: &str) -> Self { - Self { name: name.into() } + pub fn new(name: &str, arg_count: usize) -> Self { + Self { + name: name.into(), + arg_count, + } } pub fn dvm(&self) -> Instruction { - Instruction::InvokeStatic(Rc::from(self.name.clone())) + Instruction::InvokeStatic(Rc::from(self.name.clone()), self.arg_count) } } diff --git a/dmc-lib/src/ast/call.rs b/dmc-lib/src/ast/call.rs index 0d59975..2805795 100644 --- a/dmc-lib/src/ast/call.rs +++ b/dmc-lib/src/ast/call.rs @@ -194,8 +194,10 @@ impl Call { InvokePlatformStatic::new(function_symbol.name(), arg_count), )); } else { + let arg_count = function_symbol.parameters().len(); context.instruction(AsmInstruction::InvokeStatic(InvokeStatic::new( function_symbol.name(), + arg_count, ))); } } diff --git a/dvm-lib/src/instruction.rs b/dvm-lib/src/instruction.rs index abe6ad2..c463791 100644 --- a/dvm-lib/src/instruction.rs +++ b/dvm-lib/src/instruction.rs @@ -14,7 +14,7 @@ pub enum Instruction { PushInt(i32), PushStackFrameOffset(isize), - InvokeStatic(FunctionName), + InvokeStatic(FunctionName, ArgCount), InvokePlatformStatic(FunctionName, ArgCount), LoadStringConstant(ConstantName, Register), diff --git a/dvm-lib/src/platform_function.rs b/dvm-lib/src/platform_function.rs index 4666fac..0d4e286 100644 --- a/dvm-lib/src/platform_function.rs +++ b/dvm-lib/src/platform_function.rs @@ -1,6 +1,4 @@ use crate::vm::value::Value; -use crate::vm::{DvmContext, DvmState}; use std::error::Error; -pub type PlatformFunction = - fn(context: &DvmContext, state: &DvmState, args: &[Value]) -> Result>; +pub type PlatformFunction = fn(args: &[Value]) -> Result>; diff --git a/dvm-lib/src/vm/mod.rs b/dvm-lib/src/vm/mod.rs index a0c8fa9..16700b4 100644 --- a/dvm-lib/src/vm/mod.rs +++ b/dvm-lib/src/vm/mod.rs @@ -41,6 +41,7 @@ impl DvmContext { &mut self.platform_functions } + #[deprecated] pub fn add_function(&mut self, function: Function) { self.functions.insert(function.name_owned(), function); } @@ -49,6 +50,7 @@ impl DvmContext { &self.constants } + #[deprecated] pub fn add_constant(&mut self, constant: Constant) { match &constant { Constant::String(string_constant) => { @@ -59,63 +61,110 @@ impl DvmContext { } } -pub struct DvmState { - stack: Vec, - registers: Vec, +pub struct CallFrame<'a> { + function_name: Rc, + active: bool, + instructions: &'a Vec, ip: usize, + stack: Vec, fp: usize, } -impl DvmState { - pub fn new() -> Self { +impl<'a> CallFrame<'a> { + fn new(function_name: Rc, instructions: &'a Vec) -> Self { Self { - stack: vec![], - registers: vec![], + function_name, + active: false, + instructions, ip: 0, + stack: vec![], fp: 0, } } - pub fn stack(&self) -> &Vec { - &self.stack + fn function_name(&self) -> &str { + &self.function_name } - pub fn stack_mut(&mut self) -> &mut Vec { - &mut self.stack + fn active(&self) -> bool { + self.active } - pub fn registers(&self) -> &Vec { - &self.registers + fn mark_active(&mut self) { + self.active = true; } - pub fn registers_mut(&mut self) -> &mut Vec { - &mut self.registers + fn instructions(&self) -> &'a [Instruction] { + self.instructions } - pub fn ensure_registers(&mut self, count: usize) { - self.registers.resize_with(count, Default::default); - } - - pub fn ip(&self) -> usize { + fn ip(&self) -> usize { self.ip } - pub fn increment_ip(&mut self) { + fn increment_ip(&mut self) { self.ip += 1; } - pub fn fp(&self) -> usize { + fn stack(&self) -> &[Value] { + &self.stack + } + + fn stack_mut(&mut self) -> &mut Vec { + &mut self.stack + } + + fn fp(&self) -> usize { self.fp } - pub fn set_fp(&mut self, fp: usize) { + fn set_fp(&mut self, fp: usize) { self.fp = fp; } } -pub fn call( - context: &DvmContext, - state: &mut DvmState, +pub struct CallStack<'a> { + call_frames: Vec>, +} + +impl<'a> CallStack<'a> { + pub fn new() -> Self { + Self { + call_frames: vec![], + } + } + + fn push(&mut self, call_frame: CallFrame<'a>) { + self.call_frames.push(call_frame); + } + + fn pop(&mut self) -> CallFrame<'a> { + self.call_frames.pop().unwrap() + } + + fn top(&self) -> &CallFrame<'a> { + &self.call_frames[self.call_frames.len() - 1] + } + + fn top_mut(&mut self) -> &mut CallFrame<'a> { + let call_frames_len = self.call_frames.len(); + &mut self.call_frames[call_frames_len - 1] // dang borrow checker + } + + fn penultimate(&self) -> Option<&CallFrame<'a>> { + self.call_frames.get(self.call_frames.len() - 2) + } + + fn penultimate_mut(&mut self) -> Option<&mut CallFrame<'a>> { + let call_frames_len = self.call_frames.len(); + self.call_frames.get_mut(call_frames_len - 2) // dang borrow checker + } +} + +pub fn call<'a>( + context: &'a DvmContext, + registers: &mut Vec, + call_stack: &mut CallStack<'a>, function_name: &str, arguments: Vec, ) -> Option { @@ -124,81 +173,109 @@ pub fn call( .get(function_name) .expect(&format!("Function {} not found", function_name)); - let instructions = function.instructions(); - state.ensure_registers(function.register_count()); + // ensure enough registers + registers.resize_with(function.register_count(), Default::default); + + // push call frame + call_stack.push(CallFrame::new( + function.name_owned(), + function.instructions(), + )); + + // mark it active + call_stack.top_mut().mark_active(); // put each arg on the stack for argument in arguments { - state.stack_mut().push(argument); + call_stack.top_mut().stack_mut().push(argument); } - while state.ip() < instructions.len() { - let instruction = &instructions[state.ip()]; + while call_stack.top().ip() < call_stack.top().instructions().len() { + let instruction = &call_stack.top().instructions()[call_stack.top().ip()]; match instruction { /* Move instructions */ Instruction::MoveRegister(source, destination) => { // copy value from one register to another register - let value = state.registers()[*source].clone(); - state.registers_mut()[*destination] = value; + let value = registers[*source].clone(); + registers[*destination] = value; } Instruction::MoveInt(value, destination) => { - state.registers_mut()[*destination] = Value::Int(*value); + registers[*destination] = Value::Int(*value); } Instruction::MoveStackFrameOffset(offset, destination) => { // copy a value offset from the current frame pointer (fp) to a register - let value_index = state - .fp() - .checked_add_signed(*offset) - .expect("Overflow when adding offset to fp"); - let value = state.stack()[value_index].clone(); - state.registers_mut()[*destination] = value; + let value_index = + call_stack + .top() + .fp() + .checked_add_signed(*offset) + .expect(&format!( + "Overflow when adding offset {} to fp {}", + *offset, + call_stack.top().fp() + )); + let value = call_stack.top().stack()[value_index].clone(); + registers[*destination] = value; } /* Push instructions */ Instruction::PushRegister(source) => { // copy a value from a register to the top of the stack - let value = state.registers()[*source].clone(); - state.stack_mut().push(value); + let value = registers[*source].clone(); + call_stack.top_mut().stack_mut().push(value); } Instruction::PushInt(value) => { - state.stack_mut().push(Value::Int(*value)); + call_stack.top_mut().stack_mut().push(Value::Int(*value)); } Instruction::PushStackFrameOffset(offset) => { // copy a value from somewhere on the stack to the top of the stack - let value_index = state + let value_index = call_stack + .top() .fp() .checked_add_signed(*offset) .expect("Overflow when adding offset to fp"); - let value = state.stack()[value_index].clone(); - state.stack_mut().push(value); + let value = call_stack.top().stack()[value_index].clone(); + call_stack.top_mut().stack_mut().push(value); } /* Invoke instructions */ - Instruction::InvokeStatic(function_name) => { + Instruction::InvokeStatic(function_name, arg_count) => { let function = context .functions .get(function_name) .expect(&format!("Function {} not found", function_name)); - todo!("Call stack for invoking Deimos-native functions") + + // get args + let mut args = + call_stack.top().stack()[call_stack.top().stack().len() - arg_count..].to_vec(); + + // push a new call frame + call_stack.push(CallFrame::new( + function.name_owned(), + function.instructions(), + )); + + // push args + call_stack.top_mut().stack_mut().append(&mut args); + + // set fp for callee frame + let callee_frame = call_stack.top_mut(); + callee_frame.set_fp(*arg_count); } Instruction::InvokePlatformStatic(function_name, arg_count) => { - let stack = state.stack(); - let args = &stack[stack.len() - arg_count..]; let platform_function = context .platform_functions() .get(function_name) .expect(&format!("Platform function {} not found", function_name)); - let result = platform_function(context, state, args); + + let args = &call_stack.top().stack()[call_stack.top().stack().len() - arg_count..]; + + let result = platform_function(args); match result { Ok(return_value) => { - // pop args off the stack - let mut i: usize = 0; - while i < *arg_count { - state.stack_mut().pop(); - i += 1; - } - state.stack_mut().push(return_value); + // push return value to top of caller stack + call_stack.top_mut().stack_mut().push(return_value); } Err(error) => { // Eventually we will have some kind of exception handling @@ -212,41 +289,63 @@ pub fn call( let constant = &context.constants()[constant_name]; match constant { Constant::String(string_constant) => { - state.registers_mut()[*destination] = - Value::String(string_constant.content_owned()); + registers[*destination] = Value::String(string_constant.content_owned()); } } } /* Add instructions */ Instruction::AddIntInt(lhs, rhs, destination) => { - let lhs_value = state.registers()[*lhs].unwrap_int(); - let rhs_value = state.registers()[*rhs].unwrap_int(); + let lhs_value = registers[*lhs].unwrap_int(); + let rhs_value = registers[*rhs].unwrap_int(); let result = lhs_value + rhs_value; - state.registers_mut()[*destination] = Value::Int(result); + registers[*destination] = Value::Int(result); } Instruction::AddStringInt(lhs, rhs, destination) => { - let lhs_value = state.registers()[*lhs].unwrap_string(); - let rhs_value = state.registers()[*rhs].unwrap_int(); + let lhs_value = registers[*lhs].unwrap_string(); + let rhs_value = registers[*rhs].unwrap_int(); let formatted = format!("{}{}", lhs_value, rhs_value); - state.registers_mut()[*destination] = Value::String(Rc::from(formatted)); + registers[*destination] = Value::String(Rc::from(formatted)); } Instruction::AddStringString(lhs, rhs, destination) => { - let lhs_value = state.registers()[*lhs].unwrap_string(); - let rhs_value = state.registers()[*rhs].unwrap_string(); + let lhs_value = registers[*lhs].unwrap_string(); + let rhs_value = registers[*rhs].unwrap_string(); let formatted = format!("{}{}", lhs_value, rhs_value); - state.registers_mut()[*destination] = Value::String(Rc::from(formatted)); + registers[*destination] = Value::String(Rc::from(formatted)); } /* Pop instructions */ Instruction::Pop(maybe_register) => { - let value = state.stack_mut().pop().unwrap(); + let value = call_stack.top_mut().stack_mut().pop().unwrap(); if let Some(register) = maybe_register { - state.registers_mut()[*register] = value; + registers[*register] = value; + } + } + } + + // Call frame bookkeeping + let top_frame = call_stack.top(); + if top_frame.active() { + call_stack.top_mut().increment_ip(); + } else { + // the "top" frame is inactive, which means it was created above + // set it to active for next instruction round + call_stack.top_mut().mark_active(); + + let maybe_penultimate_frame = call_stack.penultimate_mut(); + match maybe_penultimate_frame { + None => { + panic!("Invariant broken: only one call frame and it's inactive."); + } + Some(penultimate_frame) => { + if penultimate_frame.active() { + penultimate_frame.increment_ip(); + } else { + panic!("Invariant broken: top two call frames are inactive."); + } } } } - state.increment_ip(); } None // todo: returning results from main functions } diff --git a/dvm-lib/src/vm/value.rs b/dvm-lib/src/vm/value.rs index f2c63bf..20d7ca4 100644 --- a/dvm-lib/src/vm/value.rs +++ b/dvm-lib/src/vm/value.rs @@ -17,14 +17,14 @@ impl Value { pub fn unwrap_int(&self) -> i32 { match self { Value::Int(i) => *i, - _ => panic!(), + _ => panic!("Attempt to unwrap Int; found {:?}", self), } } pub fn unwrap_string(&self) -> &Rc { match self { Value::String(s) => s, - _ => panic!(), + _ => panic!("Attempt to unwrap String; found {:?}", self), } } } diff --git a/examples/call.dm b/examples/call.dm index 81e0478..03c3170 100644 --- a/examples/call.dm +++ b/examples/call.dm @@ -1,9 +1,9 @@ extern fn println(message: Any) -> Void fn add(a: Int, b: Int) -> Int - a + b + println(a + b) end fn main() - println(add(1, 2)) + add(1, 2) end \ No newline at end of file