Repl with let statements mostly working.
This commit is contained in:
parent
655a0288d3
commit
c5781114a5
@ -4,6 +4,7 @@ mod run;
|
||||
use crate::repl::repl;
|
||||
use crate::run::compile_and_run_script;
|
||||
use clap::{Parser, Subcommand};
|
||||
use std::io;
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
@ -23,29 +24,27 @@ enum SubCommand {
|
||||
|
||||
#[arg(long)]
|
||||
show_ir: bool,
|
||||
|
||||
#[arg(long, default_value = "8")]
|
||||
register_count: usize,
|
||||
},
|
||||
Repl {
|
||||
#[arg(long, default_value = "8")]
|
||||
register_count: usize,
|
||||
},
|
||||
Repl,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let register_count = std::env::var("DVM_REGISTER_COUNT")
|
||||
.map(|v| v.parse::<usize>())
|
||||
.unwrap_or(Ok(8))
|
||||
.unwrap();
|
||||
|
||||
let args = Cli::parse();
|
||||
match &args.sub_command {
|
||||
SubCommand::Run {
|
||||
script,
|
||||
show_asm,
|
||||
show_ir,
|
||||
register_count,
|
||||
} => {
|
||||
compile_and_run_script(script, *show_asm, *show_ir, *register_count);
|
||||
compile_and_run_script(script, *show_asm, *show_ir, register_count);
|
||||
}
|
||||
SubCommand::Repl { register_count } => {
|
||||
repl(*register_count);
|
||||
SubCommand::Repl => {
|
||||
repl(&mut io::stdin().lock(), register_count);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
206
dm/src/repl.rs
206
dm/src/repl.rs
@ -4,28 +4,44 @@ use dmc_lib::diagnostic::Diagnostic;
|
||||
use dmc_lib::ir::ir_function::IrFunction;
|
||||
use dmc_lib::ir::ir_return::IrReturn;
|
||||
use dmc_lib::ir::ir_statement::IrStatement;
|
||||
use dmc_lib::ir::ir_variable::IrVariable;
|
||||
use dmc_lib::lexer::Lexer;
|
||||
use dmc_lib::parser::parse_expression;
|
||||
use dmc_lib::offset_counter::OffsetCounter;
|
||||
use dmc_lib::parser::{parse_expression, parse_let_statement};
|
||||
use dmc_lib::symbol::variable_symbol::VariableSymbol;
|
||||
use dmc_lib::symbol_table::SymbolTable;
|
||||
use dmc_lib::token::TokenKind;
|
||||
use dmc_lib::type_info::TypeInfo;
|
||||
use dmc_lib::types_table::TypesTable;
|
||||
use dvm_lib::vm::constant::{Constant, StringConstant};
|
||||
use dvm_lib::vm::function::Function;
|
||||
use dvm_lib::vm::operand::Operand;
|
||||
use dvm_lib::vm::{CallStack, DvmContext, call};
|
||||
use dvm_lib::vm::{CallStack, DvmContext, loop_instructions, prepare_for_instruction_loop};
|
||||
use std::cell::RefCell;
|
||||
use std::collections::HashMap;
|
||||
use std::io;
|
||||
use std::io::Write;
|
||||
use std::io::{BufRead, Write};
|
||||
use std::rc::Rc;
|
||||
|
||||
pub fn repl(register_count: usize) {
|
||||
pub fn repl(read: &mut impl BufRead, register_count: usize) {
|
||||
let mut buffer = String::new();
|
||||
|
||||
let mut symbol_table = SymbolTable::new();
|
||||
let mut types_table = TypesTable::new();
|
||||
|
||||
let mut repl_fn_body_scope_id: Option<usize> = None;
|
||||
let mut repl_fn_offset_counter = OffsetCounter::new();
|
||||
let mut repl_fn_local_variables = HashMap::new();
|
||||
|
||||
let mut constants_table = ConstantsTable::new();
|
||||
let mut context = DvmContext::new();
|
||||
|
||||
let mut repl_fn_stack_locals: Vec<Operand> = vec![];
|
||||
|
||||
'repl: loop {
|
||||
print!("> ");
|
||||
io::stdout().flush().unwrap();
|
||||
io::stdin().read_line(&mut buffer).unwrap();
|
||||
read.read_line(&mut buffer).unwrap();
|
||||
let input = buffer.trim();
|
||||
if input.is_empty() {
|
||||
buffer.clear();
|
||||
@ -52,9 +68,40 @@ pub fn repl(register_count: usize) {
|
||||
todo!("Parse functions in repl")
|
||||
}
|
||||
TokenKind::Let => {
|
||||
todo!("Parse let statements in repl")
|
||||
match compile_let_statement(
|
||||
input,
|
||||
register_count,
|
||||
&mut symbol_table,
|
||||
&mut repl_fn_body_scope_id,
|
||||
&mut repl_fn_offset_counter,
|
||||
&mut repl_fn_local_variables,
|
||||
&mut types_table,
|
||||
&mut constants_table,
|
||||
) {
|
||||
Ok(function) => {
|
||||
context
|
||||
.functions_mut()
|
||||
.insert(function.name_owned(), function);
|
||||
}
|
||||
Err(diagnostics) => {
|
||||
for diagnostic in diagnostics {
|
||||
eprintln!("{}", diagnostic.message());
|
||||
}
|
||||
buffer.clear();
|
||||
continue 'repl;
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => match compile_expression(input, register_count, &mut constants_table) {
|
||||
_ => match compile_expression(
|
||||
input,
|
||||
register_count,
|
||||
&mut symbol_table,
|
||||
&mut types_table,
|
||||
&mut repl_fn_body_scope_id,
|
||||
&mut repl_fn_local_variables,
|
||||
&mut repl_fn_offset_counter,
|
||||
&mut constants_table,
|
||||
) {
|
||||
Ok(function) => {
|
||||
context
|
||||
.functions_mut()
|
||||
@ -63,9 +110,9 @@ pub fn repl(register_count: usize) {
|
||||
Err(diagnostics) => {
|
||||
for diagnostic in &diagnostics {
|
||||
eprintln!("{}", diagnostic.message());
|
||||
buffer.clear();
|
||||
continue 'repl;
|
||||
}
|
||||
buffer.clear();
|
||||
continue 'repl;
|
||||
}
|
||||
},
|
||||
}
|
||||
@ -77,10 +124,25 @@ pub fn repl(register_count: usize) {
|
||||
);
|
||||
}
|
||||
|
||||
let mut registers = vec![Operand::Null; register_count];
|
||||
let mut call_stack = CallStack::new();
|
||||
prepare_for_instruction_loop(&context, "__repl", &mut call_stack, &[]);
|
||||
|
||||
// copy all old locals to current call frame's stack
|
||||
// this has to be done with indexing because the preparation above creates space for them
|
||||
for (i, operand) in repl_fn_stack_locals.iter().enumerate() {
|
||||
let target_index = call_stack.top().fp() + i;
|
||||
call_stack.top_mut().stack_mut()[target_index] = operand.clone();
|
||||
}
|
||||
|
||||
let result = loop_instructions(
|
||||
&context,
|
||||
&mut vec![Operand::Null; register_count],
|
||||
&mut call_stack,
|
||||
);
|
||||
|
||||
// copy the top frame's stack locals back to OUR stack locals for next iteration
|
||||
repl_fn_stack_locals = std::mem::take(call_stack.top_mut().stack_mut());
|
||||
|
||||
let result = call(&context, &mut registers, &mut call_stack, "__repl", &[]);
|
||||
if let Some(value) = result {
|
||||
println!("{}", value);
|
||||
}
|
||||
@ -89,35 +151,58 @@ pub fn repl(register_count: usize) {
|
||||
}
|
||||
}
|
||||
|
||||
fn prepare_scopes(symbol_table: &mut SymbolTable, fn_body_scope_id: &mut Option<usize>) -> usize {
|
||||
if let Some(scope_id) = fn_body_scope_id {
|
||||
symbol_table.change_scope(*scope_id);
|
||||
*scope_id
|
||||
} else {
|
||||
symbol_table.push_module_scope("__repl_module");
|
||||
symbol_table.push_function_scope("__repl_fn");
|
||||
let container_scope_id = symbol_table.push_block_scope("__repl_fn_body");
|
||||
fn_body_scope_id.replace(container_scope_id);
|
||||
container_scope_id
|
||||
}
|
||||
}
|
||||
|
||||
fn compile_expression(
|
||||
input: &str,
|
||||
register_count: usize,
|
||||
symbol_table: &mut SymbolTable,
|
||||
types_table: &mut TypesTable,
|
||||
fn_body_scope_id: &mut Option<usize>,
|
||||
fn_local_variables: &HashMap<Rc<VariableSymbol>, Rc<RefCell<IrVariable>>>,
|
||||
offset_counter: &mut OffsetCounter,
|
||||
constants_table: &mut ConstantsTable,
|
||||
) -> Result<Function, Vec<Diagnostic>> {
|
||||
let parse_result = parse_expression(input);
|
||||
let mut expression = parse_result?;
|
||||
// parse
|
||||
let mut expression = parse_expression(input)?;
|
||||
|
||||
let mut symbol_table = SymbolTable::new();
|
||||
let mut types_table = TypesTable::new();
|
||||
// init scopes, if necessary
|
||||
let container_scope = prepare_scopes(symbol_table, fn_body_scope_id);
|
||||
|
||||
// "fake" scopes
|
||||
symbol_table.push_module_scope("__repl_module");
|
||||
let function_scope_id = symbol_table.push_function_scope("__repl_function");
|
||||
|
||||
expression.init_scopes(&mut symbol_table, function_scope_id);
|
||||
|
||||
symbol_table.pop_scope(); // function
|
||||
symbol_table.pop_scope(); // module
|
||||
// inner scopes
|
||||
expression.init_scopes(symbol_table, container_scope);
|
||||
|
||||
// names
|
||||
let diagnostics = expression.check_static_fn_local_names(&symbol_table);
|
||||
if !diagnostics.is_empty() {
|
||||
return Err(diagnostics);
|
||||
}
|
||||
|
||||
expression.type_check(&symbol_table, &mut types_table)?;
|
||||
// type check
|
||||
expression.type_check(&symbol_table, types_table)?;
|
||||
|
||||
// synthesize a function
|
||||
// init ir_builder
|
||||
let mut ir_builder = IrBuilder::new();
|
||||
|
||||
// copy all previous declared variables to here so we preserve their stack offsets
|
||||
for (key, value) in fn_local_variables {
|
||||
ir_builder
|
||||
.local_variables_mut()
|
||||
.insert(key.clone(), value.clone());
|
||||
}
|
||||
|
||||
let entry_block_id = ir_builder.new_block();
|
||||
|
||||
let maybe_ir_expression =
|
||||
@ -138,6 +223,75 @@ fn compile_expression(
|
||||
entry_block.clone(),
|
||||
);
|
||||
|
||||
let (_, stack_size) = ir_function.assign_registers(register_count);
|
||||
Ok(ir_function.assemble(stack_size, constants_table))
|
||||
// spilled registers are put on the stack, so we need to add it to our stack size
|
||||
ir_function.assign_registers(register_count, offset_counter);
|
||||
|
||||
Ok(ir_function.assemble(offset_counter.get_count(), constants_table))
|
||||
}
|
||||
|
||||
fn compile_let_statement(
|
||||
input: &str,
|
||||
register_count: usize,
|
||||
symbol_table: &mut SymbolTable,
|
||||
body_scope_id: &mut Option<usize>,
|
||||
offset_counter: &mut OffsetCounter,
|
||||
local_variables: &mut HashMap<Rc<VariableSymbol>, Rc<RefCell<IrVariable>>>,
|
||||
types_table: &mut TypesTable,
|
||||
constants_table: &mut ConstantsTable,
|
||||
) -> Result<Function, Vec<Diagnostic>> {
|
||||
// parse
|
||||
let mut let_statement = parse_let_statement(input)?;
|
||||
|
||||
// names
|
||||
let container_scope_id = prepare_scopes(symbol_table, body_scope_id);
|
||||
|
||||
let_statement.init_scopes(symbol_table, container_scope_id);
|
||||
|
||||
let name_diagnostics = let_statement.analyze_static_fn_local_names(symbol_table);
|
||||
if !name_diagnostics.is_empty() {
|
||||
return Err(name_diagnostics);
|
||||
}
|
||||
|
||||
// types
|
||||
let_statement.type_check(&symbol_table, types_table)?;
|
||||
|
||||
// init the ir builder
|
||||
let mut ir_builder = IrBuilder::new();
|
||||
|
||||
// put previous locals in ir_builder so expressions can find them
|
||||
for (k, v) in local_variables.iter() {
|
||||
ir_builder
|
||||
.local_variables_mut()
|
||||
.insert(k.clone(), v.clone());
|
||||
}
|
||||
|
||||
// ir function
|
||||
let entry_block_id = ir_builder.new_block();
|
||||
|
||||
let destination_ir_variable = let_statement.to_repl_ir(
|
||||
&mut ir_builder,
|
||||
symbol_table,
|
||||
types_table,
|
||||
offset_counter.next() as isize,
|
||||
); // put it on top
|
||||
|
||||
// Now that we've translated to ir, we can add the new local variable in the IrBuilder to our
|
||||
// record of them to be used for next loop iteration.
|
||||
let variable_symbol = let_statement.get_destination_symbol(symbol_table);
|
||||
local_variables.insert(variable_symbol, destination_ir_variable);
|
||||
|
||||
ir_builder.finish_block();
|
||||
let entry_block = ir_builder.get_block(entry_block_id);
|
||||
let mut ir_function = IrFunction::new(
|
||||
"__repl".into(),
|
||||
vec![],
|
||||
&TypeInfo::Void,
|
||||
entry_block.clone(),
|
||||
);
|
||||
|
||||
// By here, the variables should all be assigned to their new or existing stack slots.
|
||||
// Register allocation should only be required for temp variables.
|
||||
ir_function.assign_registers(register_count, offset_counter);
|
||||
|
||||
Ok(ir_function.assemble(offset_counter.get_count(), constants_table))
|
||||
}
|
||||
|
||||
@ -6,13 +6,13 @@ use dm_std_lib::add_all_std_core;
|
||||
use dmc_lib::constants_table::ConstantsTable;
|
||||
use dmc_lib::diagnostic::Diagnostic;
|
||||
use dmc_lib::intrinsics::{insert_intrinsic_symbols, insert_intrinsic_types};
|
||||
use dmc_lib::offset_counter::OffsetCounter;
|
||||
use dmc_lib::parser::parse_compilation_unit;
|
||||
use dmc_lib::symbol_table::SymbolTable;
|
||||
use dmc_lib::types_table::TypesTable;
|
||||
use dvm_lib::vm::constant::{Constant, StringConstant};
|
||||
use dvm_lib::vm::function::Function;
|
||||
use dvm_lib::vm::operand::Operand;
|
||||
use dvm_lib::vm::{CallStack, DvmContext, call};
|
||||
use dvm_lib::vm::{DvmContext, call};
|
||||
use std::path::PathBuf;
|
||||
use std::rc::Rc;
|
||||
|
||||
@ -67,8 +67,9 @@ fn run(
|
||||
let mut constants_table = ConstantsTable::new();
|
||||
|
||||
for ir_function in &mut ir_functions {
|
||||
let (_, stack_size) = ir_function.assign_registers(register_count);
|
||||
let function = ir_function.assemble(stack_size, &mut constants_table);
|
||||
let mut offset_counter = OffsetCounter::new();
|
||||
ir_function.assign_registers(register_count, &mut offset_counter);
|
||||
let function = ir_function.assemble(offset_counter.get_count(), &mut constants_table);
|
||||
functions.push(function);
|
||||
}
|
||||
|
||||
@ -107,16 +108,8 @@ fn run(
|
||||
);
|
||||
}
|
||||
|
||||
let mut registers: Vec<Operand> = vec![Operand::Null; register_count];
|
||||
let mut call_stack = CallStack::new();
|
||||
let result = call(&dvm_context, "main", &vec![], register_count);
|
||||
|
||||
let result = call(
|
||||
&dvm_context,
|
||||
&mut registers,
|
||||
&mut call_stack,
|
||||
"main",
|
||||
&vec![],
|
||||
);
|
||||
if let Some(value) = result {
|
||||
println!("{}", value);
|
||||
}
|
||||
|
||||
@ -10,6 +10,7 @@ use crate::symbol::Symbol;
|
||||
use crate::symbol::class_symbol::ClassSymbol;
|
||||
use crate::symbol::variable_symbol::VariableSymbol;
|
||||
use crate::symbol_table::SymbolTable;
|
||||
use crate::type_info::TypeInfo;
|
||||
use crate::types_table::TypesTable;
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
@ -55,6 +56,10 @@ impl LetStatement {
|
||||
self.initializer.init_scopes(symbol_table, container_scope);
|
||||
}
|
||||
|
||||
pub fn scope_id(&self) -> usize {
|
||||
self.scope_id.unwrap()
|
||||
}
|
||||
|
||||
fn make_and_insert_variable_symbol(
|
||||
&self,
|
||||
symbol_table: &mut SymbolTable,
|
||||
@ -131,6 +136,7 @@ impl LetStatement {
|
||||
types_table: &mut TypesTable,
|
||||
) -> Result<(), Vec<Diagnostic>> {
|
||||
self.initializer.type_check(symbol_table, types_table)?;
|
||||
// TODO: this is wrong. We need to check assignability
|
||||
let initializer_type_info = self
|
||||
.initializer
|
||||
.type_info(symbol_table, types_table)
|
||||
@ -144,6 +150,34 @@ impl LetStatement {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn make_vr_variable(&self, builder: &mut IrBuilder, destination_type: &TypeInfo) -> IrVariable {
|
||||
IrVariable::new_vr(
|
||||
self.declared_name().into(),
|
||||
builder.current_block().id(),
|
||||
destination_type,
|
||||
)
|
||||
}
|
||||
|
||||
fn make_stack_variable(
|
||||
&self,
|
||||
builder: &mut IrBuilder,
|
||||
destination_type: &TypeInfo,
|
||||
offset: isize,
|
||||
) -> IrVariable {
|
||||
IrVariable::new_stack_with_offset(
|
||||
self.declared_name().into(),
|
||||
builder.current_block().id(),
|
||||
destination_type,
|
||||
offset,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn get_destination_symbol(&self, symbol_table: &SymbolTable) -> Rc<VariableSymbol> {
|
||||
symbol_table
|
||||
.get_variable_symbol_owned(self.scope_id.unwrap(), &self.declared_name)
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
pub fn to_ir(
|
||||
&self,
|
||||
builder: &mut IrBuilder,
|
||||
@ -154,20 +188,14 @@ impl LetStatement {
|
||||
.initializer
|
||||
.to_ir_operation(builder, symbol_table, types_table);
|
||||
|
||||
let destination_symbol = symbol_table
|
||||
.get_variable_symbol_owned(self.scope_id.unwrap(), &self.declared_name)
|
||||
.unwrap();
|
||||
let destination_symbol = self.get_destination_symbol(symbol_table);
|
||||
|
||||
let destination_type = types_table
|
||||
.variable_types()
|
||||
.get(&destination_symbol)
|
||||
.unwrap();
|
||||
|
||||
let destination_vr_variable = IrVariable::new_vr(
|
||||
self.declared_name().into(),
|
||||
builder.current_block().id(),
|
||||
destination_type,
|
||||
);
|
||||
let destination_vr_variable = self.make_vr_variable(builder, destination_type);
|
||||
|
||||
let as_rc = Rc::new(RefCell::new(destination_vr_variable));
|
||||
let ir_assign = IrAssign::new(as_rc.clone(), init_operation);
|
||||
@ -180,4 +208,30 @@ impl LetStatement {
|
||||
.current_block_mut()
|
||||
.add_statement(IrStatement::Assign(ir_assign));
|
||||
}
|
||||
|
||||
pub fn to_repl_ir(
|
||||
&self,
|
||||
builder: &mut IrBuilder,
|
||||
symbol_table: &SymbolTable,
|
||||
types_table: &TypesTable,
|
||||
destination_stack_offset: isize,
|
||||
) -> Rc<RefCell<IrVariable>> {
|
||||
let init_operation = self
|
||||
.initializer
|
||||
.to_ir_operation(builder, symbol_table, types_table);
|
||||
let destination_symbol = self.get_destination_symbol(symbol_table);
|
||||
let destination_type = types_table
|
||||
.variable_types()
|
||||
.get(&destination_symbol)
|
||||
.unwrap();
|
||||
let destination_stack_variable =
|
||||
self.make_stack_variable(builder, destination_type, destination_stack_offset);
|
||||
let as_rc = Rc::new(RefCell::new(destination_stack_variable));
|
||||
let ir_assign = IrAssign::new(as_rc.clone(), init_operation);
|
||||
// do not need to save variable to builder as a new one is created for each repl function
|
||||
builder
|
||||
.current_block_mut()
|
||||
.add_statement(IrStatement::Assign(ir_assign));
|
||||
as_rc
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,8 +2,9 @@ use crate::constants_table::ConstantsTable;
|
||||
use crate::ir::assemble::{Assemble, InstructionsBuilder};
|
||||
use crate::ir::ir_operation::IrOperation;
|
||||
use crate::ir::ir_variable::{IrVariable, IrVariableDescriptor, IrVrVariableDescriptor};
|
||||
use crate::ir::register_allocation::{OffsetCounter, VrUser};
|
||||
use crate::ir::register_allocation::VrUser;
|
||||
use crate::ir::util::propagate_spills;
|
||||
use crate::offset_counter::OffsetCounter;
|
||||
use dvm_lib::instruction::Instruction;
|
||||
use std::cell::RefCell;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
@ -57,7 +58,7 @@ impl VrUser for IrAssign {
|
||||
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());
|
||||
stack_variable.use_next_offset(counter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,7 +2,8 @@ use crate::constants_table::ConstantsTable;
|
||||
use crate::ir::assemble::{Assemble, InstructionsBuilder};
|
||||
use crate::ir::ir_statement::IrStatement;
|
||||
use crate::ir::ir_variable::IrVrVariableDescriptor;
|
||||
use crate::ir::register_allocation::{HasVrUsers, OffsetCounter, VrUser};
|
||||
use crate::ir::register_allocation::{HasVrUsers, VrUser};
|
||||
use crate::offset_counter::OffsetCounter;
|
||||
use std::cell::RefCell;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::fmt::{Display, Formatter};
|
||||
@ -122,6 +123,7 @@ impl Display for IrBlock {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::diagnostic::Diagnostic;
|
||||
use crate::offset_counter::OffsetCounter;
|
||||
use crate::parser::parse_compilation_unit;
|
||||
use crate::symbol_table::SymbolTable;
|
||||
use crate::types_table::TypesTable;
|
||||
@ -155,7 +157,7 @@ mod tests {
|
||||
.unwrap();
|
||||
|
||||
let mut main_ir = main.to_ir(&symbol_table, &types_table, None);
|
||||
let (register_assignments, _) = main_ir.assign_registers(2);
|
||||
let register_assignments = main_ir.assign_registers(2, &mut OffsetCounter::new());
|
||||
assert_eq!(register_assignments.len(), 4);
|
||||
|
||||
Ok(())
|
||||
|
||||
@ -4,6 +4,7 @@ use crate::ir::ir_block::IrBlock;
|
||||
use crate::ir::ir_parameter::IrParameter;
|
||||
use crate::ir::ir_variable::IrVrVariableDescriptor;
|
||||
use crate::ir::register_allocation::HasVrUsers;
|
||||
use crate::offset_counter::OffsetCounter;
|
||||
use crate::type_info::TypeInfo;
|
||||
use dvm_lib::vm::function::Function;
|
||||
use std::cell::RefCell;
|
||||
@ -36,11 +37,14 @@ impl IrFunction {
|
||||
pub fn assign_registers(
|
||||
&mut self,
|
||||
register_count: usize,
|
||||
) -> (HashMap<IrVrVariableDescriptor, usize>, isize) {
|
||||
self.entry.borrow_mut().assign_registers(register_count)
|
||||
offset_counter: &mut OffsetCounter,
|
||||
) -> HashMap<IrVrVariableDescriptor, usize> {
|
||||
self.entry
|
||||
.borrow_mut()
|
||||
.assign_registers(register_count, offset_counter)
|
||||
}
|
||||
|
||||
pub fn assemble(&self, stack_size: isize, constants_table: &mut ConstantsTable) -> Function {
|
||||
pub fn assemble(&self, stack_size: usize, constants_table: &mut ConstantsTable) -> Function {
|
||||
let mut builder = InstructionsBuilder::new();
|
||||
self.entry.borrow().assemble(&mut builder, constants_table);
|
||||
let instructions = builder.take_instructions();
|
||||
|
||||
@ -5,7 +5,8 @@ use crate::ir::ir_call::IrCall;
|
||||
use crate::ir::ir_return::IrReturn;
|
||||
use crate::ir::ir_set_field::IrSetField;
|
||||
use crate::ir::ir_variable::IrVrVariableDescriptor;
|
||||
use crate::ir::register_allocation::{OffsetCounter, VrUser};
|
||||
use crate::ir::register_allocation::VrUser;
|
||||
use crate::offset_counter::OffsetCounter;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::fmt::{Display, Formatter};
|
||||
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
use crate::offset_counter::OffsetCounter;
|
||||
use crate::type_info::TypeInfo;
|
||||
use dvm_lib::instruction::Location;
|
||||
use std::collections::HashSet;
|
||||
@ -20,9 +21,16 @@ impl IrVariable {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_stack(name: Rc<str>, block_id: usize, type_info: TypeInfo) -> Self {
|
||||
pub fn new_stack_with_offset(
|
||||
name: Rc<str>,
|
||||
block_id: usize,
|
||||
type_info: &TypeInfo,
|
||||
offset: isize,
|
||||
) -> Self {
|
||||
let mut descriptor = IrStackVariableDescriptor::new(name, block_id);
|
||||
descriptor.set_offset(offset);
|
||||
Self {
|
||||
descriptor: IrVariableDescriptor::Stack(IrStackVariableDescriptor::new(name, block_id)),
|
||||
descriptor: IrVariableDescriptor::Stack(descriptor),
|
||||
type_info: type_info.clone(),
|
||||
}
|
||||
}
|
||||
@ -177,6 +185,12 @@ impl IrStackVariableDescriptor {
|
||||
self.name.clone()
|
||||
}
|
||||
|
||||
pub fn use_next_offset(&mut self, offset_counter: &mut OffsetCounter) {
|
||||
if self.offset.is_none() {
|
||||
self.offset = Some(offset_counter.next() as isize);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_offset(&mut self, offset: isize) {
|
||||
self.offset = Some(offset);
|
||||
}
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
use crate::ir::ir_variable::IrVrVariableDescriptor;
|
||||
use crate::offset_counter::OffsetCounter;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
pub type InterferenceGraph = HashMap<IrVrVariableDescriptor, HashSet<IrVrVariableDescriptor>>;
|
||||
@ -104,7 +105,8 @@ pub trait HasVrUsers {
|
||||
fn assign_registers(
|
||||
&mut self,
|
||||
register_count: usize,
|
||||
) -> (HashMap<IrVrVariableDescriptor, usize>, isize) {
|
||||
offset_counter: &mut OffsetCounter,
|
||||
) -> HashMap<IrVrVariableDescriptor, usize> {
|
||||
let mut spills: HashSet<IrVrVariableDescriptor> = HashSet::new();
|
||||
loop {
|
||||
let mut interference_graph = self.interference_graph();
|
||||
@ -123,12 +125,11 @@ pub trait HasVrUsers {
|
||||
vr_user.propagate_register_assignments(®isters);
|
||||
}
|
||||
// also set offsets
|
||||
let mut offset_counter = OffsetCounter::new();
|
||||
for vr_user in self.vr_users_mut() {
|
||||
vr_user.propagate_stack_offsets(&mut offset_counter);
|
||||
vr_user.propagate_stack_offsets(offset_counter);
|
||||
}
|
||||
|
||||
return (registers, offset_counter.get_count());
|
||||
return registers;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -157,26 +158,6 @@ pub trait VrUser {
|
||||
}
|
||||
}
|
||||
|
||||
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)]
|
||||
struct WorkItem {
|
||||
vr: IrVrVariableDescriptor,
|
||||
|
||||
@ -5,6 +5,7 @@ pub mod error_codes;
|
||||
pub mod intrinsics;
|
||||
pub mod ir;
|
||||
pub mod lexer;
|
||||
pub mod offset_counter;
|
||||
pub mod parser;
|
||||
pub mod scope;
|
||||
pub mod source_range;
|
||||
|
||||
23
dmc-lib/src/offset_counter.rs
Normal file
23
dmc-lib/src/offset_counter.rs
Normal file
@ -0,0 +1,23 @@
|
||||
pub struct OffsetCounter {
|
||||
counter: usize,
|
||||
}
|
||||
|
||||
impl OffsetCounter {
|
||||
pub fn new() -> Self {
|
||||
Self { counter: 0 }
|
||||
}
|
||||
|
||||
pub fn with_base(base: usize) -> Self {
|
||||
Self { counter: base }
|
||||
}
|
||||
|
||||
pub fn next(&mut self) -> usize {
|
||||
let offset = self.counter;
|
||||
self.counter += 1;
|
||||
offset
|
||||
}
|
||||
|
||||
pub fn get_count(&self) -> usize {
|
||||
self.counter
|
||||
}
|
||||
}
|
||||
@ -41,6 +41,12 @@ pub fn parse_expression(input: &str) -> ParseResult<Expression> {
|
||||
parser.expression()
|
||||
}
|
||||
|
||||
pub fn parse_let_statement(input: &str) -> ParseResult<LetStatement> {
|
||||
let mut parser = Parser::new(input);
|
||||
parser.advance()?;
|
||||
parser.let_statement()
|
||||
}
|
||||
|
||||
macro_rules! matches_expression_first {
|
||||
( $token_kind : expr ) => {
|
||||
matches!(
|
||||
|
||||
@ -89,6 +89,10 @@ impl SymbolTable {
|
||||
self.current_scope_id.unwrap()
|
||||
}
|
||||
|
||||
pub fn change_scope(&mut self, scope_id: usize) {
|
||||
self.current_scope_id = Some(scope_id);
|
||||
}
|
||||
|
||||
fn scope(&self, scope_id: usize) -> &Scope {
|
||||
&self.scopes[scope_id]
|
||||
}
|
||||
|
||||
@ -5,7 +5,7 @@ use std::rc::Rc;
|
||||
pub struct Function {
|
||||
name: Rc<str>,
|
||||
parameter_count: usize,
|
||||
stack_size: isize,
|
||||
stack_size: usize,
|
||||
instructions: Vec<Instruction>,
|
||||
}
|
||||
|
||||
@ -13,7 +13,7 @@ impl Function {
|
||||
pub fn new(
|
||||
name: Rc<str>,
|
||||
parameter_count: usize,
|
||||
stack_size: isize,
|
||||
stack_size: usize,
|
||||
instructions: Vec<Instruction>,
|
||||
) -> Self {
|
||||
Self {
|
||||
@ -36,7 +36,7 @@ impl Function {
|
||||
self.parameter_count
|
||||
}
|
||||
|
||||
pub fn stack_size(&self) -> isize {
|
||||
pub fn stack_size(&self) -> usize {
|
||||
self.stack_size
|
||||
}
|
||||
|
||||
|
||||
@ -115,15 +115,15 @@ impl<'a> CallFrame<'a> {
|
||||
self.ip += 1;
|
||||
}
|
||||
|
||||
fn stack(&self) -> &[Operand] {
|
||||
pub fn stack(&self) -> &[Operand] {
|
||||
&self.stack
|
||||
}
|
||||
|
||||
fn stack_mut(&mut self) -> &mut Vec<Operand> {
|
||||
pub fn stack_mut(&mut self) -> &mut Vec<Operand> {
|
||||
&mut self.stack
|
||||
}
|
||||
|
||||
fn fp(&self) -> usize {
|
||||
pub fn fp(&self) -> usize {
|
||||
self.fp
|
||||
}
|
||||
|
||||
@ -163,11 +163,11 @@ impl<'a> CallStack<'a> {
|
||||
self.call_frames.pop().unwrap()
|
||||
}
|
||||
|
||||
fn top(&self) -> &CallFrame<'a> {
|
||||
pub fn top(&self) -> &CallFrame<'a> {
|
||||
&self.call_frames[self.call_frames.len() - 1]
|
||||
}
|
||||
|
||||
fn top_mut(&mut self) -> &mut CallFrame<'a> {
|
||||
pub fn top_mut(&mut self) -> &mut CallFrame<'a> {
|
||||
let call_frames_len = self.call_frames.len();
|
||||
&mut self.call_frames[call_frames_len - 1] // dang borrow checker
|
||||
}
|
||||
@ -190,18 +190,24 @@ impl<'a> CallStack<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn call<'a>(
|
||||
context: &'a DvmContext,
|
||||
registers: &mut Vec<Operand>,
|
||||
call_stack: &mut CallStack<'a>,
|
||||
pub fn call(
|
||||
context: &DvmContext,
|
||||
function_name: &str,
|
||||
arguments: &[Value],
|
||||
register_count: usize,
|
||||
) -> Option<Value> {
|
||||
// check if DVM_DEBUG is enabled
|
||||
let debug = std::env::var("DVM_DEBUG")
|
||||
.map(|s| s.eq("1"))
|
||||
.unwrap_or(false);
|
||||
let mut call_stack = CallStack::new();
|
||||
let mut registers = vec![Operand::Null; register_count];
|
||||
prepare_for_instruction_loop(context, function_name, &mut call_stack, arguments);
|
||||
loop_instructions(context, &mut registers, &mut call_stack)
|
||||
}
|
||||
|
||||
pub fn prepare_for_instruction_loop<'a>(
|
||||
context: &'a DvmContext,
|
||||
function_name: &str,
|
||||
call_stack: &mut CallStack<'a>,
|
||||
arguments: &[Value],
|
||||
) {
|
||||
let function = context
|
||||
.functions
|
||||
.get(function_name)
|
||||
@ -222,12 +228,22 @@ pub fn call<'a>(
|
||||
call_stack.top_mut().set_fp(arguments.len());
|
||||
|
||||
// ensure enough stack space
|
||||
call_stack.top_mut().stack_mut().resize(
|
||||
arguments.len() + (function.stack_size() as usize),
|
||||
Operand::Null,
|
||||
);
|
||||
call_stack
|
||||
.top_mut()
|
||||
.stack_mut()
|
||||
.resize(arguments.len() + function.stack_size(), Operand::Null);
|
||||
}
|
||||
|
||||
pub fn loop_instructions<'a>(
|
||||
context: &'a DvmContext,
|
||||
registers: &mut Vec<Operand>,
|
||||
call_stack: &mut CallStack<'a>,
|
||||
) -> Option<Value> {
|
||||
// check if DVM_DEBUG is enabled
|
||||
let debug = std::env::var("DVM_DEBUG")
|
||||
.map(|s| s.eq("1"))
|
||||
.unwrap_or(false);
|
||||
|
||||
// container for final return value
|
||||
let mut return_value: Option<Value> = None;
|
||||
|
||||
while call_stack.maybe_top().is_some()
|
||||
@ -296,7 +312,7 @@ pub fn call<'a>(
|
||||
call_stack
|
||||
.top_mut()
|
||||
.stack_mut()
|
||||
.resize(arg_count + (function.stack_size() as usize), Operand::Null);
|
||||
.resize(arg_count + function.stack_size(), Operand::Null);
|
||||
|
||||
// set fp for callee frame
|
||||
let callee_frame = call_stack.top_mut();
|
||||
@ -690,6 +706,9 @@ pub fn call<'a>(
|
||||
// callee is the bottommost frame ("main" or whatever was called)
|
||||
// set the return value
|
||||
return_value = callee.take_return_value();
|
||||
// push the frame back on the call stack for further inspection by caller
|
||||
call_stack.push(callee);
|
||||
return return_value;
|
||||
}
|
||||
// do not increment ip of the caller; this was done above BEFORE the call
|
||||
}
|
||||
|
||||
@ -3,15 +3,15 @@ mod e2e_tests {
|
||||
use dmc_lib::constants_table::ConstantsTable;
|
||||
use dmc_lib::diagnostic::Diagnostic;
|
||||
use dmc_lib::intrinsics::{insert_intrinsic_symbols, insert_intrinsic_types};
|
||||
use dmc_lib::offset_counter::OffsetCounter;
|
||||
use dmc_lib::parser::parse_compilation_unit;
|
||||
use dmc_lib::symbol_table::SymbolTable;
|
||||
use dmc_lib::types_table::TypesTable;
|
||||
use dvm_lib::vm::class::Class;
|
||||
use dvm_lib::vm::constant::{Constant, StringConstant};
|
||||
use dvm_lib::vm::function::Function;
|
||||
use dvm_lib::vm::operand::Operand;
|
||||
use dvm_lib::vm::value::Value;
|
||||
use dvm_lib::vm::{CallStack, DvmContext, call};
|
||||
use dvm_lib::vm::{DvmContext, call};
|
||||
use std::rc::Rc;
|
||||
|
||||
const REGISTER_COUNT: usize = 8;
|
||||
@ -56,7 +56,9 @@ mod e2e_tests {
|
||||
let mut constants_table = ConstantsTable::new();
|
||||
|
||||
for ir_function in &mut ir_functions {
|
||||
let (_, stack_size) = ir_function.assign_registers(REGISTER_COUNT);
|
||||
let mut offset_counter = OffsetCounter::new();
|
||||
ir_function.assign_registers(REGISTER_COUNT, &mut offset_counter);
|
||||
let stack_size = offset_counter.get_count();
|
||||
let function = ir_function.assemble(stack_size, &mut constants_table);
|
||||
functions.push(function);
|
||||
}
|
||||
@ -95,16 +97,7 @@ mod e2e_tests {
|
||||
function_name: &str,
|
||||
arguments: &[Value],
|
||||
) -> Option<Value> {
|
||||
let mut registers: Vec<Operand> = vec![Operand::Null; REGISTER_COUNT];
|
||||
let mut call_stack = CallStack::new();
|
||||
|
||||
call(
|
||||
&dvm_context,
|
||||
&mut registers,
|
||||
&mut call_stack,
|
||||
function_name,
|
||||
&arguments,
|
||||
)
|
||||
call(&dvm_context, function_name, &arguments, REGISTER_COUNT)
|
||||
}
|
||||
|
||||
fn assert_result(input: &str, function_name: &str, arguments: &[Value], expected_value: Value) {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user