Work on calling conventions and instructions.

This commit is contained in:
Jesse Brault 2026-03-04 12:13:05 -06:00
parent e1afb6b43b
commit 3cacde6a4c
9 changed files with 200 additions and 93 deletions

View File

@ -1,5 +1,5 @@
use dvm_lib::vm::DvmContext;
use dvm_lib::vm::value::Value; use dvm_lib::vm::value::Value;
use dvm_lib::vm::{DvmContext, DvmState};
use std::error::Error; use std::error::Error;
use std::fmt::{Debug, Display, Formatter}; use std::fmt::{Debug, Display, Formatter};
use std::rc::Rc; use std::rc::Rc;
@ -35,11 +35,7 @@ impl Display for StdCoreError {
impl Error for StdCoreError {} impl Error for StdCoreError {}
pub fn std_core_println( pub fn std_core_println(args: &[Value]) -> Result<Value, Box<dyn Error>> {
_context: &DvmContext,
_state: &DvmState,
args: &[Value],
) -> Result<Value, Box<dyn Error>> {
let maybe_to_print = args.get(0); let maybe_to_print = args.get(0);
match maybe_to_print { match maybe_to_print {
None => Err(Box::new(StdCoreError::new("Missing to_print arg"))), None => Err(Box::new(StdCoreError::new("Missing to_print arg"))),

View File

@ -9,7 +9,8 @@ use dmc_lib::diagnostic::Diagnostic;
use dmc_lib::parser::parse_compilation_unit; use dmc_lib::parser::parse_compilation_unit;
use dmc_lib::symbol_table::SymbolTable; use dmc_lib::symbol_table::SymbolTable;
use dvm_lib::vm::constant::{Constant, StringConstant}; 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::path::PathBuf;
use std::rc::Rc; use std::rc::Rc;
@ -54,7 +55,7 @@ fn main() {
if args.show_asm { if args.show_asm {
for asm_function in &asm_functions { 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<Value> = 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); println!("{:?}", result);
} }

View File

@ -107,15 +107,19 @@ impl Pop {
#[derive(Debug)] #[derive(Debug)]
pub struct InvokeStatic { pub struct InvokeStatic {
name: String, name: String,
arg_count: usize,
} }
impl InvokeStatic { impl InvokeStatic {
pub fn new(name: &str) -> Self { pub fn new(name: &str, arg_count: usize) -> Self {
Self { name: name.into() } Self {
name: name.into(),
arg_count,
}
} }
pub fn dvm(&self) -> Instruction { pub fn dvm(&self) -> Instruction {
Instruction::InvokeStatic(Rc::from(self.name.clone())) Instruction::InvokeStatic(Rc::from(self.name.clone()), self.arg_count)
} }
} }

View File

@ -194,8 +194,10 @@ impl Call {
InvokePlatformStatic::new(function_symbol.name(), arg_count), InvokePlatformStatic::new(function_symbol.name(), arg_count),
)); ));
} else { } else {
let arg_count = function_symbol.parameters().len();
context.instruction(AsmInstruction::InvokeStatic(InvokeStatic::new( context.instruction(AsmInstruction::InvokeStatic(InvokeStatic::new(
function_symbol.name(), function_symbol.name(),
arg_count,
))); )));
} }
} }

View File

@ -14,7 +14,7 @@ pub enum Instruction {
PushInt(i32), PushInt(i32),
PushStackFrameOffset(isize), PushStackFrameOffset(isize),
InvokeStatic(FunctionName), InvokeStatic(FunctionName, ArgCount),
InvokePlatformStatic(FunctionName, ArgCount), InvokePlatformStatic(FunctionName, ArgCount),
LoadStringConstant(ConstantName, Register), LoadStringConstant(ConstantName, Register),

View File

@ -1,6 +1,4 @@
use crate::vm::value::Value; use crate::vm::value::Value;
use crate::vm::{DvmContext, DvmState};
use std::error::Error; use std::error::Error;
pub type PlatformFunction = pub type PlatformFunction = fn(args: &[Value]) -> Result<Value, Box<dyn Error>>;
fn(context: &DvmContext, state: &DvmState, args: &[Value]) -> Result<Value, Box<dyn Error>>;

View File

@ -41,6 +41,7 @@ impl DvmContext {
&mut self.platform_functions &mut self.platform_functions
} }
#[deprecated]
pub fn add_function(&mut self, function: Function) { pub fn add_function(&mut self, function: Function) {
self.functions.insert(function.name_owned(), function); self.functions.insert(function.name_owned(), function);
} }
@ -49,6 +50,7 @@ impl DvmContext {
&self.constants &self.constants
} }
#[deprecated]
pub fn add_constant(&mut self, constant: Constant) { pub fn add_constant(&mut self, constant: Constant) {
match &constant { match &constant {
Constant::String(string_constant) => { Constant::String(string_constant) => {
@ -59,63 +61,110 @@ impl DvmContext {
} }
} }
pub struct DvmState { pub struct CallFrame<'a> {
stack: Vec<Value>, function_name: Rc<str>,
registers: Vec<Value>, active: bool,
instructions: &'a Vec<Instruction>,
ip: usize, ip: usize,
stack: Vec<Value>,
fp: usize, fp: usize,
} }
impl DvmState { impl<'a> CallFrame<'a> {
pub fn new() -> Self { fn new(function_name: Rc<str>, instructions: &'a Vec<Instruction>) -> Self {
Self { Self {
stack: vec![], function_name,
registers: vec![], active: false,
instructions,
ip: 0, ip: 0,
stack: vec![],
fp: 0, fp: 0,
} }
} }
pub fn stack(&self) -> &Vec<Value> { fn function_name(&self) -> &str {
&self.stack &self.function_name
} }
pub fn stack_mut(&mut self) -> &mut Vec<Value> { fn active(&self) -> bool {
&mut self.stack self.active
} }
pub fn registers(&self) -> &Vec<Value> { fn mark_active(&mut self) {
&self.registers self.active = true;
} }
pub fn registers_mut(&mut self) -> &mut Vec<Value> { fn instructions(&self) -> &'a [Instruction] {
&mut self.registers self.instructions
} }
pub fn ensure_registers(&mut self, count: usize) { fn ip(&self) -> usize {
self.registers.resize_with(count, Default::default);
}
pub fn ip(&self) -> usize {
self.ip self.ip
} }
pub fn increment_ip(&mut self) { fn increment_ip(&mut self) {
self.ip += 1; self.ip += 1;
} }
pub fn fp(&self) -> usize { fn stack(&self) -> &[Value] {
&self.stack
}
fn stack_mut(&mut self) -> &mut Vec<Value> {
&mut self.stack
}
fn fp(&self) -> usize {
self.fp self.fp
} }
pub fn set_fp(&mut self, fp: usize) { fn set_fp(&mut self, fp: usize) {
self.fp = fp; self.fp = fp;
} }
} }
pub fn call( pub struct CallStack<'a> {
context: &DvmContext, call_frames: Vec<CallFrame<'a>>,
state: &mut DvmState, }
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<Value>,
call_stack: &mut CallStack<'a>,
function_name: &str, function_name: &str,
arguments: Vec<Value>, arguments: Vec<Value>,
) -> Option<Value> { ) -> Option<Value> {
@ -124,81 +173,109 @@ pub fn call(
.get(function_name) .get(function_name)
.expect(&format!("Function {} not found", function_name)); .expect(&format!("Function {} not found", function_name));
let instructions = function.instructions(); // ensure enough registers
state.ensure_registers(function.register_count()); 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 // put each arg on the stack
for argument in arguments { for argument in arguments {
state.stack_mut().push(argument); call_stack.top_mut().stack_mut().push(argument);
} }
while state.ip() < instructions.len() { while call_stack.top().ip() < call_stack.top().instructions().len() {
let instruction = &instructions[state.ip()]; let instruction = &call_stack.top().instructions()[call_stack.top().ip()];
match instruction { match instruction {
/* Move instructions */ /* Move instructions */
Instruction::MoveRegister(source, destination) => { Instruction::MoveRegister(source, destination) => {
// copy value from one register to another register // copy value from one register to another register
let value = state.registers()[*source].clone(); let value = registers[*source].clone();
state.registers_mut()[*destination] = value; registers[*destination] = value;
} }
Instruction::MoveInt(value, destination) => { Instruction::MoveInt(value, destination) => {
state.registers_mut()[*destination] = Value::Int(*value); registers[*destination] = Value::Int(*value);
} }
Instruction::MoveStackFrameOffset(offset, destination) => { Instruction::MoveStackFrameOffset(offset, destination) => {
// copy a value offset from the current frame pointer (fp) to a register // copy a value offset from the current frame pointer (fp) to a register
let value_index = state let value_index =
call_stack
.top()
.fp() .fp()
.checked_add_signed(*offset) .checked_add_signed(*offset)
.expect("Overflow when adding offset to fp"); .expect(&format!(
let value = state.stack()[value_index].clone(); "Overflow when adding offset {} to fp {}",
state.registers_mut()[*destination] = value; *offset,
call_stack.top().fp()
));
let value = call_stack.top().stack()[value_index].clone();
registers[*destination] = value;
} }
/* Push instructions */ /* Push instructions */
Instruction::PushRegister(source) => { Instruction::PushRegister(source) => {
// copy a value from a register to the top of the stack // copy a value from a register to the top of the stack
let value = state.registers()[*source].clone(); let value = registers[*source].clone();
state.stack_mut().push(value); call_stack.top_mut().stack_mut().push(value);
} }
Instruction::PushInt(value) => { Instruction::PushInt(value) => {
state.stack_mut().push(Value::Int(*value)); call_stack.top_mut().stack_mut().push(Value::Int(*value));
} }
Instruction::PushStackFrameOffset(offset) => { Instruction::PushStackFrameOffset(offset) => {
// copy a value from somewhere on the stack to the top of the stack // 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() .fp()
.checked_add_signed(*offset) .checked_add_signed(*offset)
.expect("Overflow when adding offset to fp"); .expect("Overflow when adding offset to fp");
let value = state.stack()[value_index].clone(); let value = call_stack.top().stack()[value_index].clone();
state.stack_mut().push(value); call_stack.top_mut().stack_mut().push(value);
} }
/* Invoke instructions */ /* Invoke instructions */
Instruction::InvokeStatic(function_name) => { Instruction::InvokeStatic(function_name, arg_count) => {
let function = context let function = context
.functions .functions
.get(function_name) .get(function_name)
.expect(&format!("Function {} not found", 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) => { Instruction::InvokePlatformStatic(function_name, arg_count) => {
let stack = state.stack();
let args = &stack[stack.len() - arg_count..];
let platform_function = context let platform_function = context
.platform_functions() .platform_functions()
.get(function_name) .get(function_name)
.expect(&format!("Platform function {} not found", 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 { match result {
Ok(return_value) => { Ok(return_value) => {
// pop args off the stack // push return value to top of caller stack
let mut i: usize = 0; call_stack.top_mut().stack_mut().push(return_value);
while i < *arg_count {
state.stack_mut().pop();
i += 1;
}
state.stack_mut().push(return_value);
} }
Err(error) => { Err(error) => {
// Eventually we will have some kind of exception handling // Eventually we will have some kind of exception handling
@ -212,41 +289,63 @@ pub fn call(
let constant = &context.constants()[constant_name]; let constant = &context.constants()[constant_name];
match constant { match constant {
Constant::String(string_constant) => { Constant::String(string_constant) => {
state.registers_mut()[*destination] = registers[*destination] = Value::String(string_constant.content_owned());
Value::String(string_constant.content_owned());
} }
} }
} }
/* Add instructions */ /* Add instructions */
Instruction::AddIntInt(lhs, rhs, destination) => { Instruction::AddIntInt(lhs, rhs, destination) => {
let lhs_value = state.registers()[*lhs].unwrap_int(); let lhs_value = registers[*lhs].unwrap_int();
let rhs_value = state.registers()[*rhs].unwrap_int(); let rhs_value = registers[*rhs].unwrap_int();
let result = lhs_value + rhs_value; let result = lhs_value + rhs_value;
state.registers_mut()[*destination] = Value::Int(result); registers[*destination] = Value::Int(result);
} }
Instruction::AddStringInt(lhs, rhs, destination) => { Instruction::AddStringInt(lhs, rhs, destination) => {
let lhs_value = state.registers()[*lhs].unwrap_string(); let lhs_value = registers[*lhs].unwrap_string();
let rhs_value = state.registers()[*rhs].unwrap_int(); let rhs_value = registers[*rhs].unwrap_int();
let formatted = format!("{}{}", lhs_value, rhs_value); 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) => { Instruction::AddStringString(lhs, rhs, destination) => {
let lhs_value = state.registers()[*lhs].unwrap_string(); let lhs_value = registers[*lhs].unwrap_string();
let rhs_value = state.registers()[*rhs].unwrap_string(); let rhs_value = registers[*rhs].unwrap_string();
let formatted = format!("{}{}", lhs_value, rhs_value); 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 */ /* Pop instructions */
Instruction::Pop(maybe_register) => { 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 { 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 None // todo: returning results from main functions
} }

View File

@ -17,14 +17,14 @@ impl Value {
pub fn unwrap_int(&self) -> i32 { pub fn unwrap_int(&self) -> i32 {
match self { match self {
Value::Int(i) => *i, Value::Int(i) => *i,
_ => panic!(), _ => panic!("Attempt to unwrap Int; found {:?}", self),
} }
} }
pub fn unwrap_string(&self) -> &Rc<str> { pub fn unwrap_string(&self) -> &Rc<str> {
match self { match self {
Value::String(s) => s, Value::String(s) => s,
_ => panic!(), _ => panic!("Attempt to unwrap String; found {:?}", self),
} }
} }
} }

View File

@ -1,9 +1,9 @@
extern fn println(message: Any) -> Void extern fn println(message: Any) -> Void
fn add(a: Int, b: Int) -> Int fn add(a: Int, b: Int) -> Int
a + b println(a + b)
end end
fn main() fn main()
println(add(1, 2)) add(1, 2)
end end