Compare commits

..

No commits in common. "a7d162b2caf1d9ed353593c87830b55044b2def1" and "1171ce75f99be7521f148d09b509c418ee53fa96" have entirely different histories.

42 changed files with 1169 additions and 959 deletions

View File

@ -1,6 +0,0 @@
# Project Backlog
As of March 8, 2026.
- Type check return value from functions. Currently, the statement, if an
expression, and if there is a non-Void return type on the function, just gets
returned.

View File

@ -4,15 +4,16 @@ use codespan_reporting::files::SimpleFiles;
use codespan_reporting::term; use codespan_reporting::term;
use codespan_reporting::term::termcolor::{ColorChoice, StandardStream}; use codespan_reporting::term::termcolor::{ColorChoice, StandardStream};
use dm_std_lib::add_all_std_core; use dm_std_lib::add_all_std_core;
use dmc_lib::ast::module_level_declaration::ModuleLevelDeclaration;
use dmc_lib::constants_table::ConstantsTable; use dmc_lib::constants_table::ConstantsTable;
use dmc_lib::diagnostic::Diagnostic; 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::function::Function;
use dvm_lib::vm::value::Value; use dvm_lib::vm::value::Value;
use dvm_lib::vm::{CallStack, DvmContext, call}; use dvm_lib::vm::{CallStack, DvmContext, call};
use std::path::PathBuf; use std::path::PathBuf;
use std::rc::Rc;
#[derive(Debug, Parser)] #[derive(Debug, Parser)]
#[command(name = "dm", about = "Deimos", version = "0.1.0", long_about = None)] #[command(name = "dm", about = "Deimos", version = "0.1.0", long_about = None)]
@ -56,26 +57,23 @@ fn main() {
let type_check_diagnostics = compilation_unit.type_check(&symbol_table); let type_check_diagnostics = compilation_unit.type_check(&symbol_table);
check_and_report_diagnostics(&files, script_file_id, &type_check_diagnostics); check_and_report_diagnostics(&files, script_file_id, &type_check_diagnostics);
let mut ir_functions = compilation_unit.to_ir(&symbol_table);
if args.show_ir { if args.show_ir {
for ir_function in &ir_functions { for declaration in compilation_unit.declarations() {
println!("{}", ir_function); if let ModuleLevelDeclaration::Function(function) = declaration {
let mut ir_function = function.to_ir(&symbol_table);
let register_assignments = ir_function.assign_registers(args.register_count);
println!("{}", ir_function);
println!("{:?}", register_assignments);
}
} }
} }
let mut functions: Vec<Function> = vec![];
let mut constants_table = ConstantsTable::new(); let mut constants_table = ConstantsTable::new();
let asm_functions = compilation_unit.assemble(&symbol_table, &mut constants_table);
for ir_function in &mut ir_functions {
let (_, stack_size) = ir_function.assign_registers(args.register_count);
let function = ir_function.assemble(stack_size, &mut constants_table);
functions.push(function);
}
if args.show_asm { if args.show_asm {
for function in &functions { for asm_function in &asm_functions {
println!("{}", function); println!("{:#?}", asm_function);
} }
} }
@ -84,20 +82,19 @@ fn main() {
// add std::core fns // add std::core fns
add_all_std_core(&mut dvm_context); add_all_std_core(&mut dvm_context);
for function in functions { for asm_function in &asm_functions {
dvm_context let function = asm_function.dvm();
.functions_mut() dvm_context.add_function(function);
.insert(function.name_owned(), function);
} }
for (name, content) in &constants_table.string_constants() { for (name, content) in &constants_table.string_constants() {
dvm_context.constants_mut().insert( dvm_context.add_constant(Constant::String(StringConstant::new(
name.clone(), Rc::from(name.clone()),
Constant::String(StringConstant::new(name.clone(), content.clone())), content.as_str(),
); )));
} }
let mut registers: Vec<Value> = vec![Value::Null; args.register_count]; let mut registers: Vec<Value> = vec![];
let mut call_stack = CallStack::new(); let mut call_stack = CallStack::new();
let result = call( let result = call(
@ -107,9 +104,7 @@ fn main() {
"main", "main",
vec![], vec![],
); );
if let Some(value) = result { println!("{:?}", result);
println!("{}", value);
}
} }
fn check_and_report_diagnostics( fn check_and_report_diagnostics(

View File

@ -0,0 +1,20 @@
use crate::asm::asm_instruction::AsmInstruction;
#[derive(Debug)]
pub struct AsmBlock {
name: String,
instructions: Vec<AsmInstruction>,
}
impl AsmBlock {
pub fn new(name: &str, instructions: Vec<AsmInstruction>) -> Self {
Self {
name: name.into(),
instructions,
}
}
pub fn instructions(&self) -> &[AsmInstruction] {
&self.instructions
}
}

View File

@ -0,0 +1,29 @@
use crate::asm::asm_block::AsmBlock;
use crate::asm::asm_instruction::AsmInstruction;
use dvm_lib::vm::function::Function;
#[derive(Debug)]
pub struct AsmFunction {
name: String,
blocks: Vec<AsmBlock>,
}
impl AsmFunction {
pub fn new(name: &str, blocks: Vec<AsmBlock>) -> Self {
Self {
name: name.into(),
blocks,
}
}
pub fn dvm(&self) -> Function {
// very naive impl
let dvm_instructions = self
.blocks
.iter()
.flat_map(|block| block.instructions().iter().map(AsmInstruction::dvm))
.collect::<Vec<_>>();
Function::new(&self.name, dvm_instructions, 16)
}
}

View File

@ -0,0 +1,217 @@
use dvm_lib::instruction::Instruction;
use std::rc::Rc;
#[derive(Debug)]
pub enum AsmInstruction {
Move(Move),
Push(Push),
Pop(Pop),
InvokeStatic(InvokeStatic),
InvokePlatformStatic(InvokePlatformStatic),
LoadConstant(LoadConstant),
Add(Add),
SetReturnValue(SetReturnValue),
Return(Return),
}
impl AsmInstruction {
pub fn dvm(&self) -> Instruction {
match self {
AsmInstruction::Move(asm_move) => asm_move.dvm(),
AsmInstruction::Push(push) => push.dvm(),
AsmInstruction::Pop(pop) => pop.dvm(),
AsmInstruction::InvokeStatic(invoke_static) => invoke_static.dvm(),
AsmInstruction::InvokePlatformStatic(invoke_platform_static) => {
invoke_platform_static.dvm()
}
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(),
}
}
}
#[derive(Debug)]
pub enum Operand {
IntegerLiteral(i32),
Register(usize),
StackFrameOffset(isize),
}
#[derive(Debug)]
pub struct Move {
source: Operand,
destination_register: usize,
}
impl Move {
pub fn new(source: Operand, destination_register: usize) -> Self {
Self {
source,
destination_register,
}
}
pub fn dvm(&self) -> Instruction {
match self.source {
Operand::IntegerLiteral(i) => Instruction::MoveInt(i, self.destination_register),
Operand::Register(register) => {
Instruction::MoveRegister(register, self.destination_register)
}
Operand::StackFrameOffset(offset) => {
Instruction::MoveStackFrameOffset(offset, self.destination_register)
}
}
}
}
#[derive(Debug)]
pub struct Push {
source: Operand,
}
impl Push {
pub fn new(source: Operand) -> Self {
Self { source }
}
pub fn dvm(&self) -> Instruction {
match self.source {
Operand::IntegerLiteral(i) => Instruction::PushInt(i),
Operand::Register(register) => Instruction::PushRegister(register),
Operand::StackFrameOffset(offset) => Instruction::PushStackFrameOffset(offset),
}
}
}
#[derive(Debug)]
pub struct Pop {
destination_register: Option<usize>,
}
impl Pop {
pub fn new(destination_register: usize) -> Self {
Self {
destination_register: Some(destination_register),
}
}
pub fn empty() -> Self {
Self {
destination_register: None,
}
}
pub fn dvm(&self) -> Instruction {
Instruction::Pop(self.destination_register)
}
}
#[derive(Debug)]
pub struct InvokeStatic {
name: String,
arg_count: usize,
}
impl InvokeStatic {
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()), self.arg_count)
}
}
#[derive(Debug)]
pub struct InvokePlatformStatic {
name: String,
arg_count: usize,
}
impl InvokePlatformStatic {
pub fn new(name: &str, arg_count: usize) -> Self {
Self {
name: name.into(),
arg_count,
}
}
pub fn dvm(&self) -> Instruction {
Instruction::InvokePlatformStatic(Rc::from(self.name.clone()), self.arg_count)
}
}
#[derive(Debug)]
pub struct LoadConstant {
name: String,
destination_register: usize,
}
impl LoadConstant {
pub fn new(name: &str, destination_register: usize) -> Self {
LoadConstant {
name: name.into(),
destination_register,
}
}
pub fn dvm(&self) -> Instruction {
Instruction::LoadStringConstant(Rc::from(self.name.clone()), self.destination_register)
}
}
#[derive(Debug)]
pub enum Add {
IntInt(usize, usize, usize),
StringString(usize, usize, usize),
StringInt(usize, usize, usize),
}
impl Add {
pub fn dvm(&self) -> Instruction {
match self {
Add::IntInt(lhs, rhs, destination) => Instruction::AddIntInt(*lhs, *rhs, *destination),
Add::StringString(lhs, rhs, destination) => {
Instruction::AddStringString(*lhs, *rhs, *destination)
}
Add::StringInt(lhs, rhs, destination) => {
Instruction::AddStringInt(*lhs, *rhs, *destination)
}
}
}
}
#[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)
}
}

47
dmc-lib/src/asm/mod.rs Normal file
View File

@ -0,0 +1,47 @@
pub mod asm_block;
pub mod asm_function;
pub mod asm_instruction;
#[cfg(test)]
mod smoke_tests {
use crate::asm::asm_function::AsmFunction;
use crate::constants_table::ConstantsTable;
use crate::parser::parse_compilation_unit;
use crate::symbol_table::SymbolTable;
fn assemble(src: &str) -> Vec<AsmFunction> {
let parse_result = parse_compilation_unit(src);
let mut compilation_unit = match parse_result {
Ok(compilation_unit) => compilation_unit,
Err(diagnostics) => {
for diagnostic in diagnostics {
eprintln!("{:?}", diagnostic);
}
panic!();
}
};
let mut symbol_table = SymbolTable::new();
compilation_unit.gather_declared_names(&mut symbol_table);
compilation_unit.check_name_usages(&symbol_table);
compilation_unit.type_check(&symbol_table);
compilation_unit.assemble(&symbol_table, &mut ConstantsTable::new())
}
#[test]
fn multiple_statements() {
let asm_functions = assemble(
"
extern fn println() -> Void
fn main()
let x = 42
println(x)
println(16)
let y = \"Hello, World!\"
println(y)
end",
);
for asm_function in &asm_functions {
println!("{:#?}", asm_function);
}
}
}

View File

@ -1,10 +1,21 @@
use crate::asm::asm_instruction::{Add, AsmInstruction, LoadConstant, Move, Operand, Pop};
use crate::ast::assemble_context::AssembleContext;
use crate::ast::ast_to_ir_util::expression_to_ir_expression;
use crate::ast::expression::Expression; use crate::ast::expression::Expression;
use crate::ast::ir_builder::IrBuilder; use crate::ast::ir_builder::IrBuilder;
use crate::constants_table::ConstantsTable;
use crate::diagnostic::Diagnostic; use crate::diagnostic::Diagnostic;
use crate::ir::ir_add::IrAdd; use crate::ir::ir_add::IrAdd;
use crate::ir::ir_assign::IrAssign;
use crate::ir::ir_expression::IrExpression;
use crate::ir::ir_operation::IrOperation;
use crate::ir::ir_statement::IrStatement;
use crate::ir::ir_variable::IrVariable;
use crate::source_range::SourceRange; use crate::source_range::SourceRange;
use crate::symbol::ExpressibleSymbol;
use crate::symbol_table::SymbolTable; use crate::symbol_table::SymbolTable;
use crate::type_info::TypeInfo; use crate::type_info::TypeInfo;
use std::ops::Deref;
pub struct AdditiveExpression { pub struct AdditiveExpression {
lhs: Box<Expression>, lhs: Box<Expression>,
@ -64,17 +75,111 @@ impl AdditiveExpression {
} }
pub fn to_ir(&self, builder: &mut IrBuilder, symbol_table: &SymbolTable) -> IrAdd { pub fn to_ir(&self, builder: &mut IrBuilder, symbol_table: &SymbolTable) -> IrAdd {
let lhs_ir_expression = self let lhs_ir_expression = expression_to_ir_expression(&self.lhs, builder, symbol_table)
.lhs
.to_ir(builder, symbol_table)
.expect("Attempt to add non-expression"); .expect("Attempt to add non-expression");
let rhs_ir_expression = self let rhs_ir_expression = expression_to_ir_expression(&self.rhs, builder, symbol_table)
.rhs
.to_ir(builder, symbol_table)
.expect("Attempt to add non-expression"); .expect("Attempt to add non-expression");
IrAdd::new(lhs_ir_expression, rhs_ir_expression) IrAdd::new(lhs_ir_expression, rhs_ir_expression)
} }
fn assemble_side(
expression: &Expression,
context: &mut AssembleContext,
symbol_table: &SymbolTable,
constants_table: &mut ConstantsTable,
) -> usize {
match expression {
Expression::Call(call) => {
call.assemble(context, symbol_table, constants_table);
let register = context.new_local_register();
context.instruction(AsmInstruction::Pop(Pop::new(register)));
register
}
Expression::IntegerLiteral(integer_literal) => {
let register = context.new_local_register();
context.instruction(AsmInstruction::Move(Move::new(
Operand::IntegerLiteral(integer_literal.value()),
register,
)));
register
}
Expression::String(string_literal) => {
let register = context.new_local_register();
let constant_name = constants_table.insert_string(string_literal.content());
context.instruction(AsmInstruction::LoadConstant(LoadConstant::new(
&constant_name,
register,
)));
register
}
Expression::Identifier(identifier) => {
let register = context.new_local_register();
match identifier.expressible_symbol() {
ExpressibleSymbol::Function(_) => unreachable!(),
ExpressibleSymbol::Parameter(parameter_symbol) => {
let offset = parameter_symbol.borrow().stack_frame_offset();
context.instruction(AsmInstruction::Move(Move::new(
Operand::StackFrameOffset(offset),
register,
)));
}
ExpressibleSymbol::Variable(variable_symbol) => {
context.instruction(AsmInstruction::Move(Move::new(
Operand::Register(variable_symbol.borrow().register()),
register,
)));
}
}
register
}
Expression::Additive(additive_expression) => {
additive_expression.assemble(context, symbol_table, constants_table)
}
}
}
pub fn assemble(
&self,
context: &mut AssembleContext,
symbol_table: &SymbolTable,
constants_table: &mut ConstantsTable,
) -> usize {
let lhs_register = Self::assemble_side(&self.lhs, context, symbol_table, constants_table);
let rhs_register = Self::assemble_side(&self.rhs, context, symbol_table, constants_table);
let result_register = context.new_local_register();
match self.lhs.type_info() {
TypeInfo::Integer => match self.rhs.type_info() {
TypeInfo::Integer => {
context.instruction(AsmInstruction::Add(Add::IntInt(
lhs_register,
rhs_register,
result_register,
)));
}
_ => unreachable!(),
},
TypeInfo::String => match self.rhs.type_info() {
TypeInfo::Integer => {
context.instruction(AsmInstruction::Add(Add::StringInt(
lhs_register,
rhs_register,
result_register,
)));
}
TypeInfo::String => {
context.instruction(AsmInstruction::Add(Add::StringString(
lhs_register,
rhs_register,
result_register,
)));
}
_ => unreachable!(),
},
_ => unreachable!(),
}
result_register
}
pub fn type_info(&self) -> TypeInfo { pub fn type_info(&self) -> TypeInfo {
self.lhs.type_info().additive_result(&self.rhs.type_info()) self.lhs.type_info().additive_result(&self.rhs.type_info())
} }

View File

@ -0,0 +1,114 @@
use crate::asm::asm_block::AsmBlock;
use crate::asm::asm_function::AsmFunction;
use crate::asm::asm_instruction::AsmInstruction;
use crate::source_range::SourceRange;
pub struct AssembleContext {
functions: Vec<AsmFunction>,
current_function: Option<FunctionAssembler>,
}
impl AssembleContext {
pub fn new() -> Self {
Self {
functions: vec![],
current_function: None,
}
}
pub fn take_functions(&mut self) -> Vec<AsmFunction> {
std::mem::take(&mut self.functions)
}
pub fn new_function(&mut self, name: &str, source_range: &SourceRange) {
self.current_function = Some(FunctionAssembler::new(name, source_range));
}
pub fn complete_function(&mut self) {
let mut function_assembler = self.current_function.take().unwrap();
function_assembler.complete_block();
let asm_function = function_assembler.result();
self.functions.push(asm_function);
}
pub fn new_block(&mut self, name: &str) {
self.current_function.as_mut().unwrap().new_block(name);
}
pub fn new_local_register(&mut self) -> usize {
self.current_function.as_mut().unwrap().new_local_register()
}
pub fn instruction(&mut self, to_add: AsmInstruction) {
self.current_function.as_mut().unwrap().instruction(to_add);
}
}
struct FunctionAssembler {
name: String,
source_range: SourceRange,
blocks: Vec<AsmBlock>,
current_block: Option<BlockAssembler>,
local_register_counter: usize,
}
impl FunctionAssembler {
fn new(name: &str, source_range: &SourceRange) -> Self {
Self {
name: name.into(),
source_range: source_range.clone(),
blocks: vec![],
current_block: None,
local_register_counter: 0,
}
}
fn new_block(&mut self, name: &str) {
let maybe_block_assembler = self.current_block.take();
if let Some(mut block_assembler) = maybe_block_assembler {
self.blocks.push(block_assembler.result());
}
self.current_block = Some(BlockAssembler::new(name))
}
fn complete_block(&mut self) {
let mut block_assembler = self.current_block.take().unwrap();
self.blocks.push(block_assembler.result());
}
fn result(&mut self) -> AsmFunction {
AsmFunction::new(&self.name, std::mem::take(&mut self.blocks))
}
fn new_local_register(&mut self) -> usize {
let register = self.local_register_counter;
self.local_register_counter += 1;
register
}
fn instruction(&mut self, to_add: AsmInstruction) {
self.current_block.as_mut().unwrap().instruction(to_add);
}
}
struct BlockAssembler {
name: String,
instructions: Vec<AsmInstruction>,
}
impl BlockAssembler {
fn new(name: &str) -> Self {
Self {
name: name.into(),
instructions: vec![],
}
}
fn instruction(&mut self, to_add: AsmInstruction) {
self.instructions.push(to_add);
}
fn result(&mut self) -> AsmBlock {
AsmBlock::new(&self.name, std::mem::take(&mut self.instructions))
}
}

View File

@ -0,0 +1,65 @@
use crate::ast::expression::Expression;
use crate::ast::ir_builder::IrBuilder;
use crate::ir::ir_assign::IrAssign;
use crate::ir::ir_expression::IrExpression;
use crate::ir::ir_operation::IrOperation;
use crate::ir::ir_statement::IrStatement;
use crate::ir::ir_variable::IrVariable;
use crate::symbol_table::SymbolTable;
use crate::type_info::TypeInfo;
use std::cell::RefCell;
use std::rc::Rc;
pub fn expression_to_ir_expression(
expression: &Expression,
builder: &mut IrBuilder,
symbol_table: &SymbolTable,
) -> Option<IrExpression> {
match expression {
Expression::Call(call) => {
let ir_call = call.to_ir(builder, symbol_table);
if matches!(call.type_info(), TypeInfo::Void) {
builder
.current_block_mut()
.add_statement(IrStatement::Call(ir_call));
None
} else {
let t_var = IrVariable::new_vr(
builder.new_t_var().into(),
builder.current_block().id(),
call.type_info(),
);
let as_rc = Rc::new(RefCell::new(t_var));
let assign = IrAssign::new(as_rc.clone(), IrOperation::Call(ir_call));
builder
.current_block_mut()
.add_statement(IrStatement::Assign(assign));
Some(IrExpression::Variable(as_rc))
}
}
Expression::IntegerLiteral(integer_literal) => {
Some(IrExpression::Int(integer_literal.value()))
}
Expression::String(string_literal) => {
Some(IrExpression::String(string_literal.content().into()))
}
Expression::Identifier(identifier) => {
let expressible_symbol = identifier.expressible_symbol();
Some(expressible_symbol.ir_expression())
}
Expression::Additive(additive_expression) => {
let ir_add = additive_expression.to_ir(builder, symbol_table);
let t_var = IrVariable::new_vr(
builder.new_t_var().into(),
builder.current_block().id(),
additive_expression.type_info(),
);
let as_rc = Rc::new(RefCell::new(t_var));
let assign = IrAssign::new(as_rc.clone(), IrOperation::Add(ir_add));
builder
.current_block_mut()
.add_statement(IrStatement::Assign(assign));
Some(IrExpression::Variable(as_rc))
}
}
}

View File

@ -1,5 +1,11 @@
use crate::asm::asm_instruction::{
AsmInstruction, InvokePlatformStatic, InvokeStatic, LoadConstant, Operand, Push,
};
use crate::ast::assemble_context::AssembleContext;
use crate::ast::ast_to_ir_util::expression_to_ir_expression;
use crate::ast::expression::Expression; use crate::ast::expression::Expression;
use crate::ast::ir_builder::IrBuilder; use crate::ast::ir_builder::IrBuilder;
use crate::constants_table::ConstantsTable;
use crate::diagnostic::Diagnostic; use crate::diagnostic::Diagnostic;
use crate::ir::ir_call::IrCall; use crate::ir::ir_call::IrCall;
use crate::ir::ir_expression::IrExpression; use crate::ir::ir_expression::IrExpression;
@ -138,7 +144,7 @@ impl Call {
let arguments: Vec<IrExpression> = self let arguments: Vec<IrExpression> = self
.arguments .arguments
.iter() .iter()
.map(|argument| argument.to_ir(builder, symbol_table)) .map(|argument| expression_to_ir_expression(argument, builder, symbol_table))
.inspect(|expression| { .inspect(|expression| {
if expression.is_none() { if expression.is_none() {
panic!("Attempt to pass non-expression") panic!("Attempt to pass non-expression")
@ -147,11 +153,88 @@ impl Call {
.map(Option::unwrap) .map(Option::unwrap)
.collect(); .collect();
let function_symbol = self.get_callee_symbol(); let function_symbol = self.get_callee_symbol();
IrCall::new( IrCall::new(function_symbol.borrow().name_owned(), arguments)
function_symbol.borrow().name_owned(), }
arguments,
function_symbol.clone(), pub fn assemble(
) &self,
context: &mut AssembleContext,
symbol_table: &SymbolTable,
constants_table: &mut ConstantsTable,
) {
// push all args
for argument in &self.arguments {
match argument {
Expression::Call(call) => {
call.assemble(context, symbol_table, constants_table); // will leave return val on stack
}
Expression::IntegerLiteral(integer_literal) => {
context.instruction(AsmInstruction::Push(Push::new(Operand::IntegerLiteral(
integer_literal.value(),
))));
}
Expression::String(string_literal) => {
let name = constants_table.insert_string(string_literal.content());
let temp_register = context.new_local_register();
context.instruction(AsmInstruction::LoadConstant(LoadConstant::new(
&name,
temp_register,
)));
context.instruction(AsmInstruction::Push(Push::new(Operand::Register(
temp_register,
))));
}
Expression::Identifier(identifier) => match identifier.expressible_symbol() {
ExpressibleSymbol::Function(_) => {
panic!("Pushing function symbols not supported.")
}
ExpressibleSymbol::Parameter(parameter_symbol) => {
context.instruction(AsmInstruction::Push(Push::new(
Operand::StackFrameOffset(
parameter_symbol.borrow().stack_frame_offset(),
),
)));
}
ExpressibleSymbol::Variable(variable_symbol) => {
context.instruction(AsmInstruction::Push(Push::new(Operand::Register(
variable_symbol.borrow().register(),
))))
}
},
Expression::Additive(additive_expression) => {
let result_register =
additive_expression.assemble(context, symbol_table, constants_table);
context.instruction(AsmInstruction::Push(Push::new(Operand::Register(
result_register,
))));
}
}
}
let function_symbol = match self.callee() {
Expression::Identifier(identifier) => {
let expressible_symbol = identifier.expressible_symbol();
match expressible_symbol {
ExpressibleSymbol::Function(function_symbol) => function_symbol.clone(),
_ => panic!("Calling things other than functions not yet supported."),
}
}
_ => panic!("Calling things other than identifiers not yet supported."),
};
let function_symbol = function_symbol.borrow();
if function_symbol.is_platform() {
let arg_count = function_symbol.parameters().len();
context.instruction(AsmInstruction::InvokePlatformStatic(
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,
)));
}
} }
pub fn source_range(&self) -> &SourceRange { pub fn source_range(&self) -> &SourceRange {

View File

@ -1,6 +1,8 @@
use crate::asm::asm_function::AsmFunction;
use crate::ast::assemble_context::AssembleContext;
use crate::ast::module_level_declaration::ModuleLevelDeclaration; use crate::ast::module_level_declaration::ModuleLevelDeclaration;
use crate::constants_table::ConstantsTable;
use crate::diagnostic::Diagnostic; use crate::diagnostic::Diagnostic;
use crate::ir::ir_function::IrFunction;
use crate::symbol_table::SymbolTable; use crate::symbol_table::SymbolTable;
pub struct CompilationUnit { pub struct CompilationUnit {
@ -42,13 +44,15 @@ impl CompilationUnit {
diagnostics diagnostics
} }
pub fn to_ir(&self, symbol_table: &SymbolTable) -> Vec<IrFunction> { pub fn assemble(
let mut ir_functions = vec![]; &self,
symbol_table: &SymbolTable,
constants_table: &mut ConstantsTable,
) -> Vec<AsmFunction> {
let mut context = AssembleContext::new();
for declaration in &self.declarations { for declaration in &self.declarations {
if let ModuleLevelDeclaration::Function(function) = declaration { declaration.assemble(&mut context, symbol_table, constants_table);
ir_functions.push(function.to_ir(symbol_table));
}
} }
ir_functions context.take_functions()
} }
} }

View File

@ -2,19 +2,11 @@ use crate::ast::additive_expression::AdditiveExpression;
use crate::ast::call::Call; use crate::ast::call::Call;
use crate::ast::identifier::Identifier; use crate::ast::identifier::Identifier;
use crate::ast::integer_literal::IntegerLiteral; use crate::ast::integer_literal::IntegerLiteral;
use crate::ast::ir_builder::IrBuilder;
use crate::ast::string_literal::StringLiteral; use crate::ast::string_literal::StringLiteral;
use crate::diagnostic::Diagnostic; use crate::diagnostic::Diagnostic;
use crate::ir::ir_assign::IrAssign;
use crate::ir::ir_expression::IrExpression;
use crate::ir::ir_operation::IrOperation;
use crate::ir::ir_statement::IrStatement;
use crate::ir::ir_variable::IrVariable;
use crate::source_range::SourceRange; use crate::source_range::SourceRange;
use crate::symbol_table::SymbolTable; use crate::symbol_table::SymbolTable;
use crate::type_info::TypeInfo; use crate::type_info::TypeInfo;
use std::cell::RefCell;
use std::rc::Rc;
pub enum Expression { pub enum Expression {
Call(Call), Call(Call),
@ -91,58 +83,4 @@ impl Expression {
Expression::Additive(additive_expression) => additive_expression.source_range(), Expression::Additive(additive_expression) => additive_expression.source_range(),
} }
} }
pub fn to_ir(
&self,
builder: &mut IrBuilder,
symbol_table: &SymbolTable,
) -> Option<IrExpression> {
match self {
Expression::Call(call) => {
let ir_call = call.to_ir(builder, symbol_table);
if matches!(call.type_info(), TypeInfo::Void) {
builder
.current_block_mut()
.add_statement(IrStatement::Call(ir_call));
None
} else {
let t_var = IrVariable::new_vr(
builder.new_t_var().into(),
builder.current_block().id(),
call.type_info(),
);
let as_rc = Rc::new(RefCell::new(t_var));
let assign = IrAssign::new(as_rc.clone(), IrOperation::Call(ir_call));
builder
.current_block_mut()
.add_statement(IrStatement::Assign(assign));
Some(IrExpression::Variable(as_rc))
}
}
Expression::IntegerLiteral(integer_literal) => {
Some(IrExpression::Int(integer_literal.value()))
}
Expression::String(string_literal) => {
Some(IrExpression::String(string_literal.content().into()))
}
Expression::Identifier(identifier) => {
let expressible_symbol = identifier.expressible_symbol();
Some(expressible_symbol.ir_expression())
}
Expression::Additive(additive_expression) => {
let ir_add = additive_expression.to_ir(builder, symbol_table);
let t_var = IrVariable::new_vr(
builder.new_t_var().into(),
builder.current_block().id(),
additive_expression.type_info(),
);
let as_rc = Rc::new(RefCell::new(t_var));
let assign = IrAssign::new(as_rc.clone(), IrOperation::Add(ir_add));
builder
.current_block_mut()
.add_statement(IrStatement::Assign(assign));
Some(IrExpression::Variable(as_rc))
}
}
}
} }

View File

@ -1,9 +1,17 @@
use crate::asm::asm_instruction::{AsmInstruction, Pop, SetReturnValue};
use crate::ast::assemble_context::AssembleContext;
use crate::ast::ast_to_ir_util::expression_to_ir_expression;
use crate::ast::expression::Expression; use crate::ast::expression::Expression;
use crate::ast::ir_builder::IrBuilder; use crate::ast::ir_builder::IrBuilder;
use crate::constants_table::ConstantsTable;
use crate::diagnostic::Diagnostic; use crate::diagnostic::Diagnostic;
use crate::ir::ir_return::IrReturn; use crate::ir::ir_return::IrReturn;
use crate::ir::ir_statement::IrStatement; use crate::ir::ir_statement::IrStatement;
use crate::symbol::FunctionSymbol;
use crate::symbol_table::SymbolTable; use crate::symbol_table::SymbolTable;
use crate::type_info::TypeInfo;
use std::cell::RefCell;
use std::rc::Rc;
pub struct ExpressionStatement { pub struct ExpressionStatement {
expression: Box<Expression>, expression: Box<Expression>,
@ -32,17 +40,51 @@ impl ExpressionStatement {
self.expression.type_check(symbol_table) self.expression.type_check(symbol_table)
} }
pub fn to_ir( pub fn to_ir(&self, builder: &mut IrBuilder, symbol_table: &SymbolTable, is_last: bool) {
&self, let ir_expression = expression_to_ir_expression(self.expression(), builder, symbol_table);
builder: &mut IrBuilder, if ir_expression.is_some() && is_last {
symbol_table: &SymbolTable,
should_return_value: bool,
) {
let ir_expression = self.expression.to_ir(builder, symbol_table);
if ir_expression.is_some() && should_return_value {
builder builder
.current_block_mut() .current_block_mut()
.add_statement(IrStatement::Return(IrReturn::new(ir_expression))); .add_statement(IrStatement::Return(IrReturn::new(ir_expression)));
} }
} }
pub fn assemble(
&self,
context: &mut AssembleContext,
symbol_table: &SymbolTable,
constants_table: &mut ConstantsTable,
outer_function_symbol: &Rc<RefCell<FunctionSymbol>>,
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) => {
let result_register = additive.assemble(context, symbol_table, constants_table);
if is_last
&& !matches!(outer_function_symbol.borrow().return_type(), TypeInfo::Void)
{
// set return value
context.instruction(AsmInstruction::SetReturnValue(SetReturnValue::new(
result_register,
)));
}
}
_ => unreachable!(),
}
}
} }

View File

@ -1,7 +1,10 @@
use crate::asm::asm_instruction::{AsmInstruction, Return};
use crate::ast::assemble_context::AssembleContext;
use crate::ast::ir_builder::IrBuilder; use crate::ast::ir_builder::IrBuilder;
use crate::ast::parameter::Parameter; use crate::ast::parameter::Parameter;
use crate::ast::statement::Statement; use crate::ast::statement::Statement;
use crate::ast::type_use::TypeUse; use crate::ast::type_use::TypeUse;
use crate::constants_table::ConstantsTable;
use crate::diagnostic::Diagnostic; use crate::diagnostic::Diagnostic;
use crate::ir::ir_function::IrFunction; use crate::ir::ir_function::IrFunction;
use crate::ir::ir_parameter::IrParameter; use crate::ir::ir_parameter::IrParameter;
@ -87,9 +90,14 @@ impl Function {
// handle parameters // handle parameters
symbol_table.push_scope(&format!("parameter_scope({})", self.declared_name)); symbol_table.push_scope(&format!("parameter_scope({})", self.declared_name));
let mut parameter_symbols = vec![]; let mut parameter_symbols = vec![];
for parameter in &mut self.parameters { let parameter_count = self.parameters.len();
let stack_frame_offset_base = (parameter_count as isize).neg();
for (i, parameter) in self.parameters.iter_mut().enumerate() {
match parameter.gather_declared_names(symbol_table) { match parameter.gather_declared_names(symbol_table) {
Ok(parameter_symbol) => { Ok(parameter_symbol) => {
parameter_symbol
.borrow_mut()
.set_stack_frame_offset(stack_frame_offset_base + (i as isize));
parameter_symbols.push(parameter_symbol); parameter_symbols.push(parameter_symbol);
} }
Err(mut parameter_diagnostics) => { Err(mut parameter_diagnostics) => {
@ -157,13 +165,11 @@ impl Function {
let mut builder = IrBuilder::new(); let mut builder = IrBuilder::new();
// parameters // parameters
for (i, parameter) in self.parameters.iter().enumerate() { for parameter in &self.parameters {
let parameter_symbol = parameter.parameter_symbol(); let parameter_symbol = parameter.parameter_symbol();
let stack_offset = (self.parameters.len() as isize).neg() + (i as isize);
let ir_parameter = IrParameter::new( let ir_parameter = IrParameter::new(
parameter_symbol.borrow().name(), parameter_symbol.borrow().name(),
parameter_symbol.borrow().type_info().clone(), parameter_symbol.borrow().type_info().clone(),
stack_offset,
); );
let as_rc = Rc::new(ir_parameter); let as_rc = Rc::new(ir_parameter);
builder.parameters_mut().push(as_rc.clone()); builder.parameters_mut().push(as_rc.clone());
@ -171,28 +177,51 @@ impl Function {
} }
let entry_block_id = builder.new_block(); let entry_block_id = builder.new_block();
let return_type_info = self
.function_symbol
.as_ref()
.unwrap()
.borrow()
.return_type();
let should_return_value = !matches!(return_type_info, TypeInfo::Void);
for (i, statement) in self.statements.iter().enumerate() { for (i, statement) in self.statements.iter().enumerate() {
let is_last = i == self.statements.len() - 1; let is_last = i == self.statements.len() - 1;
statement.to_ir(&mut builder, symbol_table, should_return_value && is_last); statement.to_ir(&mut builder, symbol_table, is_last);
} }
builder.finish_block(); builder.finish_block();
let return_type_info = self
.return_type
.as_ref()
.map(|return_type| return_type.to_type_info())
.unwrap_or(TypeInfo::Void);
let entry_block = builder.get_block(entry_block_id).clone(); let entry_block = builder.get_block(entry_block_id).clone();
IrFunction::new( IrFunction::new(
self.function_symbol.as_ref().unwrap().clone(), Rc::from(self.declared_name()),
builder.parameters(), builder.parameters(),
return_type_info, return_type_info,
entry_block, entry_block,
) )
} }
pub fn assemble(
&self,
context: &mut AssembleContext,
symbol_table: &SymbolTable,
constants_table: &mut ConstantsTable,
) {
context.new_function(&self.declared_name, &self.declared_name_source_range);
context.new_block(&format!("{}_enter", self.declared_name));
let function_symbol = self
.function_symbol
.as_ref()
.expect("function_symbol not initialized; did you type check yet?");
for (i, statement) in self.statements.iter().enumerate() {
let is_last = i == self.statements.len() - 1;
statement.assemble(
context,
symbol_table,
constants_table,
function_symbol,
is_last,
);
}
// return
context.instruction(AsmInstruction::Return(Return::new(self.parameters.len())));
context.complete_function();
}
} }

View File

@ -1,5 +1,8 @@
use crate::asm::asm_instruction::{AsmInstruction, LoadConstant, Move, Operand, Pop};
use crate::ast::assemble_context::AssembleContext;
use crate::ast::expression::Expression; use crate::ast::expression::Expression;
use crate::ast::ir_builder::IrBuilder; use crate::ast::ir_builder::IrBuilder;
use crate::constants_table::ConstantsTable;
use crate::diagnostic::Diagnostic; use crate::diagnostic::Diagnostic;
use crate::ir::ir_assign::IrAssign; use crate::ir::ir_assign::IrAssign;
use crate::ir::ir_expression::IrExpression; use crate::ir::ir_expression::IrExpression;
@ -7,7 +10,7 @@ use crate::ir::ir_operation::IrOperation;
use crate::ir::ir_statement::IrStatement; use crate::ir::ir_statement::IrStatement;
use crate::ir::ir_variable::IrVariable; use crate::ir::ir_variable::IrVariable;
use crate::source_range::SourceRange; use crate::source_range::SourceRange;
use crate::symbol::VariableSymbol; use crate::symbol::{ExpressibleSymbol, VariableSymbol};
use crate::symbol_table::{SymbolInsertError, SymbolTable}; use crate::symbol_table::{SymbolInsertError, SymbolTable};
use std::cell::RefCell; use std::cell::RefCell;
use std::rc::Rc; use std::rc::Rc;
@ -118,4 +121,69 @@ impl LetStatement {
.current_block_mut() .current_block_mut()
.add_statement(IrStatement::Assign(ir_assign)); .add_statement(IrStatement::Assign(ir_assign));
} }
pub fn assemble(
&self,
context: &mut AssembleContext,
symbol_table: &SymbolTable,
constants_table: &mut ConstantsTable,
) {
let destination_register = context.new_local_register();
// save register to symbol
let variable_symbol =
symbol_table.get_variable_symbol(self.scope_id.unwrap(), self.declared_name());
variable_symbol
.borrow_mut()
.set_register(destination_register);
match self.initializer() {
Expression::Call(call) => {
call.assemble(context, symbol_table, constants_table);
context.instruction(AsmInstruction::Pop(Pop::new(destination_register)));
}
Expression::IntegerLiteral(integer_literal) => {
context.instruction(AsmInstruction::Move(Move::new(
Operand::IntegerLiteral(integer_literal.value()),
destination_register,
)));
}
Expression::String(string_literal) => {
let name = constants_table.insert_string(string_literal.content());
context.instruction(AsmInstruction::LoadConstant(LoadConstant::new(
&name,
destination_register,
)));
}
Expression::Identifier(identifier) => {
let expressible_symbol = identifier.expressible_symbol();
match expressible_symbol {
ExpressibleSymbol::Function(_) => {
panic!("Moving functions to registers not yet supported");
}
ExpressibleSymbol::Parameter(parameter_symbol) => {
context.instruction(AsmInstruction::Move(Move::new(
Operand::StackFrameOffset(
parameter_symbol.borrow().stack_frame_offset(),
),
destination_register,
)));
}
ExpressibleSymbol::Variable(variable_symbol) => {
context.instruction(AsmInstruction::Move(Move::new(
Operand::Register(variable_symbol.borrow().register()),
destination_register,
)));
}
}
}
Expression::Additive(additive) => {
let result_register = additive.assemble(context, symbol_table, constants_table);
context.instruction(AsmInstruction::Move(Move::new(
Operand::Register(result_register),
destination_register,
)));
}
}
}
} }

View File

@ -1,4 +1,6 @@
pub mod additive_expression; pub mod additive_expression;
pub mod assemble_context;
mod ast_to_ir_util;
pub mod call; pub mod call;
pub mod compilation_unit; pub mod compilation_unit;
pub mod expression; pub mod expression;
@ -8,7 +10,7 @@ pub mod fqn;
pub mod function; pub mod function;
pub mod identifier; pub mod identifier;
pub mod integer_literal; pub mod integer_literal;
pub mod ir_builder; mod ir_builder;
pub mod let_statement; pub mod let_statement;
pub mod module_level_declaration; pub mod module_level_declaration;
pub mod parameter; pub mod parameter;

View File

@ -1,5 +1,7 @@
use crate::ast::assemble_context::AssembleContext;
use crate::ast::extern_function::ExternFunction; use crate::ast::extern_function::ExternFunction;
use crate::ast::function::Function; use crate::ast::function::Function;
use crate::constants_table::ConstantsTable;
use crate::diagnostic::Diagnostic; use crate::diagnostic::Diagnostic;
use crate::symbol_table::SymbolTable; use crate::symbol_table::SymbolTable;
@ -37,4 +39,20 @@ impl ModuleLevelDeclaration {
} }
} }
} }
pub fn assemble(
&self,
context: &mut AssembleContext,
symbol_table: &SymbolTable,
constants_table: &mut ConstantsTable,
) {
match self {
ModuleLevelDeclaration::Function(function) => {
function.assemble(context, symbol_table, constants_table)
}
ModuleLevelDeclaration::ExternFunction(_) => {
// no-op
}
}
}
} }

View File

@ -1,8 +1,13 @@
use crate::ast::assemble_context::AssembleContext;
use crate::ast::expression_statement::ExpressionStatement; use crate::ast::expression_statement::ExpressionStatement;
use crate::ast::ir_builder::IrBuilder; use crate::ast::ir_builder::IrBuilder;
use crate::ast::let_statement::LetStatement; use crate::ast::let_statement::LetStatement;
use crate::constants_table::ConstantsTable;
use crate::diagnostic::Diagnostic; use crate::diagnostic::Diagnostic;
use crate::symbol::FunctionSymbol;
use crate::symbol_table::SymbolTable; use crate::symbol_table::SymbolTable;
use std::cell::RefCell;
use std::rc::Rc;
pub enum Statement { pub enum Statement {
Let(LetStatement), Let(LetStatement),
@ -37,18 +42,37 @@ impl Statement {
} }
} }
pub fn to_ir( pub fn to_ir(&self, builder: &mut IrBuilder, symbol_table: &SymbolTable, is_last: bool) {
&self,
builder: &mut IrBuilder,
symbol_table: &SymbolTable,
should_return_value: bool,
) {
match self { match self {
Statement::Let(let_statement) => { Statement::Let(let_statement) => {
let_statement.to_ir(builder, symbol_table); let_statement.to_ir(builder, symbol_table);
} }
Statement::Expression(expression_statement) => { Statement::Expression(expression_statement) => {
expression_statement.to_ir(builder, symbol_table, should_return_value); expression_statement.to_ir(builder, symbol_table, is_last);
}
}
}
pub fn assemble(
&self,
context: &mut AssembleContext,
symbol_table: &SymbolTable,
constants_table: &mut ConstantsTable,
outer_function_symbol: &Rc<RefCell<FunctionSymbol>>,
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,
outer_function_symbol,
is_last,
);
} }
} }
} }

View File

@ -1,9 +1,8 @@
use std::collections::HashMap; use std::collections::HashMap;
use std::rc::Rc;
pub struct ConstantsTable { pub struct ConstantsTable {
string_counter: usize, string_counter: usize,
strings_to_names: HashMap<Rc<str>, Rc<str>>, strings_to_names: HashMap<String, String>,
} }
impl ConstantsTable { impl ConstantsTable {
@ -14,18 +13,14 @@ impl ConstantsTable {
} }
} }
pub fn get_or_insert(&mut self, s: &str) -> Rc<str> { pub fn insert_string(&mut self, s: &str) -> String {
if self.strings_to_names.contains_key(s) { let name = format!("s_{}", self.string_counter);
self.strings_to_names.get(s).unwrap().clone() self.string_counter += 1;
} else { self.strings_to_names.insert(s.into(), name.clone());
let name: Rc<str> = Rc::from(format!("s_{}", self.string_counter).as_str()); name
self.string_counter += 1;
self.strings_to_names.insert(s.into(), name.clone());
name
}
} }
pub fn string_constants(&self) -> HashMap<Rc<str>, Rc<str>> { pub fn string_constants(&self) -> HashMap<String, String> {
let mut constants = HashMap::new(); let mut constants = HashMap::new();
self.strings_to_names.iter().for_each(|(content, name)| { self.strings_to_names.iter().for_each(|(content, name)| {
constants.insert(name.clone(), content.clone()); constants.insert(name.clone(), content.clone());

View File

@ -1,26 +0,0 @@
use crate::constants_table::ConstantsTable;
use dvm_lib::instruction::Instruction;
pub trait Assemble {
fn assemble(&self, builder: &mut InstructionsBuilder, constants_table: &mut ConstantsTable);
}
pub struct InstructionsBuilder {
instructions: Vec<Instruction>,
}
impl InstructionsBuilder {
pub fn new() -> Self {
Self {
instructions: vec![],
}
}
pub fn push(&mut self, instruction: Instruction) {
self.instructions.push(instruction);
}
pub fn take_instructions(&mut self) -> Vec<Instruction> {
std::mem::take(&mut self.instructions)
}
}

View File

@ -1,8 +1,6 @@
use crate::constants_table::ConstantsTable;
use crate::ir::ir_expression::IrExpression; use crate::ir::ir_expression::IrExpression;
use crate::ir::ir_variable::IrVrVariableDescriptor; use crate::ir::ir_variable::IrVrVariableDescriptor;
use crate::ir::register_allocation::{OffsetCounter, VrUser}; use crate::ir::register_allocation::VrUser;
use dvm_lib::instruction::AddOperand;
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
use std::fmt::{Display, Formatter}; use std::fmt::{Display, Formatter};
@ -18,21 +16,6 @@ impl IrAdd {
right: right.into(), right: right.into(),
} }
} }
pub fn left(&self) -> &IrExpression {
&self.left
}
pub fn right(&self) -> &IrExpression {
&self.right
}
pub fn operand_pair(&self, constants_table: &mut ConstantsTable) -> (AddOperand, AddOperand) {
(
self.left.add_operand(constants_table),
self.right.add_operand(constants_table),
)
}
} }
impl Display for IrAdd { impl Display for IrAdd {
@ -68,8 +51,4 @@ impl VrUser for IrAdd {
self.left.propagate_register_assignments(assignments); self.left.propagate_register_assignments(assignments);
self.right.propagate_register_assignments(assignments); self.right.propagate_register_assignments(assignments);
} }
fn propagate_stack_offsets(&mut self, _counter: &mut OffsetCounter) {
// no-op, no definitions here
}
} }

View File

@ -1,9 +1,6 @@
use crate::constants_table::ConstantsTable;
use crate::ir::assemble::{Assemble, InstructionsBuilder};
use crate::ir::ir_operation::IrOperation; use crate::ir::ir_operation::IrOperation;
use crate::ir::ir_variable::{IrVariable, IrVariableDescriptor, IrVrVariableDescriptor}; use crate::ir::ir_variable::{IrVariable, IrVariableDescriptor, IrVrVariableDescriptor};
use crate::ir::register_allocation::{OffsetCounter, VrUser}; use crate::ir::register_allocation::VrUser;
use dvm_lib::instruction::{Instruction, Location};
use std::cell::RefCell; use std::cell::RefCell;
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
use std::fmt::{Display, Formatter}; use std::fmt::{Display, Formatter};
@ -68,41 +65,6 @@ impl VrUser for IrAssign {
} }
self.initializer.propagate_register_assignments(assignments); self.initializer.propagate_register_assignments(assignments);
} }
fn propagate_stack_offsets(&mut self, counter: &mut OffsetCounter) {
let mut borrowed_destination = self.destination.borrow_mut();
if let IrVariableDescriptor::Stack(stack_variable) = borrowed_destination.descriptor_mut() {
stack_variable.set_offset(counter.next());
}
}
}
impl Assemble for IrAssign {
fn assemble(&self, builder: &mut InstructionsBuilder, constants_table: &mut ConstantsTable) {
let destination = match self.destination.borrow().descriptor() {
IrVariableDescriptor::VirtualRegister(vr_variable) => {
Location::Register(vr_variable.assigned_register())
}
IrVariableDescriptor::Stack(stack_variable) => {
Location::StackFrameOffset(stack_variable.offset())
}
};
match self.initializer.as_ref() {
IrOperation::Load(ir_expression) => {
let move_operand = ir_expression.move_operand(constants_table);
builder.push(Instruction::Move(move_operand, destination));
}
IrOperation::Add(ir_add) => {
let (left, right) = ir_add.operand_pair(constants_table);
builder.push(Instruction::Add(left, right, destination));
}
IrOperation::Call(ir_call) => {
ir_call.assemble(builder, constants_table);
builder.push(Instruction::Pop(Some(destination)));
}
}
}
} }
impl Display for IrAssign { impl Display for IrAssign {

View File

@ -1,8 +1,6 @@
use crate::constants_table::ConstantsTable;
use crate::ir::assemble::{Assemble, InstructionsBuilder};
use crate::ir::ir_statement::IrStatement; use crate::ir::ir_statement::IrStatement;
use crate::ir::ir_variable::IrVrVariableDescriptor; use crate::ir::ir_variable::IrVrVariableDescriptor;
use crate::ir::register_allocation::{HasVrUsers, OffsetCounter, VrUser}; use crate::ir::register_allocation::{HasVrUsers, VrUser};
use std::cell::RefCell; use std::cell::RefCell;
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
use std::fmt::{Display, Formatter}; use std::fmt::{Display, Formatter};
@ -75,20 +73,6 @@ impl VrUser for IrBlock {
statement.propagate_register_assignments(assignments); statement.propagate_register_assignments(assignments);
} }
} }
fn propagate_stack_offsets(&mut self, counter: &mut OffsetCounter) {
for statement in &mut self.statements {
statement.propagate_stack_offsets(counter);
}
}
}
impl Assemble for IrBlock {
fn assemble(&self, builder: &mut InstructionsBuilder, constants_table: &mut ConstantsTable) {
for statement in &self.statements {
statement.assemble(builder, constants_table);
}
}
} }
impl Display for IrBlock { impl Display for IrBlock {
@ -151,7 +135,7 @@ mod tests {
if let ModuleLevelDeclaration::Function(main) = main { if let ModuleLevelDeclaration::Function(main) = main {
let mut main_ir = main.to_ir(&symbol_table); let mut main_ir = main.to_ir(&symbol_table);
let (register_assignments, _) = main_ir.assign_registers(2); let register_assignments = main_ir.assign_registers(2);
assert_eq!(register_assignments.len(), 4); assert_eq!(register_assignments.len(), 4);
} else { } else {
unreachable!() unreachable!()

View File

@ -1,11 +1,6 @@
use crate::constants_table::ConstantsTable;
use crate::ir::assemble::{Assemble, InstructionsBuilder};
use crate::ir::ir_expression::IrExpression; use crate::ir::ir_expression::IrExpression;
use crate::ir::ir_variable::IrVrVariableDescriptor; use crate::ir::ir_variable::IrVrVariableDescriptor;
use crate::ir::register_allocation::{OffsetCounter, VrUser}; use crate::ir::register_allocation::VrUser;
use crate::symbol::FunctionSymbol;
use dvm_lib::instruction::Instruction;
use std::cell::RefCell;
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
use std::fmt::{Display, Formatter}; use std::fmt::{Display, Formatter};
use std::rc::Rc; use std::rc::Rc;
@ -13,19 +8,13 @@ use std::rc::Rc;
pub struct IrCall { pub struct IrCall {
function_name: Rc<str>, function_name: Rc<str>,
arguments: Vec<IrExpression>, arguments: Vec<IrExpression>,
function_symbol: Rc<RefCell<FunctionSymbol>>,
} }
impl IrCall { impl IrCall {
pub fn new( pub fn new(function_name: Rc<str>, arguments: Vec<IrExpression>) -> Self {
function_name: Rc<str>,
arguments: Vec<IrExpression>,
function_symbol: Rc<RefCell<FunctionSymbol>>,
) -> Self {
Self { Self {
function_name, function_name,
arguments, arguments,
function_symbol,
} }
} }
} }
@ -53,32 +42,6 @@ impl VrUser for IrCall {
argument.propagate_register_assignments(assignments); argument.propagate_register_assignments(assignments);
} }
} }
fn propagate_stack_offsets(&mut self, _counter: &mut OffsetCounter) {
// no-op, because no definitions here
}
}
impl Assemble for IrCall {
fn assemble(&self, builder: &mut InstructionsBuilder, constants_table: &mut ConstantsTable) {
// push all args
self.arguments
.iter()
.map(|ir_expression| ir_expression.push_operand(constants_table))
.for_each(|push_operand| builder.push(Instruction::Push(push_operand)));
let symbol = self.function_symbol.borrow();
if symbol.is_platform() {
builder.push(Instruction::InvokePlatformStatic(
symbol.name_owned(),
symbol.parameters().len(),
));
} else {
builder.push(Instruction::InvokeStatic(
symbol.name_owned(),
symbol.parameters().len(),
));
}
}
} }
impl Display for IrCall { impl Display for IrCall {

View File

@ -1,11 +1,8 @@
use crate::constants_table::ConstantsTable;
use crate::ir::ir_parameter::IrParameter; use crate::ir::ir_parameter::IrParameter;
use crate::ir::ir_variable::{ use crate::ir::ir_variable::{
IrStackVariableDescriptor, IrVariable, IrVariableDescriptor, IrVrVariableDescriptor, IrStackVariableDescriptor, IrVariable, IrVariableDescriptor, IrVrVariableDescriptor,
}; };
use crate::ir::register_allocation::{OffsetCounter, VrUser}; use crate::ir::register_allocation::VrUser;
use crate::type_info::TypeInfo;
use dvm_lib::instruction::{AddOperand, Location, MoveOperand, PushOperand, ReturnOperand};
use std::cell::RefCell; use std::cell::RefCell;
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
use std::fmt::{Display, Formatter}; use std::fmt::{Display, Formatter};
@ -18,117 +15,6 @@ pub enum IrExpression {
String(Rc<str>), String(Rc<str>),
} }
impl IrExpression {
pub fn type_info(&self) -> TypeInfo {
match self {
IrExpression::Parameter(ir_parameter) => ir_parameter.type_info().clone(),
IrExpression::Variable(ir_variable) => ir_variable.borrow().type_info().clone(),
IrExpression::Int(_) => TypeInfo::Integer,
IrExpression::String(_) => TypeInfo::String,
}
}
pub fn move_operand(&self, constants_table: &mut ConstantsTable) -> MoveOperand {
match self {
IrExpression::Parameter(ir_parameter) => {
MoveOperand::Location(Location::StackFrameOffset(ir_parameter.stack_offset()))
}
IrExpression::Variable(ir_variable) => match ir_variable.borrow().descriptor() {
IrVariableDescriptor::VirtualRegister(register_variable) => {
MoveOperand::Location(Location::Register(register_variable.assigned_register()))
}
IrVariableDescriptor::Stack(stack_variable) => {
MoveOperand::Location(Location::StackFrameOffset(stack_variable.offset()))
}
},
IrExpression::Int(i) => MoveOperand::Int(*i),
IrExpression::String(s) => {
let constant_name = constants_table.get_or_insert(s);
MoveOperand::String(constant_name)
}
}
}
pub fn push_operand(&self, constants_table: &mut ConstantsTable) -> PushOperand {
match self {
IrExpression::Parameter(ir_parameter) => {
PushOperand::Location(Location::StackFrameOffset(ir_parameter.stack_offset()))
}
IrExpression::Variable(ir_variable) => match ir_variable.borrow().descriptor() {
IrVariableDescriptor::VirtualRegister(register_variable) => {
PushOperand::Location(Location::Register(register_variable.assigned_register()))
}
IrVariableDescriptor::Stack(stack_variable) => {
PushOperand::Location(Location::StackFrameOffset(stack_variable.offset()))
}
},
IrExpression::Int(i) => PushOperand::Int(*i),
IrExpression::String(s) => {
let constant_name = constants_table.get_or_insert(s);
PushOperand::String(constant_name)
}
}
}
pub fn add_operand(&self, constants_table: &mut ConstantsTable) -> AddOperand {
match self {
IrExpression::Parameter(ir_parameter) => match ir_parameter.type_info() {
TypeInfo::Integer | TypeInfo::String => {
AddOperand::Location(Location::StackFrameOffset(ir_parameter.stack_offset()))
}
_ => panic!(
"Attempt to add non-integer/non-string (found: {})",
ir_parameter.type_info()
),
},
IrExpression::Variable(ir_variable) => match ir_variable.borrow().type_info() {
TypeInfo::Integer | TypeInfo::String => match ir_variable.borrow().descriptor() {
IrVariableDescriptor::VirtualRegister(register_variable) => {
AddOperand::Location(Location::Register(
register_variable.assigned_register(),
))
}
IrVariableDescriptor::Stack(stack_variable) => {
AddOperand::Location(Location::StackFrameOffset(stack_variable.offset()))
}
},
_ => panic!(
"Attempt to add non-integer/non-string (found: {})",
ir_variable.borrow().type_info()
),
},
IrExpression::Int(i) => AddOperand::Int(*i),
IrExpression::String(s) => {
let constant_name = constants_table.get_or_insert(s);
AddOperand::String(constant_name)
}
}
}
pub fn return_operand(&self, constants_table: &mut ConstantsTable) -> ReturnOperand {
match self {
IrExpression::Parameter(ir_parameter) => {
ReturnOperand::Location(Location::StackFrameOffset(ir_parameter.stack_offset()))
}
IrExpression::Variable(ir_variable) => match ir_variable.borrow().descriptor() {
IrVariableDescriptor::VirtualRegister(register_variable) => {
ReturnOperand::Location(Location::Register(
register_variable.assigned_register(),
))
}
IrVariableDescriptor::Stack(stack_variable) => {
ReturnOperand::Location(Location::StackFrameOffset(stack_variable.offset()))
}
},
IrExpression::Int(i) => ReturnOperand::Int(*i),
IrExpression::String(s) => {
let constant_name = constants_table.get_or_insert(s);
ReturnOperand::String(constant_name)
}
}
}
}
impl Display for IrExpression { impl Display for IrExpression {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self { match self {
@ -226,8 +112,4 @@ impl VrUser for IrExpression {
} }
} }
} }
fn propagate_stack_offsets(&mut self, _counter: &mut OffsetCounter) {
// no-op
}
} }

View File

@ -1,19 +1,15 @@
use crate::constants_table::ConstantsTable;
use crate::ir::assemble::{Assemble, InstructionsBuilder};
use crate::ir::ir_block::IrBlock; use crate::ir::ir_block::IrBlock;
use crate::ir::ir_parameter::IrParameter; use crate::ir::ir_parameter::IrParameter;
use crate::ir::ir_variable::IrVrVariableDescriptor; use crate::ir::ir_variable::IrVrVariableDescriptor;
use crate::ir::register_allocation::HasVrUsers; use crate::ir::register_allocation::{HasVrUsers, VrUser};
use crate::symbol::FunctionSymbol;
use crate::type_info::TypeInfo; use crate::type_info::TypeInfo;
use dvm_lib::vm::function::Function;
use std::cell::RefCell; use std::cell::RefCell;
use std::collections::HashMap; use std::collections::HashMap;
use std::fmt::Display; use std::fmt::Display;
use std::rc::Rc; use std::rc::Rc;
pub struct IrFunction { pub struct IrFunction {
function_symbol: Rc<RefCell<FunctionSymbol>>, name: Rc<str>,
parameters: Vec<Rc<IrParameter>>, parameters: Vec<Rc<IrParameter>>,
return_type_info: TypeInfo, return_type_info: TypeInfo,
entry: Rc<RefCell<IrBlock>>, entry: Rc<RefCell<IrBlock>>,
@ -21,42 +17,31 @@ pub struct IrFunction {
impl IrFunction { impl IrFunction {
pub fn new( pub fn new(
function_symbol: Rc<RefCell<FunctionSymbol>>, name: Rc<str>,
parameters: &[Rc<IrParameter>], parameters: &[Rc<IrParameter>],
return_type_info: TypeInfo, return_type_info: TypeInfo,
entry: Rc<RefCell<IrBlock>>, entry: Rc<RefCell<IrBlock>>,
) -> Self { ) -> Self {
Self { Self {
function_symbol, name,
parameters: parameters.to_vec(), parameters: parameters.to_vec(),
return_type_info, return_type_info,
entry, entry,
} }
} }
/// Returns the register assignments for debugging purposes
pub fn assign_registers( pub fn assign_registers(
&mut self, &mut self,
register_count: usize, register_count: usize,
) -> (HashMap<IrVrVariableDescriptor, usize>, isize) { ) -> HashMap<IrVrVariableDescriptor, usize> {
self.entry.borrow_mut().assign_registers(register_count) self.entry.borrow_mut().assign_registers(register_count)
} }
pub fn assemble(&self, stack_size: isize, constants_table: &mut ConstantsTable) -> Function {
let mut builder = InstructionsBuilder::new();
self.entry.borrow().assemble(&mut builder, constants_table);
let instructions = builder.take_instructions();
Function::new(
self.function_symbol.borrow().name_owned(),
self.parameters.len(),
stack_size,
instructions,
)
}
} }
impl Display for IrFunction { impl Display for IrFunction {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "fn {}(", self.function_symbol.borrow().name())?; write!(f, "fn {}(", self.name)?;
for (i, parameter) in self.parameters.iter().enumerate() { for (i, parameter) in self.parameters.iter().enumerate() {
write!(f, "{}: {}", parameter, parameter.type_info())?; write!(f, "{}: {}", parameter, parameter.type_info())?;
if i < self.parameters.len() - 1 { if i < self.parameters.len() - 1 {

View File

@ -2,7 +2,7 @@ use crate::ir::ir_add::IrAdd;
use crate::ir::ir_call::IrCall; use crate::ir::ir_call::IrCall;
use crate::ir::ir_expression::IrExpression; use crate::ir::ir_expression::IrExpression;
use crate::ir::ir_variable::IrVrVariableDescriptor; use crate::ir::ir_variable::IrVrVariableDescriptor;
use crate::ir::register_allocation::{OffsetCounter, VrUser}; use crate::ir::register_allocation::VrUser;
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
use std::fmt::{Display, Formatter}; use std::fmt::{Display, Formatter};
@ -75,8 +75,4 @@ impl VrUser for IrOperation {
} }
} }
} }
fn propagate_stack_offsets(&mut self, _counter: &mut OffsetCounter) {
// no-op
}
} }

View File

@ -5,25 +5,19 @@ use std::rc::Rc;
pub struct IrParameter { pub struct IrParameter {
name: Rc<str>, name: Rc<str>,
type_info: TypeInfo, type_info: TypeInfo,
stack_offset: isize,
} }
impl IrParameter { impl IrParameter {
pub fn new(name: &str, type_info: TypeInfo, stack_offset: isize) -> Self { pub fn new(name: &str, type_info: TypeInfo) -> Self {
Self { Self {
name: name.into(), name: name.into(),
type_info, type_info,
stack_offset,
} }
} }
pub fn type_info(&self) -> &TypeInfo { pub fn type_info(&self) -> &TypeInfo {
&self.type_info &self.type_info
} }
pub fn stack_offset(&self) -> isize {
self.stack_offset
}
} }
impl Display for IrParameter { impl Display for IrParameter {

View File

@ -1,9 +1,6 @@
use crate::constants_table::ConstantsTable;
use crate::ir::assemble::{Assemble, InstructionsBuilder};
use crate::ir::ir_expression::IrExpression; use crate::ir::ir_expression::IrExpression;
use crate::ir::ir_variable::IrVrVariableDescriptor; use crate::ir::ir_variable::IrVrVariableDescriptor;
use crate::ir::register_allocation::{OffsetCounter, VrUser}; use crate::ir::register_allocation::VrUser;
use dvm_lib::instruction::Instruction;
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
use std::fmt::{Display, Formatter}; use std::fmt::{Display, Formatter};
@ -44,21 +41,6 @@ impl VrUser for IrReturn {
ir_expression.propagate_register_assignments(assignments); ir_expression.propagate_register_assignments(assignments);
} }
} }
fn propagate_stack_offsets(&mut self, _counter: &mut OffsetCounter) {
// no-op
}
}
impl Assemble for IrReturn {
fn assemble(&self, builder: &mut InstructionsBuilder, constants_table: &mut ConstantsTable) {
if let Some(ir_expression) = self.value.as_ref() {
builder.push(Instruction::SetReturnValue(
ir_expression.return_operand(constants_table),
));
}
builder.push(Instruction::Return);
}
} }
impl Display for IrReturn { impl Display for IrReturn {

View File

@ -1,10 +1,8 @@
use crate::constants_table::ConstantsTable;
use crate::ir::assemble::{Assemble, InstructionsBuilder};
use crate::ir::ir_assign::IrAssign; use crate::ir::ir_assign::IrAssign;
use crate::ir::ir_call::IrCall; use crate::ir::ir_call::IrCall;
use crate::ir::ir_return::IrReturn; use crate::ir::ir_return::IrReturn;
use crate::ir::ir_variable::IrVrVariableDescriptor; use crate::ir::ir_variable::IrVrVariableDescriptor;
use crate::ir::register_allocation::{OffsetCounter, VrUser}; use crate::ir::register_allocation::VrUser;
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
use std::fmt::{Display, Formatter}; use std::fmt::{Display, Formatter};
@ -61,36 +59,6 @@ impl VrUser for IrStatement {
} }
} }
} }
fn propagate_stack_offsets(&mut self, counter: &mut OffsetCounter) {
match self {
IrStatement::Assign(ir_assign) => {
ir_assign.propagate_stack_offsets(counter);
}
IrStatement::Call(ir_call) => {
ir_call.propagate_stack_offsets(counter);
}
IrStatement::Return(ir_return) => {
ir_return.propagate_stack_offsets(counter);
}
}
}
}
impl Assemble for IrStatement {
fn assemble(&self, builder: &mut InstructionsBuilder, constants_table: &mut ConstantsTable) {
match self {
IrStatement::Assign(ir_assign) => {
ir_assign.assemble(builder, constants_table);
}
IrStatement::Call(ir_call) => {
ir_call.assemble(builder, constants_table);
}
IrStatement::Return(ir_return) => {
ir_return.assemble(builder, constants_table);
}
}
}
} }
impl Display for IrStatement { impl Display for IrStatement {

View File

@ -102,10 +102,6 @@ impl IrVrVariableDescriptor {
pub fn set_assigned_register(&mut self, register: usize) { pub fn set_assigned_register(&mut self, register: usize) {
self.assigned_register = Some(register); self.assigned_register = Some(register);
} }
pub fn assigned_register(&self) -> usize {
self.assigned_register.unwrap()
}
} }
impl Display for IrVrVariableDescriptor { impl Display for IrVrVariableDescriptor {
@ -123,7 +119,7 @@ impl Debug for IrVrVariableDescriptor {
pub struct IrStackVariableDescriptor { pub struct IrStackVariableDescriptor {
name: Rc<str>, name: Rc<str>,
block_id: usize, block_id: usize,
offset: Option<isize>, offset: Option<usize>,
} }
impl IrStackVariableDescriptor { impl IrStackVariableDescriptor {
@ -138,18 +134,10 @@ impl IrStackVariableDescriptor {
pub fn name(&self) -> &str { pub fn name(&self) -> &str {
&self.name &self.name
} }
pub fn set_offset(&mut self, offset: isize) {
self.offset = Some(offset);
}
pub fn offset(&self) -> isize {
self.offset.unwrap()
}
} }
impl Display for IrStackVariableDescriptor { impl Display for IrStackVariableDescriptor {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}_{}", self.name, self.block_id) write!(f, "{}_b{}", self.name, self.block_id)
} }
} }

View File

@ -1,4 +1,3 @@
mod assemble;
pub mod ir_add; pub mod ir_add;
pub mod ir_assign; pub mod ir_assign;
pub mod ir_block; pub mod ir_block;

View File

@ -104,7 +104,7 @@ pub trait HasVrUsers {
fn assign_registers( fn assign_registers(
&mut self, &mut self,
register_count: usize, register_count: usize,
) -> (HashMap<IrVrVariableDescriptor, usize>, isize) { ) -> HashMap<IrVrVariableDescriptor, usize> {
let mut spills: HashSet<IrVrVariableDescriptor> = HashSet::new(); let mut spills: HashSet<IrVrVariableDescriptor> = HashSet::new();
loop { loop {
let mut interference_graph = self.interference_graph(); let mut interference_graph = self.interference_graph();
@ -122,13 +122,7 @@ pub trait HasVrUsers {
for vr_user in self.vr_users_mut() { for vr_user in self.vr_users_mut() {
vr_user.propagate_register_assignments(&registers); vr_user.propagate_register_assignments(&registers);
} }
// also set offsets return registers;
let mut offset_counter = OffsetCounter::new();
for vr_user in self.vr_users_mut() {
vr_user.propagate_stack_offsets(&mut offset_counter);
}
return (registers, offset_counter.get_count());
} }
} }
} }
@ -142,27 +136,6 @@ pub trait VrUser {
&mut self, &mut self,
assignments: &HashMap<IrVrVariableDescriptor, usize>, assignments: &HashMap<IrVrVariableDescriptor, usize>,
); );
fn propagate_stack_offsets(&mut self, counter: &mut OffsetCounter);
}
pub struct OffsetCounter {
counter: isize,
}
impl OffsetCounter {
pub fn new() -> Self {
Self { counter: 0 }
}
pub fn next(&mut self) -> isize {
let offset = self.counter;
self.counter += 1;
offset
}
pub fn get_count(&self) -> isize {
self.counter
}
} }
#[derive(Debug)] #[derive(Debug)]

View File

@ -1,3 +1,4 @@
pub mod asm;
pub mod ast; pub mod ast;
pub mod constants_table; pub mod constants_table;
pub mod diagnostic; pub mod diagnostic;

View File

@ -51,6 +51,9 @@ pub struct ParameterSymbol {
name: Rc<str>, name: Rc<str>,
type_info: TypeInfo, type_info: TypeInfo,
ir_parameter: Option<Rc<IrParameter>>, ir_parameter: Option<Rc<IrParameter>>,
#[deprecated]
stack_frame_offset: Option<isize>,
} }
impl ParameterSymbol { impl ParameterSymbol {
@ -59,6 +62,7 @@ impl ParameterSymbol {
name: name.into(), name: name.into(),
type_info, type_info,
ir_parameter: None, ir_parameter: None,
stack_frame_offset: None,
} }
} }
@ -81,12 +85,25 @@ impl ParameterSymbol {
pub fn ir_parameter(&self) -> &Rc<IrParameter> { pub fn ir_parameter(&self) -> &Rc<IrParameter> {
self.ir_parameter.as_ref().unwrap() self.ir_parameter.as_ref().unwrap()
} }
#[deprecated]
pub fn set_stack_frame_offset(&mut self, offset: isize) {
self.stack_frame_offset = Some(offset);
}
#[deprecated]
pub fn stack_frame_offset(&self) -> isize {
self.stack_frame_offset.unwrap()
}
} }
pub struct VariableSymbol { pub struct VariableSymbol {
name: Rc<str>, name: Rc<str>,
type_info: Option<TypeInfo>, type_info: Option<TypeInfo>,
vr_variable: Option<Rc<RefCell<IrVariable>>>, vr_variable: Option<Rc<RefCell<IrVariable>>>,
#[deprecated]
register: Option<usize>,
} }
impl VariableSymbol { impl VariableSymbol {
@ -95,6 +112,7 @@ impl VariableSymbol {
name: name.into(), name: name.into(),
type_info: None, type_info: None,
vr_variable: None, vr_variable: None,
register: None,
} }
} }
@ -125,6 +143,14 @@ impl VariableSymbol {
.as_ref() .as_ref()
.expect("ir_variable not yet initialized") .expect("ir_variable not yet initialized")
} }
pub fn set_register(&mut self, register: usize) {
self.register = Some(register);
}
pub fn register(&self) -> usize {
self.register.unwrap()
}
} }
pub enum ExpressibleSymbol { pub enum ExpressibleSymbol {

View File

@ -1,35 +1,57 @@
use std::fmt::{Display, Formatter}; use std::fmt::Display;
use std::rc::Rc; use std::rc::Rc;
pub type Register = usize; pub type Register = usize;
pub type StackFrameOffset = isize;
pub type ConstantName = Rc<str>; pub type ConstantName = Rc<str>;
pub type FunctionName = Rc<str>; pub type FunctionName = Rc<str>;
pub type ArgCount = usize; pub type ArgCount = usize;
pub type CallerPopCount = usize; pub type CallerPopCount = usize;
pub enum Instruction { pub enum Instruction {
Move(MoveOperand, Location), MoveRegister(Register, Register),
Push(PushOperand), MoveInt(i32, Register),
MoveStackFrameOffset(isize, Register),
PushRegister(Register),
PushInt(i32),
PushStackFrameOffset(isize),
InvokeStatic(FunctionName, ArgCount), InvokeStatic(FunctionName, ArgCount),
InvokePlatformStatic(FunctionName, ArgCount), InvokePlatformStatic(FunctionName, ArgCount),
Add(AddOperand, AddOperand, Location), LoadStringConstant(ConstantName, Register),
Pop(Option<Location>), Pop(Option<Register>),
SetReturnValue(ReturnOperand), AddIntInt(Register, Register, Register),
Return, AddStringInt(Register, Register, Register),
AddStringString(Register, Register, Register),
SetReturnValue(Register),
Return(CallerPopCount),
} }
impl Display for Instruction { impl Display for Instruction {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self { match self {
Instruction::Move(source, destination) => { Instruction::MoveRegister(source, destination) => {
write!(f, "mov {}, {}", 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::Push(source) => write!(f, "push {}", source),
Instruction::InvokeStatic(name, arg_count) => { Instruction::InvokeStatic(name, arg_count) => {
write!(f, "invoke_static {} (arg_count: {})", name, arg_count) write!(f, "invoke_static {} (arg_count: {})", name, arg_count)
} }
@ -40,131 +62,30 @@ impl Display for Instruction {
name, arg_count name, arg_count
) )
} }
Instruction::LoadStringConstant(name, destination) => {
write!(f, "loadsc {}, r{}", name, destination)
}
Instruction::Pop(maybe_destination) => { Instruction::Pop(maybe_destination) => {
if let Some(destination) = maybe_destination { if let Some(destination) = maybe_destination {
write!(f, "pop {}", destination) write!(f, "pop r{}", destination)
} else { } else {
write!(f, "pop") write!(f, "pop")
} }
} }
Instruction::Add(left, right, destination) => { Instruction::AddIntInt(left, right, destination) => {
write!(f, "add {}, {}, {}", 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) => { Instruction::SetReturnValue(source) => {
write!(f, "srv {}", source) write!(f, "srv r{}", source)
} }
Instruction::Return => { Instruction::Return(caller_pop_count) => {
write!(f, "ret") write!(f, "return (caller_pop_count: {})", caller_pop_count)
}
}
}
}
pub enum Location {
Register(Register),
StackFrameOffset(StackFrameOffset),
}
impl Display for Location {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Location::Register(register) => {
write!(f, "r{}", register)
}
Location::StackFrameOffset(offset) => {
if offset.is_positive() || *offset == 0 {
write!(f, "fp+{}", offset)
} else {
write!(f, "fp{}", offset)
}
}
}
}
}
pub enum MoveOperand {
Location(Location),
Int(i32),
String(Rc<str>),
}
impl Display for MoveOperand {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
MoveOperand::Location(location) => {
write!(f, "{}", location)
}
MoveOperand::Int(i) => {
write!(f, "{}", i)
}
MoveOperand::String(s) => {
write!(f, "{}", s)
}
}
}
}
pub enum PushOperand {
Location(Location),
Int(i32),
String(Rc<str>),
}
impl Display for PushOperand {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
PushOperand::Location(location) => {
write!(f, "{}", location)
}
PushOperand::Int(i) => {
write!(f, "{}", i)
}
PushOperand::String(s) => {
write!(f, "{}", s)
}
}
}
}
pub enum AddOperand {
Location(Location),
Int(i32),
String(Rc<str>),
}
impl Display for AddOperand {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
AddOperand::Location(location) => {
write!(f, "{}", location)
}
AddOperand::Int(i) => {
write!(f, "{}", i)
}
AddOperand::String(s) => {
write!(f, "{}", s)
}
}
}
}
pub enum ReturnOperand {
Location(Location),
Int(i32),
String(Rc<str>),
}
impl Display for ReturnOperand {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
ReturnOperand::Location(location) => {
write!(f, "{}", location)
}
ReturnOperand::Int(i) => {
write!(f, "{}", i)
}
ReturnOperand::String(s) => {
write!(f, "{}", s)
} }
} }
} }

View File

@ -10,8 +10,11 @@ pub struct StringConstant {
} }
impl StringConstant { impl StringConstant {
pub fn new(name: Rc<str>, content: Rc<str>) -> Self { pub fn new(name: Rc<str>, content: &str) -> Self {
Self { name, content } Self {
name,
content: content.into(),
}
} }
pub fn name(&self) -> &str { pub fn name(&self) -> &str {

View File

@ -1,26 +1,18 @@
use crate::instruction::Instruction; use crate::instruction::Instruction;
use std::fmt::Display;
use std::rc::Rc; use std::rc::Rc;
pub struct Function { pub struct Function {
name: Rc<str>, name: Rc<str>,
parameter_count: usize,
stack_size: isize,
instructions: Vec<Instruction>, instructions: Vec<Instruction>,
register_count: usize,
} }
impl Function { impl Function {
pub fn new( pub fn new(name: &str, instructions: Vec<Instruction>, register_count: usize) -> Self {
name: Rc<str>,
parameter_count: usize,
stack_size: isize,
instructions: Vec<Instruction>,
) -> Self {
Self { Self {
name, name: name.into(),
parameter_count,
stack_size,
instructions, instructions,
register_count,
} }
} }
@ -32,25 +24,11 @@ impl Function {
self.name.clone() self.name.clone()
} }
pub fn parameter_count(&self) -> usize {
self.parameter_count
}
pub fn stack_size(&self) -> isize {
self.stack_size
}
pub fn instructions(&self) -> &Vec<Instruction> { pub fn instructions(&self) -> &Vec<Instruction> {
&self.instructions &self.instructions
} }
}
impl Display for Function { pub fn register_count(&self) -> usize {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { self.register_count
writeln!(f, "fn {}", self.name)?;
for instruction in &self.instructions {
writeln!(f, " {}", instruction)?;
}
Ok(())
} }
} }

View File

@ -1,6 +1,4 @@
use crate::instruction::{ use crate::instruction::Instruction;
AddOperand, ConstantName, Instruction, Location, MoveOperand, PushOperand, ReturnOperand,
};
use crate::platform_function::PlatformFunction; use crate::platform_function::PlatformFunction;
use crate::vm::constant::Constant; use crate::vm::constant::Constant;
use crate::vm::function::Function; use crate::vm::function::Function;
@ -43,17 +41,28 @@ impl DvmContext {
&mut self.platform_functions &mut self.platform_functions
} }
#[deprecated]
pub fn add_function(&mut self, function: Function) {
self.functions.insert(function.name_owned(), function);
}
pub fn constants(&self) -> &HashMap<Rc<str>, Constant> { pub fn constants(&self) -> &HashMap<Rc<str>, Constant> {
&self.constants &self.constants
} }
pub fn constants_mut(&mut self) -> &mut HashMap<Rc<str>, Constant> { #[deprecated]
&mut self.constants pub fn add_constant(&mut self, constant: Constant) {
match &constant {
Constant::String(string_constant) => {
self.constants
.insert(string_constant.name_owned(), constant);
}
}
} }
} }
pub struct CallFrame<'a> { pub struct CallFrame<'a> {
function: &'a Function, function_name: Rc<str>,
instructions: &'a Vec<Instruction>, instructions: &'a Vec<Instruction>,
ip: usize, ip: usize,
stack: Vec<Value>, stack: Vec<Value>,
@ -62,10 +71,10 @@ pub struct CallFrame<'a> {
} }
impl<'a> CallFrame<'a> { impl<'a> CallFrame<'a> {
fn new(function: &'a Function) -> Self { fn new(function_name: Rc<str>, instructions: &'a Vec<Instruction>) -> Self {
Self { Self {
function, function_name,
instructions: function.instructions(), instructions,
ip: 0, ip: 0,
stack: vec![], stack: vec![],
fp: 0, fp: 0,
@ -73,8 +82,8 @@ impl<'a> CallFrame<'a> {
} }
} }
fn function(&self) -> &'a Function { fn function_name(&self) -> &str {
self.function &self.function_name
} }
fn instructions(&self) -> &'a [Instruction] { fn instructions(&self) -> &'a [Instruction] {
@ -164,55 +173,6 @@ impl<'a> CallStack<'a> {
} }
} }
fn load_value<'a>(
registers: &'a [Value],
stack: &'a [Value],
fp: usize,
location: &Location,
) -> &'a Value {
match location {
Location::Register(register) => &registers[*register],
Location::StackFrameOffset(offset) => {
let value_index = fp.checked_add_signed(*offset).expect(&format!(
"Overflow when adding offset {} to fp {}",
*offset, fp
));
&stack[value_index]
}
}
}
fn load_constant_value(
constants: &HashMap<Rc<str>, Constant>,
constant_name: &ConstantName,
) -> Value {
let constant = &constants[constant_name];
match constant {
Constant::String(string_constant) => Value::String(string_constant.content_owned()),
}
}
fn put_value(
registers: &mut Vec<Value>,
stack: &mut Vec<Value>,
fp: usize,
destination: &Location,
value: Value,
) {
match destination {
Location::Register(register) => {
registers[*register] = value;
}
Location::StackFrameOffset(offset) => {
let target_index = fp.checked_add_signed(*offset).expect(&format!(
"Failed to calculate target index from fp + offset ({} + {})",
fp, offset
));
stack[target_index] = value;
}
}
}
pub fn call<'a>( pub fn call<'a>(
context: &'a DvmContext, context: &'a DvmContext,
registers: &mut Vec<Value>, registers: &mut Vec<Value>,
@ -230,23 +190,20 @@ pub fn call<'a>(
.get(function_name) .get(function_name)
.expect(&format!("Function {} not found", function_name)); .expect(&format!("Function {} not found", function_name));
// ensure enough registers
registers.resize_with(function.register_count(), Default::default);
// push call frame // push call frame
call_stack.push(CallFrame::new(function)); call_stack.push(CallFrame::new(
function.name_owned(),
function.instructions(),
));
// put each arg on the stack // put each arg on the stack
for argument in &arguments { for argument in arguments {
call_stack.top_mut().stack_mut().push(argument.clone()); call_stack.top_mut().stack_mut().push(argument);
} }
// ensure enough stack space
call_stack.top_mut().stack_mut().resize(
arguments.len() + (function.stack_size() as usize),
Value::Null,
);
// container for final return value
let mut return_value: Option<Value> = None;
while call_stack.maybe_top().is_some() while call_stack.maybe_top().is_some()
&& call_stack.top().ip() < call_stack.top().instructions().len() && call_stack.top().ip() < call_stack.top().instructions().len()
{ {
@ -257,48 +214,52 @@ pub fn call<'a>(
match instruction { match instruction {
/* Move instructions */ /* Move instructions */
Instruction::Move(source, destination) => { Instruction::MoveRegister(source, destination) => {
// move a value to a destination // copy value from one register to another register
// could be a copy or an immediate let value = registers[*source].clone();
let value = match source { registers[*destination] = value;
MoveOperand::Location(location) => load_value( call_stack.top_mut().increment_ip();
registers, }
call_stack.top().stack(), Instruction::MoveInt(value, destination) => {
call_stack.top().fp(), registers[*destination] = Value::Int(*value);
location, call_stack.top_mut().increment_ip();
) }
.clone(), Instruction::MoveStackFrameOffset(offset, destination) => {
MoveOperand::Int(i) => Value::Int(*i), // copy a value offset from the current frame pointer (fp) to a register
MoveOperand::String(constant_name) => { let value_index =
load_constant_value(context.constants(), constant_name) call_stack
} .top()
}; .fp()
let fp = call_stack.top().fp(); .checked_add_signed(*offset)
put_value( .expect(&format!(
registers, "Overflow when adding offset {} to fp {}",
call_stack.top_mut().stack_mut(), *offset,
fp, call_stack.top().fp()
destination, ));
value, let value = call_stack.top().stack()[value_index].clone();
); registers[*destination] = value;
call_stack.top_mut().increment_ip(); call_stack.top_mut().increment_ip();
} }
/* Push instructions */ /* Push instructions */
Instruction::Push(operand) => { Instruction::PushRegister(source) => {
let value = match operand { // copy a value from a register to the top of the stack
PushOperand::Location(location) => load_value( let value = registers[*source].clone();
registers, call_stack.top_mut().stack_mut().push(value);
call_stack.top().stack(), call_stack.top_mut().increment_ip();
call_stack.top().fp(), }
location, Instruction::PushInt(value) => {
) call_stack.top_mut().stack_mut().push(Value::Int(*value));
.clone(), call_stack.top_mut().increment_ip();
PushOperand::Int(i) => Value::Int(*i), }
PushOperand::String(constant_name) => { Instruction::PushStackFrameOffset(offset) => {
load_constant_value(context.constants(), constant_name) // copy a value from somewhere on the stack to the top of the stack
} let value_index = call_stack
}; .top()
.fp()
.checked_add_signed(*offset)
.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().stack_mut().push(value);
call_stack.top_mut().increment_ip(); call_stack.top_mut().increment_ip();
} }
@ -318,17 +279,14 @@ pub fn call<'a>(
call_stack.top_mut().increment_ip(); call_stack.top_mut().increment_ip();
// push a new call frame // push a new call frame
call_stack.push(CallFrame::new(function)); call_stack.push(CallFrame::new(
function.name_owned(),
function.instructions(),
));
// push args // push args
call_stack.top_mut().stack_mut().append(&mut args); call_stack.top_mut().stack_mut().append(&mut args);
// ensure enough stack space
call_stack
.top_mut()
.stack_mut()
.resize(arg_count + (function.stack_size() as usize), Value::Null);
// set fp for callee frame // set fp for callee frame
let callee_frame = call_stack.top_mut(); let callee_frame = call_stack.top_mut();
callee_frame.set_fp(*arg_count); callee_frame.set_fp(*arg_count);
@ -358,141 +316,68 @@ pub fn call<'a>(
call_stack.top_mut().increment_ip(); call_stack.top_mut().increment_ip();
} }
/* Add instructions */ /* Load constant instructions */
Instruction::Add(left_operand, right_operand, destination) => { Instruction::LoadStringConstant(constant_name, destination) => {
let left_value = match left_operand { let constant = &context.constants()[constant_name];
AddOperand::Location(location) => load_value( match constant {
registers, Constant::String(string_constant) => {
call_stack.top().stack(), registers[*destination] = Value::String(string_constant.content_owned());
call_stack.top().fp(),
location,
)
.clone(),
AddOperand::Int(i) => Value::Int(*i),
AddOperand::String(constant_name) => {
load_constant_value(context.constants(), constant_name)
} }
};
let right_value = match right_operand {
AddOperand::Location(location) => load_value(
registers,
call_stack.top().stack(),
call_stack.top().fp(),
location,
)
.clone(),
AddOperand::Int(i) => Value::Int(*i),
AddOperand::String(constant_name) => {
load_constant_value(context.constants(), constant_name)
}
};
if let Value::Int(left) = left_value {
if let Value::Int(right) = right_value {
let result = left + right;
let fp = call_stack.top().fp();
put_value(
registers,
call_stack.top_mut().stack_mut(),
fp,
destination,
Value::Int(result),
);
} else if let Value::String(s) = right_value {
let result = format!("{}{}", left, s);
let fp = call_stack.top().fp();
put_value(
registers,
call_stack.top_mut().stack_mut(),
fp,
destination,
Value::String(result.into()),
);
} else {
panic!("Attempt to add on non-addable type: {:?}", right_value);
}
} else if let Value::String(left) = left_value {
if let Value::Int(right) = right_value {
let result = format!("{}{}", left, right);
let fp = call_stack.top().fp();
put_value(
registers,
call_stack.top_mut().stack_mut(),
fp,
destination,
Value::String(result.into()),
);
} else if let Value::String(right) = right_value {
let result = format!("{}{}", left, right);
let fp = call_stack.top().fp();
put_value(
registers,
call_stack.top_mut().stack_mut(),
fp,
destination,
Value::String(result.into()),
);
} else {
panic!("Attempt to add on non-addable type: {:?}", right_value);
}
} else {
panic!("Attempt to add on non-addable type: {:?}", left_value);
} }
call_stack.top_mut().increment_ip();
}
/* Add instructions */
Instruction::AddIntInt(lhs, rhs, destination) => {
let lhs_value = registers[*lhs].unwrap_int();
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(); call_stack.top_mut().increment_ip();
} }
/* Pop instructions */ /* Pop instructions */
Instruction::Pop(maybe_location) => { Instruction::Pop(maybe_register) => {
let value = call_stack.top_mut().stack_mut().pop().unwrap(); let value = call_stack.top_mut().stack_mut().pop().unwrap();
if let Some(location) = maybe_location { if let Some(register) = maybe_register {
let fp = call_stack.top().fp(); registers[*register] = value;
put_value(
registers,
call_stack.top_mut().stack_mut(),
fp,
location,
value,
);
} }
call_stack.top_mut().increment_ip(); call_stack.top_mut().increment_ip();
} }
/* Return instructions */ /* Return instructions */
Instruction::SetReturnValue(return_operand) => { Instruction::SetReturnValue(register) => {
let value = match return_operand { call_stack
ReturnOperand::Location(location) => load_value( .top_mut()
registers, .set_return_value(registers[*register].clone());
call_stack.top().stack(),
call_stack.top().fp(),
location,
)
.clone(),
ReturnOperand::Int(i) => Value::Int(*i),
ReturnOperand::String(constant_name) => {
load_constant_value(context.constants(), constant_name)
}
};
call_stack.top_mut().set_return_value(value);
call_stack.top_mut().increment_ip(); call_stack.top_mut().increment_ip();
} }
Instruction::Return => { Instruction::Return(caller_pop_count) => {
let mut callee = call_stack.pop(); // pop this frame let mut callee = call_stack.pop(); // pop this frame
let maybe_caller = call_stack.maybe_top_mut(); let maybe_caller = call_stack.maybe_top_mut();
if let Some(caller) = maybe_caller { if let Some(caller) = maybe_caller {
for _ in 0..callee.function().parameter_count() { for _ in 0..*caller_pop_count {
caller.stack_mut().pop(); caller.stack_mut().pop();
} }
if let Some(return_value) = callee.take_return_value() { if let Some(return_value) = callee.take_return_value() {
// n.b. callee // n.b. callee
caller.stack_mut().push(return_value); caller.stack_mut().push(return_value);
} }
} else {
// callee is the bottommost frame ("main" or whatever was called)
// set the return value
return_value = callee.take_return_value();
} }
// do not increment ip of the caller; this was done above BEFORE the call // do not increment ip of the caller; this was done above BEFORE the call
} }
@ -506,5 +391,5 @@ pub fn call<'a>(
} }
} }
} }
return_value None // todo: returning results from main functions
} }

View File

@ -1,4 +1,3 @@
use std::fmt::{Display, Formatter};
use std::rc::Rc; use std::rc::Rc;
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
@ -29,19 +28,3 @@ impl Value {
} }
} }
} }
impl Display for Value {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Value::Int(i) => {
write!(f, "{}", i)
}
Value::String(s) => {
write!(f, "{}", s)
}
Value::Null => {
write!(f, "Null")
}
}
}
}

View File

@ -1,3 +0,0 @@
fn main() -> Int
42
end

View File

@ -5,6 +5,4 @@ fn main()
let b = 2 let b = 2
let c = 3 let c = 3
let x = a + b + c let x = a + b + c
println(x)
println(a)
end end