Compare commits
5 Commits
713aec24ae
...
8e9ae9ed98
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8e9ae9ed98 | ||
|
|
272d288213 | ||
|
|
c91bb35d8d | ||
|
|
15089a15d2 | ||
|
|
cfff03e97d |
@ -4,6 +4,7 @@ use codespan_reporting::files::SimpleFiles;
|
||||
use codespan_reporting::term;
|
||||
use codespan_reporting::term::termcolor::{ColorChoice, StandardStream};
|
||||
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::diagnostic::Diagnostic;
|
||||
use dmc_lib::parser::parse_compilation_unit;
|
||||
@ -21,6 +22,9 @@ struct Cli {
|
||||
|
||||
#[arg(long)]
|
||||
show_asm: bool,
|
||||
|
||||
#[arg(long)]
|
||||
show_ir: bool,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
@ -50,6 +54,16 @@ fn main() {
|
||||
let type_check_diagnostics = compilation_unit.type_check(&symbol_table);
|
||||
check_and_report_diagnostics(&files, script_file_id, &type_check_diagnostics);
|
||||
|
||||
if args.show_ir {
|
||||
for declaration in compilation_unit.declarations() {
|
||||
if let ModuleLevelDeclaration::Function(function) = declaration {
|
||||
let mut ir_function = function.to_ir(&symbol_table);
|
||||
ir_function.assign_registers();
|
||||
println!("{}", ir_function)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut constants_table = ConstantsTable::new();
|
||||
let asm_functions = compilation_unit.assemble(&symbol_table, &mut constants_table);
|
||||
|
||||
|
||||
@ -1,12 +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::ir_builder::IrBuilder;
|
||||
use crate::constants_table::ConstantsTable;
|
||||
use crate::diagnostic::Diagnostic;
|
||||
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::symbol::ExpressibleSymbol;
|
||||
use crate::symbol_table::SymbolTable;
|
||||
use crate::type_info::TypeInfo;
|
||||
use std::ops::Deref;
|
||||
|
||||
pub struct AdditiveExpression {
|
||||
lhs: Box<Expression>,
|
||||
@ -65,6 +74,14 @@ impl AdditiveExpression {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_ir(&self, builder: &mut IrBuilder, symbol_table: &SymbolTable) -> IrAdd {
|
||||
let lhs_ir_expression = expression_to_ir_expression(&self.lhs, builder, symbol_table)
|
||||
.expect("Attempt to add non-expression");
|
||||
let rhs_ir_expression = expression_to_ir_expression(&self.rhs, builder, symbol_table)
|
||||
.expect("Attempt to add non-expression");
|
||||
IrAdd::new(lhs_ir_expression, rhs_ir_expression)
|
||||
}
|
||||
|
||||
fn assemble_side(
|
||||
expression: &Expression,
|
||||
context: &mut AssembleContext,
|
||||
|
||||
59
dmc-lib/src/ast/ast_to_ir_util.rs
Normal file
59
dmc-lib/src/ast/ast_to_ir_util.rs
Normal file
@ -0,0 +1,59 @@
|
||||
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, IrVirtualRegisterVariable};
|
||||
use crate::symbol_table::SymbolTable;
|
||||
use crate::type_info::TypeInfo;
|
||||
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 = IrVirtualRegisterVariable::new(&builder.new_t_var(), call.type_info());
|
||||
let as_rc = Rc::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(IrVariable::VirtualRegister(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 = IrVirtualRegisterVariable::new(
|
||||
&builder.new_t_var(),
|
||||
additive_expression.type_info(),
|
||||
);
|
||||
let as_rc = Rc::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(IrVariable::VirtualRegister(as_rc)))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2,13 +2,19 @@ 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::ir_builder::IrBuilder;
|
||||
use crate::constants_table::ConstantsTable;
|
||||
use crate::diagnostic::Diagnostic;
|
||||
use crate::ir::ir_call::IrCall;
|
||||
use crate::ir::ir_expression::IrExpression;
|
||||
use crate::source_range::SourceRange;
|
||||
use crate::symbol::ExpressibleSymbol;
|
||||
use crate::symbol::{ExpressibleSymbol, FunctionSymbol};
|
||||
use crate::symbol_table::SymbolTable;
|
||||
use crate::type_info::TypeInfo;
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
|
||||
pub struct Call {
|
||||
callee: Box<Expression>,
|
||||
@ -121,6 +127,35 @@ impl Call {
|
||||
}
|
||||
}
|
||||
|
||||
fn get_callee_symbol(&self) -> Rc<RefCell<FunctionSymbol>> {
|
||||
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."),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_ir(&self, builder: &mut IrBuilder, symbol_table: &SymbolTable) -> IrCall {
|
||||
let arguments: Vec<IrExpression> = self
|
||||
.arguments
|
||||
.iter()
|
||||
.map(|argument| expression_to_ir_expression(argument, builder, symbol_table))
|
||||
.inspect(|expression| {
|
||||
if expression.is_none() {
|
||||
panic!("Attempt to pass non-expression")
|
||||
}
|
||||
})
|
||||
.map(Option::unwrap)
|
||||
.collect();
|
||||
let function_symbol = self.get_callee_symbol();
|
||||
IrCall::new(function_symbol.borrow().name_owned(), arguments)
|
||||
}
|
||||
|
||||
pub fn assemble(
|
||||
&self,
|
||||
context: &mut AssembleContext,
|
||||
|
||||
@ -1,8 +1,12 @@
|
||||
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::ir_builder::IrBuilder;
|
||||
use crate::constants_table::ConstantsTable;
|
||||
use crate::diagnostic::Diagnostic;
|
||||
use crate::ir::ir_return::IrReturn;
|
||||
use crate::ir::ir_statement::IrStatement;
|
||||
use crate::symbol::FunctionSymbol;
|
||||
use crate::symbol_table::SymbolTable;
|
||||
use crate::type_info::TypeInfo;
|
||||
@ -36,6 +40,15 @@ impl ExpressionStatement {
|
||||
self.expression.type_check(symbol_table)
|
||||
}
|
||||
|
||||
pub fn to_ir(&self, builder: &mut IrBuilder, symbol_table: &SymbolTable, is_last: bool) {
|
||||
let ir_expression = expression_to_ir_expression(self.expression(), builder, symbol_table);
|
||||
if ir_expression.is_some() && is_last {
|
||||
builder
|
||||
.current_block_mut()
|
||||
.add_statement(IrStatement::Return(IrReturn::new(ir_expression)));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn assemble(
|
||||
&self,
|
||||
context: &mut AssembleContext,
|
||||
|
||||
@ -1,10 +1,13 @@
|
||||
use crate::asm::asm_instruction::{AsmInstruction, Return};
|
||||
use crate::ast::assemble_context::AssembleContext;
|
||||
use crate::ast::ir_builder::IrBuilder;
|
||||
use crate::ast::parameter::Parameter;
|
||||
use crate::ast::statement::Statement;
|
||||
use crate::ast::type_use::TypeUse;
|
||||
use crate::constants_table::ConstantsTable;
|
||||
use crate::diagnostic::Diagnostic;
|
||||
use crate::ir::ir_function::IrFunction;
|
||||
use crate::ir::ir_parameter::IrParameter;
|
||||
use crate::source_range::SourceRange;
|
||||
use crate::symbol::FunctionSymbol;
|
||||
use crate::symbol_table::{SymbolInsertError, SymbolTable};
|
||||
@ -158,6 +161,42 @@ impl Function {
|
||||
diagnostics
|
||||
}
|
||||
|
||||
pub fn to_ir(&self, symbol_table: &SymbolTable) -> IrFunction {
|
||||
let mut builder = IrBuilder::new();
|
||||
|
||||
// parameters
|
||||
for parameter in &self.parameters {
|
||||
let parameter_symbol = parameter.parameter_symbol();
|
||||
let ir_parameter = IrParameter::new(
|
||||
parameter_symbol.borrow().name(),
|
||||
parameter_symbol.borrow().type_info().clone(),
|
||||
);
|
||||
let as_rc = Rc::new(ir_parameter);
|
||||
builder.parameters_mut().push(as_rc.clone());
|
||||
parameter_symbol.borrow_mut().set_ir_parameter(as_rc);
|
||||
}
|
||||
|
||||
let entry_block_id = builder.new_block();
|
||||
for (i, statement) in self.statements.iter().enumerate() {
|
||||
let is_last = i == self.statements.len() - 1;
|
||||
statement.to_ir(&mut builder, symbol_table, is_last);
|
||||
}
|
||||
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();
|
||||
IrFunction::new(
|
||||
Rc::from(self.declared_name()),
|
||||
builder.parameters(),
|
||||
return_type_info,
|
||||
entry_block,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn assemble(
|
||||
&self,
|
||||
context: &mut AssembleContext,
|
||||
|
||||
92
dmc-lib/src/ast/ir_builder.rs
Normal file
92
dmc-lib/src/ast/ir_builder.rs
Normal file
@ -0,0 +1,92 @@
|
||||
use crate::ir::ir_block::IrBlock;
|
||||
use crate::ir::ir_parameter::IrParameter;
|
||||
use crate::ir::ir_statement::IrStatement;
|
||||
use crate::ir::ir_variable::IrVariable;
|
||||
use std::cell::RefCell;
|
||||
use std::collections::HashMap;
|
||||
use std::rc::Rc;
|
||||
|
||||
pub struct IrBuilder {
|
||||
parameters: Vec<Rc<IrParameter>>,
|
||||
block_counter: usize,
|
||||
t_var_counter: usize,
|
||||
blocks: HashMap<usize, Rc<RefCell<IrBlock>>>,
|
||||
current_block_builder: Option<IrBlockBuilder>,
|
||||
}
|
||||
|
||||
impl IrBuilder {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
parameters: vec![],
|
||||
block_counter: 0,
|
||||
t_var_counter: 0,
|
||||
blocks: HashMap::new(),
|
||||
current_block_builder: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parameters(&self) -> &[Rc<IrParameter>] {
|
||||
&self.parameters
|
||||
}
|
||||
|
||||
pub fn parameters_mut(&mut self) -> &mut Vec<Rc<IrParameter>> {
|
||||
&mut self.parameters
|
||||
}
|
||||
|
||||
pub fn new_block(&mut self) -> usize {
|
||||
let block_id = self.block_counter;
|
||||
self.block_counter += 1;
|
||||
let block_builder = IrBlockBuilder::new(block_id);
|
||||
self.current_block_builder = Some(block_builder);
|
||||
block_id
|
||||
}
|
||||
|
||||
pub fn get_block(&mut self, block_id: usize) -> &Rc<RefCell<IrBlock>> {
|
||||
self.blocks
|
||||
.get(&block_id)
|
||||
.expect(&format!("Block {} not found", block_id))
|
||||
}
|
||||
|
||||
pub fn current_block_mut(&mut self) -> &mut IrBlockBuilder {
|
||||
self.current_block_builder
|
||||
.as_mut()
|
||||
.expect("No current block builder")
|
||||
}
|
||||
|
||||
pub fn finish_block(&mut self) {
|
||||
let builder = self
|
||||
.current_block_builder
|
||||
.take()
|
||||
.expect("No current block builder");
|
||||
let block = builder.build();
|
||||
self.blocks.insert(block.id(), Rc::new(RefCell::new(block)));
|
||||
}
|
||||
|
||||
pub fn new_t_var(&mut self) -> String {
|
||||
let id = self.t_var_counter;
|
||||
self.t_var_counter += 1;
|
||||
format!("t{}", id)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct IrBlockBuilder {
|
||||
id: usize,
|
||||
statements: Vec<IrStatement>,
|
||||
}
|
||||
|
||||
impl IrBlockBuilder {
|
||||
pub fn new(id: usize) -> Self {
|
||||
Self {
|
||||
id,
|
||||
statements: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_statement(&mut self, statement: IrStatement) {
|
||||
self.statements.push(statement);
|
||||
}
|
||||
|
||||
pub fn build(self) -> IrBlock {
|
||||
IrBlock::new(self.id, &format!("b{}", self.id), self.statements)
|
||||
}
|
||||
}
|
||||
@ -1,11 +1,18 @@
|
||||
use crate::asm::asm_instruction::{AsmInstruction, LoadConstant, Move, Operand, Pop};
|
||||
use crate::ast::assemble_context::AssembleContext;
|
||||
use crate::ast::expression::Expression;
|
||||
use crate::ast::ir_builder::IrBuilder;
|
||||
use crate::constants_table::ConstantsTable;
|
||||
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, IrVirtualRegisterVariable};
|
||||
use crate::source_range::SourceRange;
|
||||
use crate::symbol::{ExpressibleSymbol, VariableSymbol};
|
||||
use crate::symbol_table::{SymbolInsertError, SymbolTable};
|
||||
use std::rc::Rc;
|
||||
|
||||
pub struct LetStatement {
|
||||
declared_name: String,
|
||||
@ -81,6 +88,36 @@ impl LetStatement {
|
||||
vec![]
|
||||
}
|
||||
|
||||
pub fn to_ir(&self, builder: &mut IrBuilder, symbol_table: &SymbolTable) {
|
||||
let init_operation = match self.initializer() {
|
||||
Expression::Call(call) => IrOperation::Call(call.to_ir(builder, symbol_table)),
|
||||
Expression::IntegerLiteral(integer_literal) => {
|
||||
IrOperation::Load(IrExpression::Int(integer_literal.value()))
|
||||
}
|
||||
Expression::String(string_literal) => {
|
||||
IrOperation::Load(IrExpression::String(string_literal.content().into()))
|
||||
}
|
||||
Expression::Identifier(identifier) => {
|
||||
IrOperation::Load(identifier.expressible_symbol().ir_expression())
|
||||
}
|
||||
Expression::Additive(additive_expression) => {
|
||||
IrOperation::Add(additive_expression.to_ir(builder, symbol_table))
|
||||
}
|
||||
};
|
||||
|
||||
let destination_symbol =
|
||||
symbol_table.get_variable_symbol(self.scope_id.unwrap(), &self.declared_name);
|
||||
let destination_vr_variable =
|
||||
IrVirtualRegisterVariable::new(self.declared_name(), self.initializer().type_info());
|
||||
let as_rc = Rc::new(destination_vr_variable);
|
||||
let ir_assign = IrAssign::new(as_rc.clone(), init_operation);
|
||||
destination_symbol.borrow_mut().set_vr_variable(as_rc);
|
||||
|
||||
builder
|
||||
.current_block_mut()
|
||||
.add_statement(IrStatement::Assign(ir_assign));
|
||||
}
|
||||
|
||||
pub fn assemble(
|
||||
&self,
|
||||
context: &mut AssembleContext,
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
pub mod additive_expression;
|
||||
pub mod assemble_context;
|
||||
mod ast_to_ir_util;
|
||||
pub mod call;
|
||||
pub mod compilation_unit;
|
||||
pub mod expression;
|
||||
@ -9,6 +10,7 @@ pub mod fqn;
|
||||
pub mod function;
|
||||
pub mod identifier;
|
||||
pub mod integer_literal;
|
||||
mod ir_builder;
|
||||
pub mod let_statement;
|
||||
pub mod module_level_declaration;
|
||||
pub mod parameter;
|
||||
|
||||
@ -11,6 +11,7 @@ pub struct Parameter {
|
||||
declared_name: String,
|
||||
declared_name_source_range: SourceRange,
|
||||
type_use: TypeUse,
|
||||
parameter_symbol: Option<Rc<RefCell<ParameterSymbol>>>,
|
||||
}
|
||||
|
||||
impl Parameter {
|
||||
@ -23,6 +24,7 @@ impl Parameter {
|
||||
declared_name: declared_name.into(),
|
||||
declared_name_source_range,
|
||||
type_use,
|
||||
parameter_symbol: None,
|
||||
}
|
||||
}
|
||||
|
||||
@ -35,7 +37,10 @@ impl Parameter {
|
||||
TypeInfo::from_declared_name(self.type_use.declared_name()),
|
||||
));
|
||||
match insert_result {
|
||||
Ok(parameter_symbol) => Ok(parameter_symbol),
|
||||
Ok(parameter_symbol) => {
|
||||
self.parameter_symbol = Some(parameter_symbol.clone());
|
||||
Ok(parameter_symbol)
|
||||
}
|
||||
Err(symbol_insert_error) => match symbol_insert_error {
|
||||
SymbolInsertError::AlreadyDeclared(already_declared) => Err(vec![Diagnostic::new(
|
||||
&format!("Parameter {} already declared.", already_declared.name()),
|
||||
@ -46,11 +51,20 @@ impl Parameter {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn check_name_usages(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec<Diagnostic>> {
|
||||
pub fn check_name_usages(
|
||||
&mut self,
|
||||
_symbol_table: &SymbolTable,
|
||||
) -> Result<(), Vec<Diagnostic>> {
|
||||
Ok(()) // no-op for now
|
||||
}
|
||||
|
||||
pub fn type_check(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec<Diagnostic>> {
|
||||
pub fn type_check(&mut self, _symbol_table: &SymbolTable) -> Result<(), Vec<Diagnostic>> {
|
||||
Ok(()) // no-op for now
|
||||
}
|
||||
|
||||
pub fn parameter_symbol(&self) -> &Rc<RefCell<ParameterSymbol>> {
|
||||
self.parameter_symbol
|
||||
.as_ref()
|
||||
.expect("parameter symbol not initialized")
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
use crate::ast::assemble_context::AssembleContext;
|
||||
use crate::ast::expression_statement::ExpressionStatement;
|
||||
use crate::ast::ir_builder::IrBuilder;
|
||||
use crate::ast::let_statement::LetStatement;
|
||||
use crate::constants_table::ConstantsTable;
|
||||
use crate::diagnostic::Diagnostic;
|
||||
@ -41,6 +42,17 @@ impl Statement {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_ir(&self, builder: &mut IrBuilder, symbol_table: &SymbolTable, is_last: bool) {
|
||||
match self {
|
||||
Statement::Let(let_statement) => {
|
||||
let_statement.to_ir(builder, symbol_table);
|
||||
}
|
||||
Statement::Expression(expression_statement) => {
|
||||
expression_statement.to_ir(builder, symbol_table, is_last);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn assemble(
|
||||
&self,
|
||||
context: &mut AssembleContext,
|
||||
|
||||
32
dmc-lib/src/ir/ir_add.rs
Normal file
32
dmc-lib/src/ir/ir_add.rs
Normal file
@ -0,0 +1,32 @@
|
||||
use crate::ir::ir_expression::IrExpression;
|
||||
use crate::ir::ir_variable::IrVirtualRegisterVariable;
|
||||
use std::collections::HashSet;
|
||||
use std::fmt::{Display, Formatter};
|
||||
use std::rc::Rc;
|
||||
|
||||
pub struct IrAdd {
|
||||
left: Box<IrExpression>,
|
||||
right: Box<IrExpression>,
|
||||
}
|
||||
|
||||
impl IrAdd {
|
||||
pub fn new(left: IrExpression, right: IrExpression) -> Self {
|
||||
Self {
|
||||
left: left.into(),
|
||||
right: right.into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn vr_uses(&self) -> HashSet<Rc<IrVirtualRegisterVariable>> {
|
||||
let mut set = HashSet::new();
|
||||
set.extend(self.left.vr_uses());
|
||||
set.extend(self.right.vr_uses());
|
||||
set
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for IrAdd {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{} + {}", self.left, self.right)
|
||||
}
|
||||
}
|
||||
58
dmc-lib/src/ir/ir_assign.rs
Normal file
58
dmc-lib/src/ir/ir_assign.rs
Normal file
@ -0,0 +1,58 @@
|
||||
use crate::ir::ir_operation::IrOperation;
|
||||
use crate::ir::ir_variable::{IrStackVariable, IrVariable, IrVirtualRegisterVariable};
|
||||
use std::collections::HashSet;
|
||||
use std::fmt::{Display, Formatter};
|
||||
use std::ops::Deref;
|
||||
use std::rc::Rc;
|
||||
|
||||
pub struct IrAssign {
|
||||
destination: Box<IrVariable>,
|
||||
initializer: Box<IrOperation>,
|
||||
}
|
||||
|
||||
impl IrAssign {
|
||||
pub fn new(destination: Rc<IrVirtualRegisterVariable>, initializer: IrOperation) -> Self {
|
||||
Self {
|
||||
destination: Box::new(IrVariable::VirtualRegister(destination)),
|
||||
initializer: initializer.into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn vr_definitions(&self) -> HashSet<Rc<IrVirtualRegisterVariable>> {
|
||||
match self.destination.deref() {
|
||||
IrVariable::VirtualRegister(vr_variable) => {
|
||||
let mut set = HashSet::new();
|
||||
set.insert(vr_variable.clone());
|
||||
set
|
||||
}
|
||||
IrVariable::Stack(_) => HashSet::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn vr_uses(&self) -> HashSet<Rc<IrVirtualRegisterVariable>> {
|
||||
self.initializer.vr_uses()
|
||||
}
|
||||
|
||||
pub fn propagate_spills(&mut self, spills: &HashSet<Rc<IrVirtualRegisterVariable>>) {
|
||||
if let IrVariable::VirtualRegister(vr_variable) = self.destination.deref() {
|
||||
if spills.contains(vr_variable) {
|
||||
self.destination = Box::new(IrVariable::Stack(IrStackVariable::new(
|
||||
vr_variable.name(),
|
||||
vr_variable.type_info().clone(),
|
||||
)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for IrAssign {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{}: {} = {}",
|
||||
self.destination,
|
||||
self.destination.type_info(),
|
||||
self.initializer
|
||||
)
|
||||
}
|
||||
}
|
||||
351
dmc-lib/src/ir/ir_block.rs
Normal file
351
dmc-lib/src/ir/ir_block.rs
Normal file
@ -0,0 +1,351 @@
|
||||
use crate::ir::ir_statement::IrStatement;
|
||||
use crate::ir::ir_variable::IrVirtualRegisterVariable;
|
||||
use std::cell::RefCell;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::fmt::{Display, Formatter};
|
||||
use std::rc::Rc;
|
||||
|
||||
pub struct IrBlock {
|
||||
id: usize,
|
||||
debug_label: String,
|
||||
predecessors: Vec<Rc<RefCell<IrBlock>>>,
|
||||
successors: Vec<Rc<RefCell<IrBlock>>>,
|
||||
statements: Vec<IrStatement>,
|
||||
}
|
||||
|
||||
type LivenessMapByStatement = HashMap<usize, HashSet<Rc<IrVirtualRegisterVariable>>>;
|
||||
|
||||
impl IrBlock {
|
||||
pub fn new(id: usize, debug_label: &str, statements: Vec<IrStatement>) -> Self {
|
||||
Self {
|
||||
id,
|
||||
debug_label: debug_label.into(),
|
||||
predecessors: vec![],
|
||||
successors: vec![],
|
||||
statements,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn id(&self) -> usize {
|
||||
self.id
|
||||
}
|
||||
|
||||
pub fn statements(&self) -> &[IrStatement] {
|
||||
&self.statements
|
||||
}
|
||||
|
||||
fn vr_definitions(&self) -> HashSet<Rc<IrVirtualRegisterVariable>> {
|
||||
let mut set = HashSet::new();
|
||||
for statement in &self.statements {
|
||||
set.extend(statement.vr_definitions());
|
||||
}
|
||||
set
|
||||
}
|
||||
|
||||
fn vr_uses(&self) -> HashSet<Rc<IrVirtualRegisterVariable>> {
|
||||
let mut set = HashSet::new();
|
||||
for statement in &self.statements {
|
||||
set.extend(statement.vr_uses());
|
||||
}
|
||||
set
|
||||
}
|
||||
|
||||
fn live_in_live_out(&self) -> (LivenessMapByStatement, LivenessMapByStatement) {
|
||||
let mut live_in: LivenessMapByStatement = HashMap::new();
|
||||
let mut live_out: LivenessMapByStatement = HashMap::new();
|
||||
|
||||
loop {
|
||||
let mut did_work = false;
|
||||
for (statement_index, statement) in self.statements.iter().enumerate().rev() {
|
||||
// init if necessary
|
||||
if !live_in.contains_key(&statement_index) {
|
||||
live_in.insert(statement_index, HashSet::new());
|
||||
}
|
||||
if !live_out.contains_key(&statement_index) {
|
||||
live_out.insert(statement_index, HashSet::new());
|
||||
}
|
||||
|
||||
// out (union of successors ins)
|
||||
// for now, a statement can only have one successor
|
||||
// this will need to be updated when we add jumps
|
||||
if let Some(successor_live_in) = live_in.get(&(statement_index + 1)) {
|
||||
let statement_live_out = live_out.get_mut(&statement_index).unwrap();
|
||||
for vr_variable in successor_live_in {
|
||||
if statement_live_out.insert(vr_variable.clone()) {
|
||||
did_work = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// in: use(s) U ( out(s) - def(s) )
|
||||
let mut new_ins = statement.vr_uses();
|
||||
let statement_live_out = live_out.get(&statement_index).unwrap();
|
||||
let defs = statement.vr_definitions();
|
||||
let rhs = statement_live_out - &defs;
|
||||
new_ins.extend(rhs);
|
||||
|
||||
let statement_live_in = live_in.get_mut(&statement_index).unwrap();
|
||||
for new_in in new_ins {
|
||||
if statement_live_in.insert(new_in) {
|
||||
did_work = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if !did_work {
|
||||
break;
|
||||
}
|
||||
}
|
||||
(live_in, live_out)
|
||||
}
|
||||
|
||||
fn interference_graph(
|
||||
&self,
|
||||
) -> HashMap<Rc<IrVirtualRegisterVariable>, HashSet<Rc<IrVirtualRegisterVariable>>> {
|
||||
let mut all_vr_variables: HashSet<Rc<IrVirtualRegisterVariable>> = HashSet::new();
|
||||
for statement in &self.statements {
|
||||
all_vr_variables.extend(statement.vr_definitions());
|
||||
all_vr_variables.extend(statement.vr_uses());
|
||||
}
|
||||
|
||||
let mut graph: HashMap<
|
||||
Rc<IrVirtualRegisterVariable>,
|
||||
HashSet<Rc<IrVirtualRegisterVariable>>,
|
||||
> = HashMap::new();
|
||||
for variable in all_vr_variables {
|
||||
graph.insert(variable, HashSet::new());
|
||||
}
|
||||
|
||||
let (_, live_out) = self.live_in_live_out();
|
||||
|
||||
for (statement_index, statement) in self.statements.iter().enumerate() {
|
||||
let statement_live_out = live_out.get(&statement_index).unwrap();
|
||||
for definition_vr_variable in statement.vr_definitions() {
|
||||
for live_out_variable in statement_live_out {
|
||||
// we do the following check to avoid adding an edge to itself
|
||||
if definition_vr_variable != *live_out_variable {
|
||||
graph
|
||||
.get_mut(&definition_vr_variable)
|
||||
.unwrap()
|
||||
.insert(live_out_variable.clone());
|
||||
graph
|
||||
.get_mut(live_out_variable)
|
||||
.unwrap()
|
||||
.insert(definition_vr_variable.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
graph
|
||||
}
|
||||
|
||||
pub fn assign_registers(&mut self) {
|
||||
let mut spills: HashSet<Rc<IrVirtualRegisterVariable>> = HashSet::new();
|
||||
loop {
|
||||
let (registers, new_spills) = register_assignment::assign_registers(self);
|
||||
if spills != new_spills {
|
||||
spills = new_spills;
|
||||
// mutate all IrVirtualRegisters to constituent statements
|
||||
for statement in &mut self.statements {
|
||||
statement.propagate_spills(&spills);
|
||||
}
|
||||
} else {
|
||||
println!("{:?}", registers);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for IrBlock {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
writeln!(f, " {}:", self.debug_label)?;
|
||||
|
||||
let (live_in, live_out) = self.live_in_live_out();
|
||||
|
||||
for (statement_index, statement) in self.statements.iter().enumerate() {
|
||||
let statement_live_in = live_in.get(&statement_index).unwrap();
|
||||
let statement_live_out = live_out.get(&statement_index).unwrap();
|
||||
|
||||
writeln!(
|
||||
f,
|
||||
" {} // live_in: {:?}, live_out: {:?}",
|
||||
statement, statement_live_in, statement_live_out
|
||||
)?;
|
||||
}
|
||||
writeln!(f, " // ---- {} meta ----", self.debug_label)?;
|
||||
writeln!(f, " // definitions: {:?}", self.vr_definitions())?;
|
||||
writeln!(f, " // uses: {:?}", self.vr_uses())?;
|
||||
writeln!(
|
||||
f,
|
||||
" // interference graph: {:?}",
|
||||
self.interference_graph()
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
mod register_assignment {
|
||||
use super::*;
|
||||
|
||||
#[derive(Debug)]
|
||||
struct WorkItem {
|
||||
vr: Rc<IrVirtualRegisterVariable>,
|
||||
edges: HashSet<Rc<IrVirtualRegisterVariable>>,
|
||||
color: bool,
|
||||
}
|
||||
|
||||
pub fn assign_registers(
|
||||
block: &IrBlock,
|
||||
) -> (
|
||||
HashMap<Rc<IrVirtualRegisterVariable>, usize>,
|
||||
HashSet<Rc<IrVirtualRegisterVariable>>,
|
||||
) {
|
||||
let k = 8;
|
||||
let mut spills: HashSet<Rc<IrVirtualRegisterVariable>> = HashSet::new();
|
||||
|
||||
loop {
|
||||
// 1. get interference graph
|
||||
let mut interference_graph = block.interference_graph();
|
||||
let mut work_stack: Vec<WorkItem> = vec![];
|
||||
|
||||
loop {
|
||||
// 2. coloring by simplification
|
||||
// try to find a node (virtual register) with less than k outgoing edges,
|
||||
// and mark as color
|
||||
// if not, pick any, and mark as spill for step 3
|
||||
let register_lt_k = interference_graph.iter().find_map(|(vr, neighbors)| {
|
||||
if neighbors.len() < k { Some(vr) } else { None }
|
||||
});
|
||||
if let Some(vr) = register_lt_k {
|
||||
let vr = vr.clone();
|
||||
// remove both outgoing and incoming edges; save either set for WorkItem
|
||||
// first, outgoing:
|
||||
let outgoing_edges = interference_graph.remove(&vr).unwrap();
|
||||
|
||||
// second, incoming
|
||||
interference_graph.iter_mut().for_each(|(_, neighbors)| {
|
||||
neighbors.remove(&vr);
|
||||
});
|
||||
|
||||
// push to work stack
|
||||
work_stack.push(WorkItem {
|
||||
vr,
|
||||
edges: outgoing_edges,
|
||||
color: true,
|
||||
})
|
||||
} else {
|
||||
// pick any
|
||||
let vr = interference_graph.iter().last().unwrap().0.clone();
|
||||
|
||||
// first, outgoing
|
||||
let outgoing_edges = interference_graph.remove(&vr).unwrap();
|
||||
|
||||
// second, incoming
|
||||
interference_graph.iter_mut().for_each(|(_, neighbors)| {
|
||||
neighbors.remove(&vr);
|
||||
});
|
||||
|
||||
work_stack.push(WorkItem {
|
||||
vr,
|
||||
edges: outgoing_edges,
|
||||
color: false, // spill
|
||||
});
|
||||
}
|
||||
|
||||
if interference_graph.is_empty() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 3. assign colors to registers
|
||||
let mut rebuilt_graph: HashMap<
|
||||
Rc<IrVirtualRegisterVariable>,
|
||||
HashSet<Rc<IrVirtualRegisterVariable>>,
|
||||
> = HashMap::new();
|
||||
let mut register_assignments: HashMap<Rc<IrVirtualRegisterVariable>, usize> =
|
||||
HashMap::new();
|
||||
let mut new_spills: HashSet<Rc<IrVirtualRegisterVariable>> = HashSet::new();
|
||||
|
||||
while let Some(work_item) = work_stack.pop() {
|
||||
if work_item.color {
|
||||
assign_register(&work_item, &mut rebuilt_graph, k, &mut register_assignments);
|
||||
} else {
|
||||
// first, see if we can optimistically color
|
||||
// find how many assignments have been made for the outgoing edges
|
||||
// if it's less than k, we can do it
|
||||
let mut number_of_assigned_edges = 0;
|
||||
for edge in &work_item.edges {
|
||||
if register_assignments.contains_key(edge) {
|
||||
number_of_assigned_edges += 1;
|
||||
}
|
||||
}
|
||||
|
||||
if number_of_assigned_edges < k {
|
||||
// optimistically color
|
||||
assign_register(
|
||||
&work_item,
|
||||
&mut rebuilt_graph,
|
||||
k,
|
||||
&mut register_assignments,
|
||||
);
|
||||
} else {
|
||||
// spill
|
||||
new_spills.insert(work_item.vr.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if spills.eq(&new_spills) {
|
||||
return (register_assignments, spills);
|
||||
} else {
|
||||
spills = new_spills;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn assign_register(
|
||||
work_item: &WorkItem,
|
||||
rebuilt_graph: &mut HashMap<
|
||||
Rc<IrVirtualRegisterVariable>,
|
||||
HashSet<Rc<IrVirtualRegisterVariable>>,
|
||||
>,
|
||||
k: usize,
|
||||
register_assignments: &mut HashMap<Rc<IrVirtualRegisterVariable>, usize>,
|
||||
) {
|
||||
let this_vertex_vr = &work_item.vr;
|
||||
// init the vertex
|
||||
rebuilt_graph.insert(this_vertex_vr.clone(), HashSet::new());
|
||||
|
||||
// add edges, both outgoing and incoming
|
||||
let neighbors = rebuilt_graph.get_mut(this_vertex_vr).unwrap();
|
||||
// outgoing
|
||||
for edge in &work_item.edges {
|
||||
neighbors.insert(edge.clone());
|
||||
}
|
||||
// incoming
|
||||
for neighbor in neighbors.clone() {
|
||||
if rebuilt_graph.contains_key(&neighbor) {
|
||||
rebuilt_graph
|
||||
.get_mut(&neighbor)
|
||||
.unwrap()
|
||||
.insert(this_vertex_vr.clone());
|
||||
}
|
||||
}
|
||||
|
||||
// find a register which is not yet shared by all outgoing edges' vertices
|
||||
// I think the bug is somewhere here
|
||||
'outer: for i in 0..k {
|
||||
for edge in rebuilt_graph.get_mut(this_vertex_vr).unwrap().iter() {
|
||||
if register_assignments.contains_key(edge) {
|
||||
let assignment = register_assignments.get(edge).unwrap();
|
||||
if *assignment == i {
|
||||
continue 'outer;
|
||||
}
|
||||
}
|
||||
}
|
||||
register_assignments.insert(this_vertex_vr.clone(), i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
48
dmc-lib/src/ir/ir_call.rs
Normal file
48
dmc-lib/src/ir/ir_call.rs
Normal file
@ -0,0 +1,48 @@
|
||||
use crate::ir::ir_expression::IrExpression;
|
||||
use crate::ir::ir_variable::{IrStackVariable, IrVariable, IrVirtualRegisterVariable};
|
||||
use crate::type_info::TypeInfo;
|
||||
use std::collections::HashSet;
|
||||
use std::fmt::{Display, Formatter};
|
||||
use std::rc::Rc;
|
||||
|
||||
pub struct IrCall {
|
||||
function_name: Rc<str>,
|
||||
arguments: Vec<IrExpression>,
|
||||
}
|
||||
|
||||
impl IrCall {
|
||||
pub fn new(function_name: Rc<str>, arguments: Vec<IrExpression>) -> Self {
|
||||
Self {
|
||||
function_name,
|
||||
arguments,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn vr_uses(&self) -> HashSet<Rc<IrVirtualRegisterVariable>> {
|
||||
let mut set = HashSet::new();
|
||||
for argument in &self.arguments {
|
||||
set.extend(argument.vr_uses())
|
||||
}
|
||||
set
|
||||
}
|
||||
|
||||
pub fn propagate_spills(&mut self, spills: &HashSet<Rc<IrVirtualRegisterVariable>>) {
|
||||
for argument in &mut self.arguments {
|
||||
argument.propagate_spills(spills);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for IrCall {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}(", self.function_name)?;
|
||||
for (i, argument) in self.arguments.iter().enumerate() {
|
||||
write!(f, "{}", argument)?;
|
||||
if i < self.arguments.len() - 1 {
|
||||
write!(f, ", ")?;
|
||||
}
|
||||
}
|
||||
write!(f, ")")?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
74
dmc-lib/src/ir/ir_expression.rs
Normal file
74
dmc-lib/src/ir/ir_expression.rs
Normal file
@ -0,0 +1,74 @@
|
||||
use crate::ir::ir_parameter::IrParameter;
|
||||
use crate::ir::ir_variable::{IrStackVariable, IrVariable, IrVirtualRegisterVariable};
|
||||
use std::collections::HashSet;
|
||||
use std::fmt::{Display, Formatter};
|
||||
use std::rc::Rc;
|
||||
|
||||
pub enum IrExpression {
|
||||
Parameter(Rc<IrParameter>),
|
||||
Variable(IrVariable),
|
||||
Int(i32),
|
||||
String(Rc<str>),
|
||||
}
|
||||
|
||||
impl IrExpression {
|
||||
pub fn vr_uses(&self) -> HashSet<Rc<IrVirtualRegisterVariable>> {
|
||||
match self {
|
||||
IrExpression::Parameter(_) => HashSet::new(),
|
||||
IrExpression::Variable(ir_variable) => {
|
||||
let mut set = HashSet::new();
|
||||
if let IrVariable::VirtualRegister(vr_variable) = ir_variable {
|
||||
set.insert(vr_variable.clone());
|
||||
}
|
||||
set
|
||||
}
|
||||
IrExpression::Int(_) => HashSet::new(),
|
||||
IrExpression::String(_) => HashSet::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn propagate_spills(&mut self, spills: &HashSet<Rc<IrVirtualRegisterVariable>>) {
|
||||
match self {
|
||||
IrExpression::Parameter(_) => {
|
||||
// no-op
|
||||
}
|
||||
IrExpression::Variable(ir_variable) => {
|
||||
if let IrVariable::VirtualRegister(vr_variable) = ir_variable {
|
||||
if spills.contains(vr_variable) {
|
||||
let name = vr_variable.name().to_string();
|
||||
let type_info = vr_variable.type_info().clone();
|
||||
let _ = std::mem::replace(
|
||||
ir_variable,
|
||||
IrVariable::Stack(IrStackVariable::new(&name, type_info)),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
IrExpression::Int(_) => {
|
||||
// no-op
|
||||
}
|
||||
IrExpression::String(_) => {
|
||||
// no-op
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for IrExpression {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
IrExpression::Parameter(ir_parameter) => {
|
||||
write!(f, "{}", ir_parameter)
|
||||
}
|
||||
IrExpression::Variable(ir_variable) => {
|
||||
write!(f, "{}", ir_variable)
|
||||
}
|
||||
IrExpression::Int(i) => {
|
||||
write!(f, "{}", i)
|
||||
}
|
||||
IrExpression::String(s) => {
|
||||
write!(f, "\"{}\"", s)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
47
dmc-lib/src/ir/ir_function.rs
Normal file
47
dmc-lib/src/ir/ir_function.rs
Normal file
@ -0,0 +1,47 @@
|
||||
use crate::ir::ir_block::IrBlock;
|
||||
use crate::ir::ir_parameter::IrParameter;
|
||||
use crate::type_info::TypeInfo;
|
||||
use std::cell::RefCell;
|
||||
use std::fmt::Display;
|
||||
use std::rc::Rc;
|
||||
|
||||
pub struct IrFunction {
|
||||
name: Rc<str>,
|
||||
parameters: Vec<Rc<IrParameter>>,
|
||||
return_type_info: TypeInfo,
|
||||
entry: Rc<RefCell<IrBlock>>,
|
||||
}
|
||||
|
||||
impl IrFunction {
|
||||
pub fn new(
|
||||
name: Rc<str>,
|
||||
parameters: &[Rc<IrParameter>],
|
||||
return_type_info: TypeInfo,
|
||||
entry: Rc<RefCell<IrBlock>>,
|
||||
) -> Self {
|
||||
Self {
|
||||
name,
|
||||
parameters: parameters.to_vec(),
|
||||
return_type_info,
|
||||
entry,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn assign_registers(&mut self) {
|
||||
self.entry.borrow_mut().assign_registers();
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for IrFunction {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "fn {}(", self.name)?;
|
||||
for (i, parameter) in self.parameters.iter().enumerate() {
|
||||
write!(f, "{}: {}", parameter, parameter.type_info())?;
|
||||
if i < self.parameters.len() - 1 {
|
||||
write!(f, ", ")?;
|
||||
}
|
||||
}
|
||||
write!(f, ") -> {}\n{}", self.return_type_info, self.entry.borrow())?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
39
dmc-lib/src/ir/ir_operation.rs
Normal file
39
dmc-lib/src/ir/ir_operation.rs
Normal file
@ -0,0 +1,39 @@
|
||||
use crate::ir::ir_add::IrAdd;
|
||||
use crate::ir::ir_call::IrCall;
|
||||
use crate::ir::ir_expression::IrExpression;
|
||||
use crate::ir::ir_variable::{IrVariable, IrVirtualRegisterVariable};
|
||||
use std::collections::HashSet;
|
||||
use std::fmt::{Display, Formatter};
|
||||
use std::rc::Rc;
|
||||
|
||||
pub enum IrOperation {
|
||||
Load(IrExpression),
|
||||
Add(IrAdd),
|
||||
Call(IrCall),
|
||||
}
|
||||
|
||||
impl Display for IrOperation {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
IrOperation::Load(ir_expression) => {
|
||||
write!(f, "{}", ir_expression)
|
||||
}
|
||||
IrOperation::Add(ir_add) => {
|
||||
write!(f, "{}", ir_add)
|
||||
}
|
||||
IrOperation::Call(ir_call) => {
|
||||
write!(f, "{}", ir_call)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl IrOperation {
|
||||
pub fn vr_uses(&self) -> HashSet<Rc<IrVirtualRegisterVariable>> {
|
||||
match self {
|
||||
IrOperation::Load(ir_expression) => ir_expression.vr_uses(),
|
||||
IrOperation::Add(ir_add) => ir_add.vr_uses(),
|
||||
IrOperation::Call(ir_call) => ir_call.vr_uses(),
|
||||
}
|
||||
}
|
||||
}
|
||||
27
dmc-lib/src/ir/ir_parameter.rs
Normal file
27
dmc-lib/src/ir/ir_parameter.rs
Normal file
@ -0,0 +1,27 @@
|
||||
use crate::type_info::TypeInfo;
|
||||
use std::fmt::{Display, Formatter};
|
||||
use std::rc::Rc;
|
||||
|
||||
pub struct IrParameter {
|
||||
name: Rc<str>,
|
||||
type_info: TypeInfo,
|
||||
}
|
||||
|
||||
impl IrParameter {
|
||||
pub fn new(name: &str, type_info: TypeInfo) -> Self {
|
||||
Self {
|
||||
name: name.into(),
|
||||
type_info,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn type_info(&self) -> &TypeInfo {
|
||||
&self.type_info
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for IrParameter {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.name)
|
||||
}
|
||||
}
|
||||
35
dmc-lib/src/ir/ir_return.rs
Normal file
35
dmc-lib/src/ir/ir_return.rs
Normal file
@ -0,0 +1,35 @@
|
||||
use crate::ir::ir_expression::IrExpression;
|
||||
use crate::ir::ir_variable::IrVirtualRegisterVariable;
|
||||
use std::collections::HashSet;
|
||||
use std::fmt::{Display, Formatter};
|
||||
use std::rc::Rc;
|
||||
|
||||
pub struct IrReturn {
|
||||
value: Option<IrExpression>,
|
||||
}
|
||||
|
||||
impl IrReturn {
|
||||
pub fn new(value: Option<IrExpression>) -> Self {
|
||||
Self { value }
|
||||
}
|
||||
|
||||
pub fn vr_uses(&self) -> HashSet<Rc<IrVirtualRegisterVariable>> {
|
||||
self.value.as_ref().map_or(HashSet::new(), |v| v.vr_uses())
|
||||
}
|
||||
|
||||
pub fn propagate_spills(&mut self, spills: &HashSet<Rc<IrVirtualRegisterVariable>>) {
|
||||
if let Some(ir_expression) = self.value.as_mut() {
|
||||
ir_expression.propagate_spills(spills);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for IrReturn {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "return")?;
|
||||
if let Some(value) = &self.value {
|
||||
write!(f, " {}", value)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
61
dmc-lib/src/ir/ir_statement.rs
Normal file
61
dmc-lib/src/ir/ir_statement.rs
Normal file
@ -0,0 +1,61 @@
|
||||
use crate::ir::ir_assign::IrAssign;
|
||||
use crate::ir::ir_call::IrCall;
|
||||
use crate::ir::ir_return::IrReturn;
|
||||
use crate::ir::ir_variable::IrVirtualRegisterVariable;
|
||||
use std::collections::HashSet;
|
||||
use std::fmt::{Display, Formatter};
|
||||
use std::rc::Rc;
|
||||
|
||||
pub enum IrStatement {
|
||||
Assign(IrAssign),
|
||||
Call(IrCall),
|
||||
Return(IrReturn),
|
||||
}
|
||||
|
||||
impl IrStatement {
|
||||
pub fn vr_definitions(&self) -> HashSet<Rc<IrVirtualRegisterVariable>> {
|
||||
match self {
|
||||
IrStatement::Assign(ir_assign) => ir_assign.vr_definitions(),
|
||||
IrStatement::Call(_) => HashSet::new(),
|
||||
IrStatement::Return(_) => HashSet::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn vr_uses(&self) -> HashSet<Rc<IrVirtualRegisterVariable>> {
|
||||
match self {
|
||||
IrStatement::Assign(ir_assign) => ir_assign.vr_uses(),
|
||||
IrStatement::Call(ir_call) => ir_call.vr_uses(),
|
||||
IrStatement::Return(ir_return) => ir_return.vr_uses(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn propagate_spills(&mut self, spills: &HashSet<Rc<IrVirtualRegisterVariable>>) {
|
||||
match self {
|
||||
IrStatement::Assign(ir_assign) => {
|
||||
ir_assign.propagate_spills(spills);
|
||||
}
|
||||
IrStatement::Call(ir_call) => {
|
||||
ir_call.propagate_spills(spills);
|
||||
}
|
||||
IrStatement::Return(ir_return) => {
|
||||
ir_return.propagate_spills(spills);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for IrStatement {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
IrStatement::Assign(ir_assign) => {
|
||||
write!(f, "{}", ir_assign)
|
||||
}
|
||||
IrStatement::Call(ir_call) => {
|
||||
write!(f, "{}", ir_call)
|
||||
}
|
||||
IrStatement::Return(ir_return) => {
|
||||
write!(f, "{}", ir_return)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
109
dmc-lib/src/ir/ir_variable.rs
Normal file
109
dmc-lib/src/ir/ir_variable.rs
Normal file
@ -0,0 +1,109 @@
|
||||
use crate::type_info::TypeInfo;
|
||||
use std::fmt::{Debug, Display, Formatter};
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::rc::Rc;
|
||||
|
||||
pub enum IrVariable {
|
||||
VirtualRegister(Rc<IrVirtualRegisterVariable>),
|
||||
Stack(IrStackVariable),
|
||||
}
|
||||
|
||||
impl IrVariable {
|
||||
pub fn new(name: &str, type_info: TypeInfo) -> IrVariable {
|
||||
IrVariable::VirtualRegister(Rc::new(IrVirtualRegisterVariable::new(name, type_info)))
|
||||
}
|
||||
|
||||
pub fn type_info(&self) -> &TypeInfo {
|
||||
match self {
|
||||
IrVariable::VirtualRegister(vr_variable) => vr_variable.type_info(),
|
||||
IrVariable::Stack(stack_variable) => stack_variable.type_info(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for IrVariable {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
IrVariable::VirtualRegister(vr_variable) => {
|
||||
write!(f, "{}", vr_variable)
|
||||
}
|
||||
IrVariable::Stack(stack_variable) => {
|
||||
write!(f, "{}", stack_variable)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct IrVirtualRegisterVariable {
|
||||
name: Rc<str>,
|
||||
type_info: TypeInfo,
|
||||
}
|
||||
|
||||
impl IrVirtualRegisterVariable {
|
||||
pub fn new(name: &str, type_info: TypeInfo) -> Self {
|
||||
Self {
|
||||
name: name.into(),
|
||||
type_info,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
|
||||
pub fn type_info(&self) -> &TypeInfo {
|
||||
&self.type_info
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for IrVirtualRegisterVariable {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.name)
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for IrVirtualRegisterVariable {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.name)
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for IrVirtualRegisterVariable {}
|
||||
|
||||
impl PartialEq for IrVirtualRegisterVariable {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.name == other.name
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash for IrVirtualRegisterVariable {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.name.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct IrStackVariable {
|
||||
name: Rc<str>,
|
||||
type_info: TypeInfo,
|
||||
offset: Option<isize>,
|
||||
}
|
||||
|
||||
impl IrStackVariable {
|
||||
pub fn new(name: &str, type_info: TypeInfo) -> Self {
|
||||
Self {
|
||||
name: name.into(),
|
||||
type_info,
|
||||
offset: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn type_info(&self) -> &TypeInfo {
|
||||
&self.type_info
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for IrStackVariable {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.name)
|
||||
}
|
||||
}
|
||||
11
dmc-lib/src/ir/mod.rs
Normal file
11
dmc-lib/src/ir/mod.rs
Normal file
@ -0,0 +1,11 @@
|
||||
pub mod ir_add;
|
||||
pub mod ir_assign;
|
||||
pub mod ir_block;
|
||||
pub mod ir_call;
|
||||
pub mod ir_expression;
|
||||
pub mod ir_function;
|
||||
pub mod ir_operation;
|
||||
pub mod ir_parameter;
|
||||
pub mod ir_return;
|
||||
pub mod ir_statement;
|
||||
pub mod ir_variable;
|
||||
@ -2,6 +2,7 @@ pub mod asm;
|
||||
pub mod ast;
|
||||
pub mod constants_table;
|
||||
pub mod diagnostic;
|
||||
pub mod ir;
|
||||
pub mod lexer;
|
||||
pub mod parser;
|
||||
pub mod scope;
|
||||
|
||||
@ -1,3 +1,6 @@
|
||||
use crate::ir::ir_expression::IrExpression;
|
||||
use crate::ir::ir_parameter::IrParameter;
|
||||
use crate::ir::ir_variable::{IrVariable, IrVirtualRegisterVariable};
|
||||
use crate::type_info::TypeInfo;
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
@ -47,6 +50,9 @@ impl FunctionSymbol {
|
||||
pub struct ParameterSymbol {
|
||||
name: Rc<str>,
|
||||
type_info: TypeInfo,
|
||||
ir_parameter: Option<Rc<IrParameter>>,
|
||||
|
||||
#[deprecated]
|
||||
stack_frame_offset: Option<isize>,
|
||||
}
|
||||
|
||||
@ -55,6 +61,7 @@ impl ParameterSymbol {
|
||||
Self {
|
||||
name: name.into(),
|
||||
type_info,
|
||||
ir_parameter: None,
|
||||
stack_frame_offset: None,
|
||||
}
|
||||
}
|
||||
@ -71,10 +78,20 @@ impl ParameterSymbol {
|
||||
&self.type_info
|
||||
}
|
||||
|
||||
pub fn set_ir_parameter(&mut self, ir_parameter: Rc<IrParameter>) {
|
||||
self.ir_parameter = Some(ir_parameter);
|
||||
}
|
||||
|
||||
pub fn ir_parameter(&self) -> &Rc<IrParameter> {
|
||||
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()
|
||||
}
|
||||
@ -83,6 +100,9 @@ impl ParameterSymbol {
|
||||
pub struct VariableSymbol {
|
||||
name: Rc<str>,
|
||||
type_info: Option<TypeInfo>,
|
||||
vr_variable: Option<Rc<IrVirtualRegisterVariable>>,
|
||||
|
||||
#[deprecated]
|
||||
register: Option<usize>,
|
||||
}
|
||||
|
||||
@ -91,6 +111,7 @@ impl VariableSymbol {
|
||||
Self {
|
||||
name: name.into(),
|
||||
type_info: None,
|
||||
vr_variable: None,
|
||||
register: None,
|
||||
}
|
||||
}
|
||||
@ -113,6 +134,16 @@ impl VariableSymbol {
|
||||
.expect("TypeInfo not initialized. Did you type check?")
|
||||
}
|
||||
|
||||
pub fn set_vr_variable(&mut self, ir_variable: Rc<IrVirtualRegisterVariable>) {
|
||||
self.vr_variable = Some(ir_variable);
|
||||
}
|
||||
|
||||
pub fn vr_variable(&self) -> &Rc<IrVirtualRegisterVariable> {
|
||||
self.vr_variable
|
||||
.as_ref()
|
||||
.expect("ir_variable not yet initialized")
|
||||
}
|
||||
|
||||
pub fn set_register(&mut self, register: usize) {
|
||||
self.register = Some(register);
|
||||
}
|
||||
@ -127,3 +158,19 @@ pub enum ExpressibleSymbol {
|
||||
Parameter(Rc<RefCell<ParameterSymbol>>),
|
||||
Variable(Rc<RefCell<VariableSymbol>>),
|
||||
}
|
||||
|
||||
impl ExpressibleSymbol {
|
||||
pub fn ir_expression(&self) -> IrExpression {
|
||||
match self {
|
||||
ExpressibleSymbol::Function(_) => {
|
||||
panic!("Cannot get ir_variable for FunctionSymbol");
|
||||
}
|
||||
ExpressibleSymbol::Parameter(parameter_symbol) => {
|
||||
IrExpression::Parameter(parameter_symbol.borrow().ir_parameter().clone())
|
||||
}
|
||||
ExpressibleSymbol::Variable(variable_symbol) => IrExpression::Variable(
|
||||
IrVariable::VirtualRegister(variable_symbol.borrow().vr_variable().clone()),
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
9
examples/add_strings.dm
Normal file
9
examples/add_strings.dm
Normal file
@ -0,0 +1,9 @@
|
||||
extern fn println(message: Any) -> Void
|
||||
|
||||
fn addStrings(a: String, b: String) -> String
|
||||
a + " " + b
|
||||
end
|
||||
|
||||
fn main()
|
||||
println(addStrings("Hello", "World"))
|
||||
end
|
||||
@ -8,4 +8,5 @@ fn main()
|
||||
let y = add(1, 2)
|
||||
println(y)
|
||||
println(add(3, add(4, 5)))
|
||||
println(y)
|
||||
end
|
||||
8
examples/register_alloc_test.dm
Normal file
8
examples/register_alloc_test.dm
Normal file
@ -0,0 +1,8 @@
|
||||
extern fn println(message: Any) -> Void
|
||||
|
||||
fn main()
|
||||
let a = 1
|
||||
let b = 2
|
||||
let c = 3
|
||||
let x = a + b + c
|
||||
end
|
||||
Loading…
Reference in New Issue
Block a user