Update calling conventions.

This commit is contained in:
Jesse Brault 2025-04-15 14:31:28 -05:00
parent 1263d84802
commit ae8f89bb4e
3 changed files with 201 additions and 55 deletions

View File

@ -1,7 +1,7 @@
use deimos::object_file::{DvmObjectFile, DvmPath}; use deimos::object_file::{DvmObjectFile, DvmPath};
use deimos::vm::constant::DvmConstant; use deimos::vm::constant::DvmConstant;
use deimos::vm::function::DvmFunction; use deimos::vm::function::DvmFunction;
use deimos::vm::instruction::{Immediate, Instruction, Location}; use deimos::vm::instruction::{Immediate, Instruction};
use deimos::vm::platform::get_std_lib_platform_functions; use deimos::vm::platform::get_std_lib_platform_functions;
use deimos::vm::source_code_location::SourceCodeLocation; use deimos::vm::source_code_location::SourceCodeLocation;
use deimos::vm::{dump_state, run_main_function, DvmContext, DvmState}; use deimos::vm::{dump_state, run_main_function, DvmContext, DvmState};
@ -16,16 +16,23 @@ fn main() {
// //
// fn main() { // fn main() {
// println "Hello, World!" // println "Hello, World!"
// println foo()
// } // }
//
// fn foo() = 42
let mut greeter_object_file = DvmObjectFile::new(DvmPath::new("greeter", "greeter.dmo")); let mut greeter_object_file = DvmObjectFile::new(DvmPath::new("greeter", "greeter.dmo"));
use Instruction::*;
let main_instructions = vec![ let main_instructions = vec![
Instruction::MoveImmediate { MoveImmediate {
destination: Location::Register(0), destination_register: 0,
immediate: Immediate::Constant(DvmConstant::String(String::from("Hello, World!"))), immediate: Immediate::Constant(DvmConstant::String(String::from("Hello, World!"))),
}, },
Instruction::Push { source_register: 0 }, // prepare for call to println
Instruction::InvokeStaticPlatform { Push { source_register: 0 }, // save r0
Push { source_register: 0 }, // arg0
// call
InvokeStaticPlatform {
function_name: Rc::new(String::from("std::core::println")), function_name: Rc::new(String::from("std::core::println")),
source_code_location: SourceCodeLocation { source_code_location: SourceCodeLocation {
source_file_name: String::from("greeter.dm"), source_file_name: String::from("greeter.dm"),
@ -33,20 +40,55 @@ fn main() {
char: 5, char: 5,
}, },
}, },
Instruction::Pop { // unwind from call
// println return value Pop {
destination_register: None, destination_register: None,
}, // arg0
Pop {
destination_register: Some(0), // restore r0
}, },
Instruction::Pop { // prepare for call to foo
// arg0 to println Push { source_register: 0 }, // save r0
InvokeStatic {
function_name: Rc::new(String::from("greeter::foo")),
source_code_location: SourceCodeLocation {
source_file_name: String::from("greeter.dm"),
line: 5,
char: 13,
},
},
Pop { destination_register: Some(0) }, // restore r0
TakeReturnValue {
destination_register: 1,
},
// call println again
Push { source_register: 1 }, // save r1
Push { source_register: 0 }, // save r0
Push { source_register: 1 }, // arg0
InvokeStaticPlatform {
function_name: Rc::new(String::from("std::core::println")),
source_code_location: SourceCodeLocation {
source_file_name: String::from("greeter.dm"),
line: 5,
char: 5,
},
},
Pop {
destination_register: None, destination_register: None,
}, // arg0
Pop {
destination_register: Some(0), // restore r0
}, },
Instruction::MoveImmediate { Pop {
destination: Location::Register(1), destination_register: Some(1), // restore r1
},
// return 0
MoveImmediate {
destination_register: 2,
immediate: Immediate::Int(0), immediate: Immediate::Int(0),
}, },
Instruction::Push { source_register: 1 }, SetReturnValue { source_register: 2 },
Instruction::Return, Return, // explicit, not needed
]; ];
let main_function = DvmFunction::new( let main_function = DvmFunction::new(
@ -58,16 +100,34 @@ fn main() {
char: 1, char: 1,
}, },
); );
let foo_function_instructions = vec![
MoveImmediate {
destination_register: 0,
immediate: Immediate::Int(42),
},
SetReturnValue { source_register: 0 },
Return, // explicit, not needed
];
let foo_function = DvmFunction::new(
"greeter::foo",
&foo_function_instructions,
SourceCodeLocation {
source_file_name: String::from("greeter.dm"),
line: 8,
char: 1,
},
);
greeter_object_file.add_function(main_function); greeter_object_file.add_function(main_function);
greeter_object_file.add_function(foo_function);
let mut state = DvmState::new(); let mut state = DvmState::new();
let mut context = DvmContext::new("greeter::main"); let mut context = DvmContext::new("greeter::main");
context.add_platform_functions(get_std_lib_platform_functions()); context.add_platform_functions(get_std_lib_platform_functions());
greeter_object_file.load_to_context(&mut context, &|path| { greeter_object_file.load_to_context(&mut context, &|path| todo!());
todo!()
});
let exit_code = run_main_function(&mut state, &context); let exit_code = run_main_function(&mut state, &context);
dump_state(&state, "After main!"); dump_state(&state, "After main!");

View File

@ -5,7 +5,7 @@ use std::rc::Rc;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum Instruction { pub enum Instruction {
MoveImmediate { MoveImmediate {
destination: Location, destination_register: usize,
immediate: Immediate, immediate: Immediate,
}, },
Copy { Copy {
@ -40,6 +40,12 @@ pub enum Instruction {
function_name: Rc<String>, function_name: Rc<String>,
source_code_location: SourceCodeLocation, source_code_location: SourceCodeLocation,
}, },
SetReturnValue {
source_register: usize,
},
TakeReturnValue {
destination_register: usize,
},
Add { Add {
left: u8, left: u8,
right: u8, right: u8,
@ -104,6 +110,9 @@ pub enum Instruction {
offset: isize, offset: isize,
}, },
Return, Return,
DumpState {
message: String
}
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -121,8 +130,11 @@ pub enum Immediate {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum Location { pub enum Location {
Register(usize), Register(usize),
Stack { StackFrameBase {
offset: usize, offset: isize,
},
StackTop {
offset: isize,
}, },
Field { Field {
object_register: usize, object_register: usize,
@ -131,6 +143,6 @@ pub enum Location {
Array { Array {
array_register: usize, array_register: usize,
index_register: usize, index_register: usize,
offset: Option<isize>, offset: isize,
}, },
} }

View File

@ -43,10 +43,12 @@ pub fn dump_state(state: &DvmState, message: &str) {
for (i, register) in state.registers.iter().enumerate() { for (i, register) in state.registers.iter().enumerate() {
println!("\tr{:x}: {:?}", i, register); println!("\tr{:x}: {:?}", i, register);
} }
println!("Return value register: {}", state.return_value_register);
println!("Call stack:"); println!("Call stack:");
for call in state.call_stack.iter() { for call in state.call_stack.iter() {
println!("\t{}", call); println!("\t{}", call);
} }
println!("Frame base index: {}", state.frame_base_index);
println!("Stack:"); println!("Stack:");
for (i, value) in state.stack.iter().enumerate() { for (i, value) in state.stack.iter().enumerate() {
println!("\t{}: {}", i, value); println!("\t{}: {}", i, value);
@ -63,10 +65,13 @@ macro_rules! dvm_panic {
pub type PlatformFunction = fn(state: &mut DvmState, context: &DvmContext); pub type PlatformFunction = fn(state: &mut DvmState, context: &DvmContext);
#[derive(Clone)]
pub struct DvmState { pub struct DvmState {
call_stack: Vec<DvmCall>, call_stack: Vec<DvmCall>,
current_instruction: Option<Instruction>, current_instruction: Option<Instruction>,
frame_base_index: usize,
registers: Vec<DvmValue>, registers: Vec<DvmValue>,
return_value_register: DvmValue,
stack: Vec<DvmValue>, stack: Vec<DvmValue>,
} }
@ -75,7 +80,9 @@ impl DvmState {
DvmState { DvmState {
call_stack: vec![], call_stack: vec![],
current_instruction: None, current_instruction: None,
frame_base_index: 0,
registers: vec![DvmValue::Empty; 16], registers: vec![DvmValue::Empty; 16],
return_value_register: DvmValue::Empty,
stack: vec![], stack: vec![],
} }
} }
@ -87,8 +94,14 @@ impl DvmState {
} }
pub fn get_stack_argument(&self, index: usize) -> DvmValue { pub fn get_stack_argument(&self, index: usize) -> DvmValue {
let target_index = self
.frame_base_index
.checked_sub(1 + index)
.unwrap_or_else(|| {
dvm_panic!(self, "Unsigned overflow: calculated negative target_index.");
});
self.stack self.stack
.get(self.stack.len() - 2 - index) .get(target_index)
.unwrap_or_else(|| { .unwrap_or_else(|| {
dvm_panic!(self, "Stack underflow!"); dvm_panic!(self, "Stack underflow!");
}) })
@ -150,6 +163,7 @@ impl DvmContext {
} }
} }
#[derive(Clone)]
pub struct DvmCall { pub struct DvmCall {
function_name: Rc<String>, function_name: Rc<String>,
source_code_location: SourceCodeLocation, source_code_location: SourceCodeLocation,
@ -169,9 +183,9 @@ impl Display for DvmCall {
fn update_index(index: &mut usize, offset: isize) { fn update_index(index: &mut usize, offset: isize) {
if offset.is_negative() { if offset.is_negative() {
*index -= offset.abs() as usize; *index -= offset.unsigned_abs();
} else { } else {
*index += offset as usize; *index += offset.unsigned_abs();
} }
} }
@ -193,27 +207,40 @@ fn pop_call_frame(state: &mut DvmState) {
fn compute_index_with_offset( fn compute_index_with_offset(
registers: &Vec<DvmValue>, registers: &Vec<DvmValue>,
index_register: usize, index_register: usize,
offset: &Option<isize>, offset: isize,
) -> usize { ) -> usize {
let index = registers[index_register].as_usize().expect(&format!( let index = registers[index_register].as_usize().expect(&format!(
"Could not convert the value of index_register {} to usize; found {:?}", "Could not convert the value of index_register {} to usize; found {:?}",
index_register, registers[index_register] index_register, registers[index_register]
)); ));
if let Some(o) = offset { if offset.is_negative() {
if o.is_negative() { index - offset.unsigned_abs()
index - o.unsigned_abs()
} else {
index + o.unsigned_abs()
}
} else { } else {
index index + offset.unsigned_abs()
} }
} }
fn copy_from_source(state: &DvmState, source: &Location) -> DvmValue { fn copy_from_source(state: &DvmState, source: &Location) -> DvmValue {
match source { match source {
Location::Register(register) => state.registers[*register].clone(), Location::Register(register) => state.registers[*register].clone(),
Location::Stack { offset } => state.stack[*offset].clone(), Location::StackFrameBase { offset } => {
let index = if offset.is_negative() {
state.frame_base_index - offset.unsigned_abs()
} else {
state.frame_base_index + offset.unsigned_abs()
};
state.stack[index].clone()
}
Location::StackTop { offset } => {
if *offset >= 0 {
dvm_panic!(
state,
&format!("Offset {} out of bounds (must be negative)", offset)
);
}
let stack_length = state.stack.len();
state.stack[stack_length - offset.unsigned_abs()].clone()
}
Location::Field { Location::Field {
object_register, object_register,
field_name, field_name,
@ -228,7 +255,7 @@ fn copy_from_source(state: &DvmState, source: &Location) -> DvmValue {
index_register, index_register,
offset, offset,
} => { } => {
let index = compute_index_with_offset(&state.registers, *index_register, offset); let index = compute_index_with_offset(&state.registers, *index_register, *offset);
state.registers[*array_register].map_array( state.registers[*array_register].map_array(
|a| a.borrow().read_element(index), |a| a.borrow().read_element(index),
|v| { |v| {
@ -244,8 +271,26 @@ fn write_to_destination(state: &mut DvmState, destination: &Location, value: Dvm
Location::Register(register) => { Location::Register(register) => {
state.registers[*register] = value; state.registers[*register] = value;
} }
Location::Stack { offset } => { Location::StackFrameBase { offset } => {
state.stack[*offset] = value; let index = if offset.is_negative() {
state.frame_base_index - offset.unsigned_abs()
} else {
state.frame_base_index + offset.unsigned_abs()
};
state.stack[index] = value;
}
Location::StackTop { offset } => {
if *offset >= 0 {
dvm_panic!(
state,
&format!(
"Stack top offset {} is out of range (must be negative)",
offset
)
);
}
let stack_length = state.stack.len();
state.stack[stack_length - offset.unsigned_abs()] = value;
} }
Location::Field { Location::Field {
object_register, object_register,
@ -261,7 +306,7 @@ fn write_to_destination(state: &mut DvmState, destination: &Location, value: Dvm
index_register, index_register,
offset, offset,
} => { } => {
let index = compute_index_with_offset(&state.registers, *index_register, offset); let index = compute_index_with_offset(&state.registers, *index_register, *offset);
state.registers[*array_register] state.registers[*array_register]
.expect_array() .expect_array()
.borrow_mut() .borrow_mut()
@ -310,7 +355,9 @@ fn immediate_to_value(immediate: &Immediate) -> DvmValue {
} }
pub fn run_main_function(state: &mut DvmState, context: &DvmContext) -> i32 { pub fn run_main_function(state: &mut DvmState, context: &DvmContext) -> i32 {
let main_function = context.static_functions.get(&context.main_function) let main_function = context
.static_functions
.get(&context.main_function)
.expect(&format!("No such main function: {}", context.main_function)); .expect(&format!("No such main function: {}", context.main_function));
push_call_frame( push_call_frame(
Rc::new(main_function.fqn().to_string()), Rc::new(main_function.fqn().to_string()),
@ -319,23 +366,29 @@ pub fn run_main_function(state: &mut DvmState, context: &DvmContext) -> i32 {
); );
run(main_function.instructions(), state, context); run(main_function.instructions(), state, context);
pop_call_frame(state); pop_call_frame(state);
state.pop_stack().expect_int_or_else(|v| { state.return_value_register.expect_int_or_else(|v| {
dvm_panic!(state, &format!("Expected DvmValue::Int, but found {}", v)); dvm_panic!(
state,
&format!("Expected main to return DvmValue::Int, but found {}", v)
);
}) })
} }
// TODO: find all possible unwraps/expects and wrap with call to dvm_panic // TODO: find all possible unwraps/expects and wrap with call to dvm_panic
pub fn run(instructions: &[Instruction], state: &mut DvmState, context: &DvmContext) { pub fn run(instructions: &[Instruction], state: &mut DvmState, context: &DvmContext) {
state.frame_base_index = state.stack.len();
let mut index = 0; let mut index = 0;
while index < instructions.len() { while index < instructions.len() {
state.current_instruction = Some(instructions[index].clone()); let current_instruction = &instructions[index];
state.current_instruction = Some(current_instruction.clone());
index += 1;
use Instruction::*; use Instruction::*;
match &instructions[index] { match current_instruction {
MoveImmediate { MoveImmediate {
destination, destination_register,
immediate, immediate,
} => { } => {
write_to_destination(state, destination, immediate_to_value(immediate)); state.registers[*destination_register] = immediate_to_value(immediate);
} }
Copy { Copy {
source, source,
@ -392,9 +445,18 @@ pub fn run(instructions: &[Instruction], state: &mut DvmState, context: &DvmCont
.clone(); .clone();
// Do call // Do call
state.stack.push(DvmValue::Empty); // space for return value // before:
// - push call frame
// - save current frame base index
// - set current frame base index to current stack size
push_call_frame(function_name.clone(), source_code_location.clone(), state); push_call_frame(function_name.clone(), source_code_location.clone(), state);
let saved_frame_base_index = state.frame_base_index;
state.frame_base_index = state.stack.len();
// actual call
run(static_function.instructions(), state, context); run(static_function.instructions(), state, context);
// reverse order
state.frame_base_index = saved_frame_base_index;
pop_call_frame(state); pop_call_frame(state);
} }
InvokeObject { InvokeObject {
@ -426,12 +488,12 @@ pub fn run(instructions: &[Instruction], state: &mut DvmState, context: &DvmCont
let instructions = function.instructions(); let instructions = function.instructions();
// Do call // Do call
state.stack.push(DvmValue::Empty); // space for return value
state.stack.push(object_value); // self object
push_call_frame(function_name.clone(), source_code_location.clone(), state); push_call_frame(function_name.clone(), source_code_location.clone(), state);
let saved_frame_base_index = state.frame_base_index;
state.frame_base_index = state.stack.len();
run(instructions, state, context); run(instructions, state, context);
state.frame_base_index = saved_frame_base_index;
pop_call_frame(state); pop_call_frame(state);
state.stack.pop(); // self object
} }
InvokeStaticPlatform { InvokeStaticPlatform {
function_name, function_name,
@ -449,9 +511,11 @@ pub fn run(instructions: &[Instruction], state: &mut DvmState, context: &DvmCont
}); });
// Do call // Do call
state.stack.push(DvmValue::Empty); // space for return value
push_call_frame(function_name.clone(), source_code_location.clone(), state); push_call_frame(function_name.clone(), source_code_location.clone(), state);
let saved_frame_base_index = state.frame_base_index;
state.frame_base_index = state.stack.len();
platform_function(state, context); platform_function(state, context);
state.frame_base_index = saved_frame_base_index;
pop_call_frame(state); pop_call_frame(state);
} }
InvokeObjectPlatform { InvokeObjectPlatform {
@ -483,12 +547,22 @@ pub fn run(instructions: &[Instruction], state: &mut DvmState, context: &DvmCont
} }
// Do call // Do call
state.stack.push(DvmValue::Empty); // space for return value
state.stack.push(object_value); // self object
push_call_frame(function_name.clone(), source_code_location.clone(), state); push_call_frame(function_name.clone(), source_code_location.clone(), state);
let saved_frame_base_index = state.frame_base_index;
state.frame_base_index = state.stack.len();
object_platform_function(state, context); object_platform_function(state, context);
state.frame_base_index = saved_frame_base_index;
pop_call_frame(state); pop_call_frame(state);
state.stack.pop(); // self object }
SetReturnValue { source_register } => {
state.return_value_register =
std::mem::take(&mut state.registers[*source_register]);
}
TakeReturnValue {
destination_register,
} => {
state.registers[*destination_register] =
std::mem::take(&mut state.return_value_register);
} }
Add { .. } => {} Add { .. } => {}
Subtract { .. } => {} Subtract { .. } => {}
@ -518,7 +592,7 @@ pub fn run(instructions: &[Instruction], state: &mut DvmState, context: &DvmCont
Return => { Return => {
break; break;
} }
DumpState { message } => dump_state(state, message),
} }
index += 1;
} }
} }