Files
damn_simple_architecture/compiler/src/backend/dsa/variable.rs
T
2026-02-10 16:37:33 +00:00

187 lines
4.8 KiB
Rust

use std::{collections::HashMap, hash::Hash};
use crate::{
backend::dsa::{
instruction::{InsBlock, Reg},
registers::Register,
},
model::{CompilerError, TypeId},
};
pub struct Variable {
name: String,
/// the type of the variable.
r#type: TypeId,
/// size taken up in bytes.
/// if size > 4, value must be stored on the stack.
size: usize,
// location
/// this must be None if it cannot be stored in a register.
allocated_register: Option<Register>,
/// represents the offset from the base pointer (Bpr) of the stack frame.
/// needs to be signed as offset is positive for function args and negative for local
/// variables. as we can't access values at negative offsets, we use the following
/// formula: addr = Spr + offset - (Spr - Bpr) where we know (Spr-Bpr) at compile
/// time.
bpr_offset: Option<isize>,
}
impl Variable {
pub fn new_uninit(name: String, r#type: TypeId) -> Self {
Self {
name,
size: r#type.size(),
r#type,
allocated_register: None,
bpr_offset: None,
}
}
fn alloc_register(
&mut self,
scope: &'_ mut Scope,
) -> Result<Register, CompilerError> {
if self.size > 4 {
return Err(CompilerError::Generic(format!(
"Type {} cannot be allocated a register as it has a size of {} bytes",
self.r#type, self.size
)));
}
todo!("integrate with register alloc logic")
// self.allocated_register = Some(...)
}
fn alloc_stack(&mut self, scope: &'_ mut Scope) -> Result<usize, CompilerError> {
todo!("integrate with stack alloc logic")
// self.bpr_offset = Some(...)
}
pub fn load(&mut self, scope: &'_ mut Scope) -> Result<Register, CompilerError> {
todo!("load var from stack to reg (if possible)")
}
pub fn new_local(
name: String,
r#type: TypeId,
scope: &'_ mut Scope,
) -> Result<Self, CompilerError> {
let mut var = Self::new_uninit(name, r#type);
var.alloc_register(scope)?;
Ok(var)
}
pub fn new_stack(
name: String,
r#type: TypeId,
scope: &'_ mut Scope,
) -> Result<Self, CompilerError> {
let mut init = Self::new_uninit(name, r#type);
init.alloc_stack(scope)?;
Ok(init)
}
pub fn drop(&mut self, scope: &'_ mut Scope) -> Result<(), CompilerError> {
if let Some(reg) = self.allocated_register {
todo!("dealloc reg in current function")
}
if let Some(offset) = self.bpr_offset {
todo!("free stack slot in current function")
}
Ok(())
}
pub fn spill(&mut self, scope: &'_ mut Scope) -> Result<(), CompilerError> {
todo!()
}
}
/// scope object
pub struct Scope<'a> {
/// outer scope, for a function this will be the global scope.
parent: Option<&'a mut Scope<'a>>,
/// is the scope a function body or just a loop?
/// depending on the type, ending a scope will have different behaviour
r#type: ScopeType,
/// variables
variables: HashMap<String, Variable>,
/// tells us if a given register is being used or not.
/// this can be an array as registers have u8 representation.
in_use: [(Register, bool); 16],
stack_offset: i32,
}
impl<'a> Scope<'a> {
pub fn new(parent: &'a mut Scope<'a>, r#type: ScopeType) -> Scope<'a> {
Self {
stack_offset: parent.stack_offset,
parent: Some(parent),
r#type,
variables: HashMap::new(),
in_use: Register::get_gp().map(|reg| (reg, false)),
}
}
pub fn stack_offset(&self) -> i32 {
self.stack_offset
}
pub fn stack_offset_mut(&mut self) -> &mut i32 {
&mut self.stack_offset
}
pub fn close(&mut self) -> Result<(), CompilerError> {
// closing a scope means we need to drop all variables in scope and free registers.
for (name, var) in self.variables {
if let Some(reg) = var.allocated_register {
}
if let Some(off)
}
for reg in self.in_use
Ok(())
}
pub fn alloc_temp_reg(&mut self) -> Result<(Register, InsBlock), CompilerError> {
todo!()
}
pub fn alloc_var_reg(&mut self) -> Result<(Register, InsBlock), CompilerError> {
todo!()
}
pub fn alloc_var_stack(&mut self) -> Result<(Register, InsBlock), CompilerError> {
todo!()
}
pub fn free_var_stack(&mut self) -> Result<(Register, InsBlock), CompilerError> {
todo!()
}
pub fn free_temp_reg(&mut self) -> Result<(Register, InsBlock), CompilerError> {
todo!()
}
}
#[derive(PartialEq, Copy, Clone, Debug)]
enum ScopeType {
Function,
IfBlock,
LoopBlock,
}