From d39e9afee2f7be7e54398517189ff55ace7a136a Mon Sep 17 00:00:00 2001 From: Jesse Brault Date: Wed, 4 Mar 2026 15:05:07 -0600 Subject: [PATCH] More work on calling and returning. --- dmc-lib/src/asm/asm_instruction.rs | 34 ++++++++ dmc-lib/src/ast/expression_statement.rs | 15 ++++ dmc-lib/src/ast/function.rs | 10 ++- dmc-lib/src/ast/statement.rs | 3 +- dvm-lib/src/instruction.rs | 65 +++++++++++++++ dvm-lib/src/vm/mod.rs | 104 ++++++++++++++++-------- examples/call.dm | 7 +- 7 files changed, 197 insertions(+), 41 deletions(-) diff --git a/dmc-lib/src/asm/asm_instruction.rs b/dmc-lib/src/asm/asm_instruction.rs index 9ab2ce7..afc9f56 100644 --- a/dmc-lib/src/asm/asm_instruction.rs +++ b/dmc-lib/src/asm/asm_instruction.rs @@ -10,6 +10,8 @@ pub enum AsmInstruction { InvokePlatformStatic(InvokePlatformStatic), LoadConstant(LoadConstant), Add(Add), + SetReturnValue(SetReturnValue), + Return(Return), } impl AsmInstruction { @@ -24,6 +26,8 @@ impl AsmInstruction { } AsmInstruction::LoadConstant(load_constant) => load_constant.dvm(), AsmInstruction::Add(add) => add.dvm(), + AsmInstruction::SetReturnValue(set_return_value) => set_return_value.dvm(), + AsmInstruction::Return(asm_return) => asm_return.dvm(), } } } @@ -181,3 +185,33 @@ impl Add { } } } + +#[derive(Debug)] +pub struct SetReturnValue { + source_register: usize, +} + +impl SetReturnValue { + pub fn new(source_register: usize) -> Self { + Self { source_register } + } + + pub fn dvm(&self) -> Instruction { + Instruction::SetReturnValue(self.source_register) + } +} + +#[derive(Debug)] +pub struct Return { + caller_pop_count: usize, +} + +impl Return { + pub fn new(caller_pop_count: usize) -> Self { + Self { caller_pop_count } + } + + pub fn dvm(&self) -> Instruction { + Instruction::Return(self.caller_pop_count) + } +} diff --git a/dmc-lib/src/ast/expression_statement.rs b/dmc-lib/src/ast/expression_statement.rs index 79ddd67..473cd0c 100644 --- a/dmc-lib/src/ast/expression_statement.rs +++ b/dmc-lib/src/ast/expression_statement.rs @@ -1,8 +1,10 @@ +use crate::asm::asm_instruction::{AsmInstruction, Pop, SetReturnValue}; use crate::ast::assemble_context::AssembleContext; use crate::ast::expression::Expression; use crate::constants_table::ConstantsTable; use crate::diagnostic::Diagnostic; use crate::symbol_table::SymbolTable; +use crate::type_info::TypeInfo; pub struct ExpressionStatement { expression: Box, @@ -36,10 +38,23 @@ impl ExpressionStatement { context: &mut AssembleContext, symbol_table: &SymbolTable, constants_table: &mut ConstantsTable, + is_last: bool, ) { match self.expression.as_ref() { Expression::Call(call) => { call.assemble(context, symbol_table, constants_table); + // if last expression and the return value of the callee isn't void, return that + // return value + if is_last && !matches!(call.type_info(), TypeInfo::Void) { + // move to register + let return_value_register = context.new_local_register(); + context.instruction(AsmInstruction::Pop(Pop::new(return_value_register))); + + // set return value + context.instruction(AsmInstruction::SetReturnValue(SetReturnValue::new( + return_value_register, + ))); + } } Expression::Additive(additive) => { additive.assemble(context, symbol_table, constants_table); diff --git a/dmc-lib/src/ast/function.rs b/dmc-lib/src/ast/function.rs index 43020ad..e7df7f4 100644 --- a/dmc-lib/src/ast/function.rs +++ b/dmc-lib/src/ast/function.rs @@ -1,3 +1,4 @@ +use crate::asm::asm_instruction::{AsmInstruction, Return}; use crate::ast::assemble_context::AssembleContext; use crate::ast::parameter::Parameter; use crate::ast::statement::Statement; @@ -158,10 +159,13 @@ impl Function { ) { context.new_function(&self.declared_name, &self.declared_name_source_range); context.new_block(&format!("{}_enter", self.declared_name)); - for statement in &self.statements { - statement.assemble(context, symbol_table, constants_table); + for (i, statement) in self.statements.iter().enumerate() { + let is_last = i == self.statements.len() - 1; + statement.assemble(context, symbol_table, constants_table, is_last); } - // todo: function exit, including popping passed args and pushing return value + // return + context.instruction(AsmInstruction::Return(Return::new(self.parameters.len()))); + context.complete_function(); } } diff --git a/dmc-lib/src/ast/statement.rs b/dmc-lib/src/ast/statement.rs index bbff3af..86d37f3 100644 --- a/dmc-lib/src/ast/statement.rs +++ b/dmc-lib/src/ast/statement.rs @@ -43,13 +43,14 @@ impl Statement { context: &mut AssembleContext, symbol_table: &SymbolTable, constants_table: &mut ConstantsTable, + is_last: bool, ) { match self { Statement::Let(let_statement) => { let_statement.assemble(context, symbol_table, constants_table); } Statement::Expression(expression_statement) => { - expression_statement.assemble(context, symbol_table, constants_table); + expression_statement.assemble(context, symbol_table, constants_table, is_last); } } } diff --git a/dvm-lib/src/instruction.rs b/dvm-lib/src/instruction.rs index c463791..b791cdf 100644 --- a/dvm-lib/src/instruction.rs +++ b/dvm-lib/src/instruction.rs @@ -1,9 +1,11 @@ +use std::fmt::Display; use std::rc::Rc; pub type Register = usize; pub type ConstantName = Rc; pub type FunctionName = Rc; pub type ArgCount = usize; +pub type CallerPopCount = usize; pub enum Instruction { MoveRegister(Register, Register), @@ -24,4 +26,67 @@ pub enum Instruction { AddIntInt(Register, Register, Register), AddStringInt(Register, Register, Register), AddStringString(Register, Register, Register), + + SetReturnValue(Register), + Return(CallerPopCount), +} + +impl Display for Instruction { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Instruction::MoveRegister(source, destination) => { + write!(f, "movr r{}, r{}", source, destination) + } + Instruction::MoveInt(i, destination) => { + write!(f, "movi {}, r{}", i, destination) + } + Instruction::MoveStackFrameOffset(offset, destination) => { + write!(f, "movsf {}, r{}", offset, destination) + } + Instruction::PushRegister(source) => { + write!(f, "pushr r{}", source) + } + Instruction::PushInt(i) => { + write!(f, "pushi {}", i) + } + Instruction::PushStackFrameOffset(offset) => { + write!(f, "pushsf {}", offset) + } + Instruction::InvokeStatic(name, arg_count) => { + write!(f, "invoke_static {} (arg_count: {})", name, arg_count) + } + Instruction::InvokePlatformStatic(name, arg_count) => { + write!( + f, + "invoke_platform_static {} (arg_count: {})", + name, arg_count + ) + } + Instruction::LoadStringConstant(name, destination) => { + write!(f, "loadsc {}, r{}", name, destination) + } + Instruction::Pop(maybe_destination) => { + if let Some(destination) = maybe_destination { + write!(f, "pop r{}", destination) + } else { + write!(f, "pop") + } + } + Instruction::AddIntInt(left, right, destination) => { + write!(f, "addii r{}, r{}, r{}", left, right, destination) + } + Instruction::AddStringInt(left, right, destination) => { + write!(f, "addsi r{}, r{}, r{}", left, right, destination) + } + Instruction::AddStringString(left, right, destination) => { + write!(f, "addss r{}, r{}, r{}", left, right, destination) + } + Instruction::SetReturnValue(source) => { + write!(f, "srv r{}", source) + } + Instruction::Return(caller_pop_count) => { + write!(f, "return (caller_pop_count: {})", caller_pop_count) + } + } + } } diff --git a/dvm-lib/src/vm/mod.rs b/dvm-lib/src/vm/mod.rs index 16700b4..a1cd40c 100644 --- a/dvm-lib/src/vm/mod.rs +++ b/dvm-lib/src/vm/mod.rs @@ -63,22 +63,22 @@ impl DvmContext { pub struct CallFrame<'a> { function_name: Rc, - active: bool, instructions: &'a Vec, ip: usize, stack: Vec, fp: usize, + return_value: Option, } impl<'a> CallFrame<'a> { fn new(function_name: Rc, instructions: &'a Vec) -> Self { Self { function_name, - active: false, instructions, ip: 0, stack: vec![], fp: 0, + return_value: None, } } @@ -86,14 +86,6 @@ impl<'a> CallFrame<'a> { &self.function_name } - fn active(&self) -> bool { - self.active - } - - fn mark_active(&mut self) { - self.active = true; - } - fn instructions(&self) -> &'a [Instruction] { self.instructions } @@ -121,6 +113,18 @@ impl<'a> CallFrame<'a> { fn set_fp(&mut self, fp: usize) { self.fp = fp; } + + fn return_value(&self) -> Option<&Value> { + self.return_value.as_ref() + } + + fn set_return_value(&mut self, return_value: Value) { + self.return_value = Some(return_value); + } + + fn take_return_value(&mut self) -> Option { + self.return_value.take() + } } pub struct CallStack<'a> { @@ -151,6 +155,14 @@ impl<'a> CallStack<'a> { &mut self.call_frames[call_frames_len - 1] // dang borrow checker } + fn maybe_top(&self) -> Option<&CallFrame<'a>> { + self.call_frames.last() + } + + fn maybe_top_mut(&mut self) -> Option<&mut CallFrame<'a>> { + self.call_frames.last_mut() + } + fn penultimate(&self) -> Option<&CallFrame<'a>> { self.call_frames.get(self.call_frames.len() - 2) } @@ -182,25 +194,28 @@ pub fn call<'a>( function.instructions(), )); - // mark it active - call_stack.top_mut().mark_active(); - // put each arg on the stack for argument in arguments { call_stack.top_mut().stack_mut().push(argument); } - while call_stack.top().ip() < call_stack.top().instructions().len() { + while call_stack.maybe_top().is_some() + && call_stack.top().ip() < call_stack.top().instructions().len() + { let instruction = &call_stack.top().instructions()[call_stack.top().ip()]; + // println!("{}", instruction); + match instruction { /* Move instructions */ Instruction::MoveRegister(source, destination) => { // copy value from one register to another register let value = registers[*source].clone(); registers[*destination] = value; + call_stack.top_mut().increment_ip(); } Instruction::MoveInt(value, destination) => { registers[*destination] = Value::Int(*value); + call_stack.top_mut().increment_ip(); } Instruction::MoveStackFrameOffset(offset, destination) => { // copy a value offset from the current frame pointer (fp) to a register @@ -216,6 +231,7 @@ pub fn call<'a>( )); let value = call_stack.top().stack()[value_index].clone(); registers[*destination] = value; + call_stack.top_mut().increment_ip(); } /* Push instructions */ @@ -223,9 +239,11 @@ pub fn call<'a>( // copy a value from a register to the top of the stack let value = registers[*source].clone(); call_stack.top_mut().stack_mut().push(value); + call_stack.top_mut().increment_ip(); } Instruction::PushInt(value) => { call_stack.top_mut().stack_mut().push(Value::Int(*value)); + call_stack.top_mut().increment_ip(); } Instruction::PushStackFrameOffset(offset) => { // copy a value from somewhere on the stack to the top of the stack @@ -236,6 +254,7 @@ pub fn call<'a>( .expect("Overflow when adding offset to fp"); let value = call_stack.top().stack()[value_index].clone(); call_stack.top_mut().stack_mut().push(value); + call_stack.top_mut().increment_ip(); } /* Invoke instructions */ @@ -249,6 +268,9 @@ pub fn call<'a>( let mut args = call_stack.top().stack()[call_stack.top().stack().len() - arg_count..].to_vec(); + // increment caller frame ip + call_stack.top_mut().increment_ip(); + // push a new call frame call_stack.push(CallFrame::new( function.name_owned(), @@ -274,14 +296,17 @@ pub fn call<'a>( let result = platform_function(args); match result { Ok(return_value) => { - // push return value to top of caller stack - call_stack.top_mut().stack_mut().push(return_value); + // push return value to top of caller stack if it's not null + if !matches!(return_value, Value::Null) { + call_stack.top_mut().stack_mut().push(return_value); + } } Err(error) => { // Eventually we will have some kind of exception handling panic!("{}", error); } } + call_stack.top_mut().increment_ip(); } /* Load constant instructions */ @@ -292,6 +317,7 @@ pub fn call<'a>( registers[*destination] = Value::String(string_constant.content_owned()); } } + call_stack.top_mut().increment_ip(); } /* Add instructions */ @@ -300,18 +326,21 @@ pub fn call<'a>( let rhs_value = registers[*rhs].unwrap_int(); let result = lhs_value + rhs_value; registers[*destination] = Value::Int(result); + call_stack.top_mut().increment_ip(); } Instruction::AddStringInt(lhs, rhs, destination) => { let lhs_value = registers[*lhs].unwrap_string(); let rhs_value = registers[*rhs].unwrap_int(); let formatted = format!("{}{}", lhs_value, rhs_value); registers[*destination] = Value::String(Rc::from(formatted)); + call_stack.top_mut().increment_ip(); } Instruction::AddStringString(lhs, rhs, destination) => { let lhs_value = registers[*lhs].unwrap_string(); let rhs_value = registers[*rhs].unwrap_string(); let formatted = format!("{}{}", lhs_value, rhs_value); registers[*destination] = Value::String(Rc::from(formatted)); + call_stack.top_mut().increment_ip(); } /* Pop instructions */ @@ -320,32 +349,37 @@ pub fn call<'a>( if let Some(register) = maybe_register { registers[*register] = value; } + call_stack.top_mut().increment_ip(); } - } - // 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(); + /* Return instructions */ + Instruction::SetReturnValue(register) => { + call_stack + .top_mut() + .set_return_value(registers[*register].clone()); + call_stack.top_mut().increment_ip(); + } - 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."); + Instruction::Return(caller_pop_count) => { + call_stack.pop(); // pop this frame + let maybe_caller = call_stack.maybe_top_mut(); + if let Some(caller) = maybe_caller { + for _ in 0..*caller_pop_count { + caller.stack_mut().pop(); + } + if let Some(return_value) = caller.take_return_value() { + caller.stack_mut().push(return_value); } } + // do not increment ip of the caller; this was done above BEFORE the call } } + + // if let Some(top) = call_stack.maybe_top() { + // println!(" stack: {:?}", top.stack()); + // println!(" registers: {:?}", registers); + // println!(" rv: {:?}", top.return_value()); + // } } None // todo: returning results from main functions } diff --git a/examples/call.dm b/examples/call.dm index 03c3170..5228904 100644 --- a/examples/call.dm +++ b/examples/call.dm @@ -1,9 +1,12 @@ extern fn println(message: Any) -> Void -fn add(a: Int, b: Int) -> Int +fn add(a: Int, b: Int) -> Void println(a + b) end fn main() - add(1, 2) + let x = 1 + 2 + 3 + add(x, 1) + add(x, 2) + add(21, 21) end \ No newline at end of file