From 5213a1864d21c5676ec87c0a101d60d7b473840c Mon Sep 17 00:00:00 2001 From: lemolatoon <63438515+lemolatoon@users.noreply.github.com> Date: Wed, 15 Mar 2023 19:49:59 +0900 Subject: [PATCH 1/6] Split register related declarations and impls --- src/analyze/types.rs | 2 +- src/generate.rs | 1 + src/generate/generate.rs | 231 +------------------------------------ src/generate/registers.rs | 233 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 237 insertions(+), 230 deletions(-) create mode 100644 src/generate/registers.rs diff --git a/src/analyze/types.rs b/src/analyze/types.rs index 58bca8d..de4da1f 100644 --- a/src/analyze/types.rs +++ b/src/analyze/types.rs @@ -1,4 +1,4 @@ -use crate::generate::generate::RegSize; +use crate::generate::registers::RegSize; use super::analyze::{Func, Struct}; diff --git a/src/generate.rs b/src/generate.rs index 57e6a9d..f3888a0 100644 --- a/src/generate.rs +++ b/src/generate.rs @@ -1 +1,2 @@ pub mod generate; +pub mod registers; diff --git a/src/generate/generate.rs b/src/generate/generate.rs index 2e9a82c..7211183 100644 --- a/src/generate/generate.rs +++ b/src/generate/generate.rs @@ -16,6 +16,8 @@ use crate::{ unimplemented_err, }; +use super::registers::{RegKind, RegOrLit, RegSize}; + #[derive(Debug, Clone)] pub struct Generator { label: usize, @@ -1230,232 +1232,3 @@ impl Generator { self.label } } - -#[derive(PartialOrd, Ord, PartialEq, Eq, Clone, Copy, Debug)] -pub enum RegOrLit { - Reg(RegKind), - Lit(isize), -} - -impl RegOrLit { - pub fn size_to_reg_name(&self, size: RegSize) -> String { - match size { - RegSize::Byte => self.byte(), - RegSize::Dword => self.dword(), - RegSize::Qword => self.qword(), - } - } - - pub fn byte(&self) -> String { - match self { - RegOrLit::Reg(reg) => reg.byte().to_string(), - RegOrLit::Lit(num) => num.to_string(), - } - } - - pub fn dword(&self) -> String { - match self { - RegOrLit::Reg(reg) => reg.dword().to_string(), - RegOrLit::Lit(num) => num.to_string(), - } - } - - pub fn qword(&self) -> String { - match self { - RegOrLit::Reg(reg) => reg.qword().to_string(), - RegOrLit::Lit(num) => num.to_string(), - } - } -} - -impl ToString for RegOrLit { - fn to_string(&self) -> String { - match self { - RegOrLit::Reg(reg) => reg.to_string(), - RegOrLit::Lit(num) => num.to_string(), - } - } -} - -#[derive(PartialOrd, Ord, PartialEq, Eq, Clone, Copy, Debug)] -pub enum RegKind { - Rax, - Rdi, - Rsi, - Rdx, - Rcx, - Rbp, - Rsp, - R8, - R9, - R10, - R11, - R12, - R13, - R14, - R15, -} - -impl ToString for RegKind { - fn to_string(&self) -> String { - match self { - RegKind::Rax => "rax", - RegKind::Rdi => "rdi", - RegKind::Rsi => "rsi", - RegKind::Rdx => "rdx", - RegKind::Rcx => "rcx", - RegKind::Rbp => "rbp", - RegKind::Rsp => "rsp", - RegKind::R8 => "r8", - RegKind::R9 => "r9", - RegKind::R10 => "r10", - RegKind::R11 => "r11", - RegKind::R12 => "r12", - RegKind::R13 => "r13", - RegKind::R14 => "r14", - RegKind::R15 => "r15", - } - .to_string() - } -} - -impl TryFrom<&str> for RegKind { - type Error = (); - - fn try_from(value: &str) -> Result { - Ok(match value { - "rax" => RegKind::Rax, - "rdi" => RegKind::Rdi, - "rsi" => RegKind::Rsi, - "rdx" => RegKind::Rdx, - "rcx" => RegKind::Rcx, - "r8" => RegKind::R8, - "r9" => RegKind::R9, - _ => return Err(()), - }) - } -} - -#[derive(PartialOrd, Ord, PartialEq, Eq, Clone, Copy, Debug)] -pub enum RegSize { - Byte, - Dword, - Qword, -} - -impl RegSize { - pub const fn try_new(size: usize) -> Option { - Some(match size { - 1 => Self::Byte, - 4 => Self::Dword, - 8 => Self::Qword, - _ => return None, - }) - } - - // clippy complains about this, but it's wrong - #[allow(clippy::missing_const_for_fn)] - pub fn try_new_with_error(size: usize, expr: ConvExpr) -> Result { - Ok(match size { - 1 => Self::Byte, - 4 => Self::Dword, - 8 => Self::Qword, - _ => { - return Err(CompileError::new_type_size_error( - UnexpectedTypeSizeStatus::Expr(expr), - )); - } - }) - } -} - -impl AsRef for RegSize { - fn as_ref(&self) -> &str { - match self { - RegSize::Byte => "BYTE", - RegSize::Dword => "DWORD", - RegSize::Qword => "QWORD", - } - } -} - -impl RegSize { - pub const fn size_to_name(size: usize) -> Option { - match size { - 1 => Some(Self::Byte), - 4 => Some(Self::Dword), - 8 => Some(Self::Qword), - _ => None, - } - } -} - -impl RegKind { - pub const fn size_to_reg_name(&self, size: RegSize) -> &str { - match size { - RegSize::Byte => self.byte(), - RegSize::Dword => self.dword(), - RegSize::Qword => self.qword(), - } - } - - pub const fn byte(&self) -> &str { - match self { - RegKind::Rax => "al", - RegKind::Rdi => "dil", - RegKind::Rsi => "sil", - RegKind::Rdx => "dl", - RegKind::Rcx => "cl", - RegKind::Rbp => "bpl", - RegKind::Rsp => "spl", - RegKind::R8 => "r8b", - RegKind::R9 => "r9b", - RegKind::R10 => "r10b", - RegKind::R11 => "r11b", - RegKind::R12 => "r12b", - RegKind::R13 => "r13b", - RegKind::R14 => "r14b", - RegKind::R15 => "r15b", - } - } - - pub const fn dword(&self) -> &str { - match self { - RegKind::Rax => "eax", - RegKind::Rdi => "edi", - RegKind::Rsi => "esi", - RegKind::Rdx => "edx", - RegKind::Rcx => "ecx", - RegKind::Rbp => "ebp", - RegKind::Rsp => "esp", - RegKind::R8 => "r8d", - RegKind::R9 => "r9d", - RegKind::R10 => "r10d", - RegKind::R11 => "r11d", - RegKind::R12 => "r12d", - RegKind::R13 => "r13d", - RegKind::R14 => "r14d", - RegKind::R15 => "r15d", - } - } - - pub const fn qword(&self) -> &str { - match self { - RegKind::Rax => "rax", - RegKind::Rdi => "rdi", - RegKind::Rsi => "rsi", - RegKind::Rdx => "rdx", - RegKind::Rcx => "rcx", - RegKind::Rbp => "rbp", - RegKind::Rsp => "rsp", - RegKind::R8 => "r8", - RegKind::R9 => "r9", - RegKind::R10 => "r10", - RegKind::R11 => "r11", - RegKind::R12 => "r12", - RegKind::R13 => "r13", - RegKind::R14 => "r14", - RegKind::R15 => "r15", - } - } -} diff --git a/src/generate/registers.rs b/src/generate/registers.rs new file mode 100644 index 0000000..01ba5be --- /dev/null +++ b/src/generate/registers.rs @@ -0,0 +1,233 @@ +use crate::{ + analyze::expr::ConvExpr, + error::{CompileError, UnexpectedTypeSizeStatus}, +}; + +#[derive(PartialOrd, Ord, PartialEq, Eq, Clone, Copy, Debug)] +pub enum RegOrLit { + Reg(RegKind), + Lit(isize), +} + +impl RegOrLit { + pub fn size_to_reg_name(&self, size: RegSize) -> String { + match size { + RegSize::Byte => self.byte(), + RegSize::Dword => self.dword(), + RegSize::Qword => self.qword(), + } + } + + pub fn byte(&self) -> String { + match self { + RegOrLit::Reg(reg) => reg.byte().to_string(), + RegOrLit::Lit(num) => num.to_string(), + } + } + + pub fn dword(&self) -> String { + match self { + RegOrLit::Reg(reg) => reg.dword().to_string(), + RegOrLit::Lit(num) => num.to_string(), + } + } + + pub fn qword(&self) -> String { + match self { + RegOrLit::Reg(reg) => reg.qword().to_string(), + RegOrLit::Lit(num) => num.to_string(), + } + } +} + +impl ToString for RegOrLit { + fn to_string(&self) -> String { + match self { + RegOrLit::Reg(reg) => reg.to_string(), + RegOrLit::Lit(num) => num.to_string(), + } + } +} + +#[derive(PartialOrd, Ord, PartialEq, Eq, Clone, Copy, Debug)] +pub enum RegKind { + Rax, + Rdi, + Rsi, + Rdx, + Rcx, + Rbp, + Rsp, + R8, + R9, + R10, + R11, + R12, + R13, + R14, + R15, +} + +impl ToString for RegKind { + fn to_string(&self) -> String { + match self { + RegKind::Rax => "rax", + RegKind::Rdi => "rdi", + RegKind::Rsi => "rsi", + RegKind::Rdx => "rdx", + RegKind::Rcx => "rcx", + RegKind::Rbp => "rbp", + RegKind::Rsp => "rsp", + RegKind::R8 => "r8", + RegKind::R9 => "r9", + RegKind::R10 => "r10", + RegKind::R11 => "r11", + RegKind::R12 => "r12", + RegKind::R13 => "r13", + RegKind::R14 => "r14", + RegKind::R15 => "r15", + } + .to_string() + } +} + +impl TryFrom<&str> for RegKind { + type Error = (); + + fn try_from(value: &str) -> Result { + Ok(match value { + "rax" => RegKind::Rax, + "rdi" => RegKind::Rdi, + "rsi" => RegKind::Rsi, + "rdx" => RegKind::Rdx, + "rcx" => RegKind::Rcx, + "r8" => RegKind::R8, + "r9" => RegKind::R9, + _ => return Err(()), + }) + } +} + +#[derive(PartialOrd, Ord, PartialEq, Eq, Clone, Copy, Debug)] +pub enum RegSize { + Byte, + Dword, + Qword, +} + +impl RegSize { + pub const fn try_new(size: usize) -> Option { + Some(match size { + 1 => Self::Byte, + 4 => Self::Dword, + 8 => Self::Qword, + _ => return None, + }) + } + + // clippy complains about this, but it's wrong + #[allow(clippy::missing_const_for_fn)] + pub fn try_new_with_error(size: usize, expr: ConvExpr) -> Result { + Ok(match size { + 1 => Self::Byte, + 4 => Self::Dword, + 8 => Self::Qword, + _ => { + return Err(CompileError::new_type_size_error( + UnexpectedTypeSizeStatus::Expr(expr), + )); + } + }) + } +} + +impl AsRef for RegSize { + fn as_ref(&self) -> &str { + match self { + RegSize::Byte => "BYTE", + RegSize::Dword => "DWORD", + RegSize::Qword => "QWORD", + } + } +} + +impl RegSize { + pub const fn size_to_name(size: usize) -> Option { + match size { + 1 => Some(Self::Byte), + 4 => Some(Self::Dword), + 8 => Some(Self::Qword), + _ => None, + } + } +} + +impl RegKind { + pub const fn size_to_reg_name(&self, size: RegSize) -> &str { + match size { + RegSize::Byte => self.byte(), + RegSize::Dword => self.dword(), + RegSize::Qword => self.qword(), + } + } + + pub const fn byte(&self) -> &str { + match self { + RegKind::Rax => "al", + RegKind::Rdi => "dil", + RegKind::Rsi => "sil", + RegKind::Rdx => "dl", + RegKind::Rcx => "cl", + RegKind::Rbp => "bpl", + RegKind::Rsp => "spl", + RegKind::R8 => "r8b", + RegKind::R9 => "r9b", + RegKind::R10 => "r10b", + RegKind::R11 => "r11b", + RegKind::R12 => "r12b", + RegKind::R13 => "r13b", + RegKind::R14 => "r14b", + RegKind::R15 => "r15b", + } + } + + pub const fn dword(&self) -> &str { + match self { + RegKind::Rax => "eax", + RegKind::Rdi => "edi", + RegKind::Rsi => "esi", + RegKind::Rdx => "edx", + RegKind::Rcx => "ecx", + RegKind::Rbp => "ebp", + RegKind::Rsp => "esp", + RegKind::R8 => "r8d", + RegKind::R9 => "r9d", + RegKind::R10 => "r10d", + RegKind::R11 => "r11d", + RegKind::R12 => "r12d", + RegKind::R13 => "r13d", + RegKind::R14 => "r14d", + RegKind::R15 => "r15d", + } + } + + pub const fn qword(&self) -> &str { + match self { + RegKind::Rax => "rax", + RegKind::Rdi => "rdi", + RegKind::Rsi => "rsi", + RegKind::Rdx => "rdx", + RegKind::Rcx => "rcx", + RegKind::Rbp => "rbp", + RegKind::Rsp => "rsp", + RegKind::R8 => "r8", + RegKind::R9 => "r9", + RegKind::R10 => "r10", + RegKind::R11 => "r11", + RegKind::R12 => "r12", + RegKind::R13 => "r13", + RegKind::R14 => "r14", + RegKind::R15 => "r15", + } + } +} From c58a284b81880196193de747d05c028e9779474c Mon Sep 17 00:00:00 2001 From: lemolatoon <63438515+lemolatoon@users.noreply.github.com> Date: Thu, 16 Mar 2023 17:22:08 +0900 Subject: [PATCH 2/6] Impl register allocator logics without actual generating assembly --- src/error.rs | 28 ++ src/generate.rs | 1 + src/generate/register_allocator.rs | 438 +++++++++++++++++++++++++++++ src/generate/registers.rs | 47 +++- 4 files changed, 499 insertions(+), 15 deletions(-) create mode 100644 src/generate/register_allocator.rs diff --git a/src/error.rs b/src/error.rs index d514549..00d9d21 100644 --- a/src/error.rs +++ b/src/error.rs @@ -4,6 +4,8 @@ use crate::analyze::expr::ConvExprKind; use crate::analyze::expr::FuncCallTargetKind; use crate::analyze::types::Type; use crate::analyze::variables::GVar; +use crate::generate::register_allocator::DataLocation; +use crate::generate::register_allocator::RegId; use crate::preprocess::tokenkind::TokenKind as PreprocessTokenKind; use crate::tokenize::debug_infos::DebugInfo; use crate::tokenize::debug_infos::Position; @@ -254,6 +256,20 @@ impl CompileError { GenerateErrorKind::UnexpectedTypeSize(status), )) } + + pub fn new_register_assign_error( + try_assign_id: RegId, + to: DataLocation, + message: impl ToString, + ) -> Self { + Self::new(CompileErrorKind::GenerateError( + GenerateErrorKind::LocationAssignError { + try_assign_id, + to, + message: message.to_string(), + }, + )) + } } #[derive(PartialEq, Eq, Clone, Debug)] @@ -521,6 +537,13 @@ impl Debug for CompileError { )) => { writeln!(f, "this type size is unexpected. size: {}", size)?; } + GenerateError(GenerateErrorKind::LocationAssignError { + try_assign_id, + to, + message, + }) => { + writeln!(f, "INTERNAL COMPILER(Register Allocator Error): try to assign RegId: {:?} to Register: {:?}, but failed.\n{}", try_assign_id, to, message)?; + } Unimplemented(Some(debug_info), msg) => { error_at(vec![debug_info.clone()], f)?; writeln!(f, "{}", msg)?; @@ -617,6 +640,11 @@ pub enum GenerateErrorKind { UnexpectedTypeSize(UnexpectedTypeSizeStatus), LeftValueError(ConvExpr), DerefError(ConvExpr), + LocationAssignError { + try_assign_id: RegId, + to: DataLocation, + message: String, + }, } #[derive(PartialEq, Eq, Clone, Debug)] diff --git a/src/generate.rs b/src/generate.rs index f3888a0..1e2488b 100644 --- a/src/generate.rs +++ b/src/generate.rs @@ -1,2 +1,3 @@ pub mod generate; +pub mod register_allocator; pub mod registers; diff --git a/src/generate/register_allocator.rs b/src/generate/register_allocator.rs new file mode 100644 index 0000000..e627304 --- /dev/null +++ b/src/generate/register_allocator.rs @@ -0,0 +1,438 @@ +use std::collections::BTreeMap; + +use crate::{error::CompileError, unimplemented_err}; + +use super::registers::RegKind; + +#[derive(PartialOrd, Ord, PartialEq, Eq, Clone, Copy, Debug)] +pub struct RegId(usize); + +impl From for RegId { + fn from(value: usize) -> Self { + RegId(value) + } +} + +#[derive(PartialOrd, Ord, PartialEq, Eq, Clone, Copy, Debug)] +pub struct StackRegIdx(usize); + +impl StackRegIdx { + pub fn as_depth(&self) -> usize { + self.0 * 8 + } + + pub fn as_idx(&self) -> usize { + self.0 + } +} + +#[derive(PartialOrd, Ord, PartialEq, Eq, Clone, Copy, Debug)] +pub enum DataLocation { + Register(RegKind), + Stack(StackRegIdx), +} + +#[derive(PartialOrd, Ord, PartialEq, Eq, Clone, Debug)] +pub struct AllocatedRegisterTable([Option; 16]); + +#[derive(PartialOrd, Ord, PartialEq, Eq, Debug)] +pub struct AllocatedRegisterTableEntry<'a> { + kind: RegKind, + entry: &'a mut Option, +} + +impl<'a> AllocatedRegisterTableEntry<'a> { + pub fn new(kind: RegKind, muttable_ref: &'a mut Option) -> Self { + Self { + kind, + entry: muttable_ref, + } + } + + pub fn kind(&self) -> RegKind { + self.kind + } + + pub fn as_location(&self) -> DataLocation { + DataLocation::Register(self.kind) + } + + pub fn insert(&mut self, id: RegId) { + *(self.entry) = Some(id); + } + + pub fn deallocate(&mut self) { + *(self.entry) = None; + } +} + +impl AllocatedRegisterTable { + pub fn record_as_allocated(&mut self, reg: RegKind, id: RegId) -> Result<(), CompileError> { + if self.0[reg as usize].is_some() { + return Err(unimplemented_err!(format!( + "INTERNAL COMPILER ERROR: try to allocate register {}, but it was already used.", + reg.qword() + ))); + } else { + self.0[reg as usize] = Some(id); + } + + Ok(()) + } + + pub fn deallocate(&mut self, reg: RegKind) -> Result { + match self.0[reg as usize] { + Some(prev_id) => { + self.0[reg as usize] = None; + Ok(prev_id) + }, + None => Err(unimplemented_err!(format!( + "INTERNAL COMPILER ERROR: try to deallocate register {}, but it was already deallocated.", + reg.qword() + ))), + } + } + + pub fn allocate_any_entry( + &mut self, + ) -> Result, CompileError> { + let idx = self + .0 + .iter() + .enumerate() + .filter_map(|(idx, &allocated)| if allocated.is_some() { None } else { Some(idx) }) + .next(); + idx.map(|idx| { + Ok(AllocatedRegisterTableEntry::new( + RegKind::try_from(idx)?, + &mut self.0[idx], + )) + }) + .transpose() + } + + pub fn allocate_any(&mut self, id: RegId) -> Result, CompileError> { + Ok(if let Some(mut entry) = self.allocate_any_entry()? { + entry.insert(id); + Some(entry.kind()) + } else { + None + }) + } + + /// try to allocate given register return Entry if successfully allocated. + pub fn allocate_entry(&mut self, reg: RegKind) -> Option { + if self.0[reg as usize].is_none() { + Some(AllocatedRegisterTableEntry::new( + reg, + &mut self.0[reg as usize], + )) + } else { + None + } + } + + pub fn get(&self, reg: RegKind) -> &Option { + &self.0[reg as usize] + } + + pub fn get_mut(&mut self, reg: RegKind) -> &mut Option { + &mut self.0[reg as usize] + } +} + +#[derive(PartialOrd, Ord, PartialEq, Eq, Clone, Debug)] +pub struct LocationMap(BTreeMap); + +impl LocationMap { + /// Call this function under the consumption that the location is not occupied. Check it by looking at AllocatedRegisterTable + pub fn allocate(&mut self, location: DataLocation) -> (RegId, DataLocation) { + for id in (0..).map(RegId::from) { + match self.0.entry(id) { + std::collections::btree_map::Entry::Occupied(_) => continue, + std::collections::btree_map::Entry::Vacant(entry) => { + entry.insert(location.clone()); + return (id, location); + } + } + } + unreachable!() + } + + pub fn relocate(&mut self, id: RegId, location: DataLocation) -> Result<(), CompileError> { + if self.0.insert(id, location).is_none() { + return Err(unimplemented_err!(format!("INTERNAL COMPILER ERROR: try to relocate data, but the id: {:?} was empty in location map.", id))); + } + Ok(()) + } + + pub fn delete(&mut self, id: RegId) -> Result<(), CompileError> { + if self.0.remove(&id).is_none() { + return Err(unimplemented_err!(format!("INTERNAL COMPILER ERROR: try to delete data from location map, but the id: {:?} was empty in location map.", id))); + } + Ok(()) + } + + pub fn get(&self, id: &RegId) -> Option<&DataLocation> { + self.0.get(id) + } + + pub fn replace(&mut self, id1: RegId, id2: RegId) -> Result<(), CompileError> { + let entry1 = match self.0.entry(id1) { + std::collections::btree_map::Entry::Vacant(_) => { + return Err(unimplemented_err!(format!( + "INTERNAL COMPILER ERROR: try to replace {:?}, {:?} but {:?} was empty", + id1, id2, id1 + ))) + } + std::collections::btree_map::Entry::Occupied(entry) => entry, + }; + let id1_location = entry1.get().clone(); + let location2 = match self.0.insert(id2, id1_location) { + Some(location2) => location2, + None => { + return Err(unimplemented_err!(format!( + "INTERNAL COMPILER ERROR: try to replace {:?}, {:?} but {:?} was empty", + id1, id2, id1 + ))) + } + }; + self.0.insert(id2, location2); + + Ok(()) + } +} + +#[derive(PartialOrd, Ord, PartialEq, Eq, Clone, Debug)] +pub struct StackDepthTable { + reg_ids: Vec>, +} + +#[derive(PartialOrd, Ord, PartialEq, Eq, Debug)] +pub struct StackDepthTableEntry<'a> { + stack_reg_idx: StackRegIdx, + stack: &'a mut Vec>, +} + +impl<'a> StackDepthTableEntry<'a> { + pub fn new( + stack_reg_idx: StackRegIdx, + reg_ids: &'a mut Vec>, + ) -> Result { + if reg_ids.get(stack_reg_idx.0).is_none() { + return Err(unimplemented_err!( + "INTERNAL COMPILER ERROR: try to create StackDepthTableEntry, but failed" + )); + } + Ok(Self { + stack_reg_idx, + stack: reg_ids, + }) + } + + pub fn as_location(&self) -> DataLocation { + DataLocation::Stack(self.stack_reg_idx) + } + + pub fn insert(&mut self, id: RegId) { + *(self.stack.get_mut(self.stack_reg_idx.0).unwrap()) = Some(id); + // TODO: StackDepthTableEntry について再考 + } + + pub fn deallocate(&mut self) { + *(self.stack.get_mut(self.stack_reg_idx.0).unwrap()) = None; + } +} + +#[derive(PartialOrd, Ord, PartialEq, Eq, Debug)] +pub enum RegIdEntry<'a> { + Reg(AllocatedRegisterTableEntry<'a>), + Stack(StackDepthTableEntry<'a>), +} +impl<'a> From> for RegIdEntry<'a> { + fn from(entry: AllocatedRegisterTableEntry<'a>) -> Self { + RegIdEntry::Reg(entry) + } +} + +impl<'a> From> for RegIdEntry<'a> { + fn from(entry: StackDepthTableEntry<'a>) -> Self { + RegIdEntry::Stack(entry) + } +} + +impl<'a> RegIdEntry<'a> { + pub fn insert(&mut self, id: RegId) { + match self { + RegIdEntry::Reg(entry) => entry.insert(id), + RegIdEntry::Stack(entry) => entry.insert(id), + } + } + + pub fn as_location(&self) -> DataLocation { + match self { + RegIdEntry::Reg(entry) => entry.as_location(), + RegIdEntry::Stack(entry) => entry.as_location(), + } + } + + pub fn deallocate(&mut self) { + match self { + RegIdEntry::Reg(entry) => entry.deallocate(), + RegIdEntry::Stack(entry) => entry.deallocate(), + } + } +} + +impl StackDepthTable { + pub fn allocate_entry(&mut self) -> Result { + let stack_reg_idx = self.reg_ids.len(); + StackDepthTableEntry::new(StackRegIdx(stack_reg_idx), &mut self.reg_ids) + } + + pub fn get(&self, idx: StackRegIdx) -> Option<&Option> { + self.reg_ids.get(idx.as_idx()) + } + + pub fn get_vec(&self) -> &Vec> { + &self.reg_ids + } + + pub fn get_vec_mut(&mut self) -> &mut Vec> { + &mut self.reg_ids + } +} + +#[derive(PartialOrd, Ord, PartialEq, Eq, Clone, Debug)] +pub struct RegisterAllocator { + location_map: LocationMap, + allocated: AllocatedRegisterTable, + depth: StackDepthTable, +} + +impl RegisterAllocator { + /// High Layer abstraction of relocate data to specific register + pub fn assign( + &mut self, + id: RegId, + assign_to_location: DataLocation, + ) -> Result<(), CompileError> { + match self.location_map.get(&id) { + Some(&prev_allocated_location) if prev_allocated_location == assign_to_location => { + // already assigned + Ok(()) + } + Some(prev_location) => { + if let Some(using_assign_to_location_id) = + self.look_up_reg_id_from_location(assign_to_location) + { + // prev state | next state + // id -> prev_location | id -> assign_to + // using_assign_to_location_id -> assign_to | using_assign_to_location_id -> prev_location + self.replace( + (id, *prev_location), + (*using_assign_to_location_id, assign_to_location), + )?; + } else { + // prev state | next state + // id -> prev_location | id -> assign_to + // None -> assign_to | None -> prev_location (deallocated) + // TODO: entry のデータ構造を変えてもっと抽象化する + let mut prev_location_entry = self.from_location_to_entry(*prev_location)?; + prev_location_entry.deallocate(); + let mut assign_to_entry = self.from_location_to_entry(assign_to_location)?; + assign_to_entry.insert(id); + self.location_map.relocate(id, assign_to_location)?; + } + Ok(()) + } + None => Err(CompileError::new_register_assign_error( + id, + assign_to_location, + "given RegId is not yet allocated.", + )), + } + } + + fn look_up_reg_id_from_location(&self, location: DataLocation) -> Option<&RegId> { + match location { + DataLocation::Register(reg) => self.allocated.get(reg).as_ref(), + DataLocation::Stack(stack_reg_idx) => self + .depth + .get(stack_reg_idx) + .and_then(|maybe_reg_id| maybe_reg_id.as_ref()), + } + } + + fn from_location_to_entry( + &mut self, + location: DataLocation, + ) -> Result { + match location { + DataLocation::Register(reg) => RegIdEntry::Reg(AllocatedRegisterTableEntry { + kind: reg, + entry: self.allocated.get_mut(reg), + }), + DataLocation::Stack(stack_reg_idx) => RegIdEntry::Stack(StackDepthTableEntry::new( + stack_reg_idx, + self.depth.get_vec_mut(), + )?), + }; + todo!() + } + + fn replace( + &mut self, + (id1, location1): (RegId, DataLocation), + (id2, location2): (RegId, DataLocation), + ) -> Result<(), CompileError> { + let mut entry_at_location1 = self.from_location_to_entry(location1)?; + entry_at_location1.insert(id2); + let mut entry_at_location2 = self.from_location_to_entry(location2)?; + entry_at_location2.insert(id1); + self.location_map.replace(id1, id2)?; + Ok(()) + } + + fn allocate_any_entry(&mut self) -> Result { + if let Some(entry) = self.allocated.allocate_any_entry()? { + Ok(entry.into()) + } else { + Ok(self.depth.allocate_entry()?.into()) + } + } + + /// High Layer abstraction of allocate to specific register + pub fn allocate_if_possible(&mut self, reg: RegKind) -> Option<(RegId, DataLocation)> { + if let Some(mut entry) = self.allocated.allocate_entry(reg) { + let (id, location) = self.location_map.allocate(DataLocation::Register(reg)); + entry.insert(id); + Some((id, location)) + } else { + None + } + } + + /// High Layer abstraction of allocate to some location + pub fn allocate( + &mut self, + prioritized_reg: Option, + ) -> Result<(RegId, DataLocation), CompileError> { + if let Some(prioritized_reg) = prioritized_reg { + if let Some((id, location)) = self.allocate_if_possible(prioritized_reg) { + return Ok((id, location)); + } + } + /* allocate any entry inlined */ + let mut entry: RegIdEntry = if let Some(entry) = self.allocated.allocate_any_entry()? { + entry.into() + } else { + self.depth.allocate_entry()?.into() + }; + /* allocate any entry end */ + let location = entry.as_location(); + let (id, location) = self.location_map.allocate(location); + entry.insert(id); + Ok((id, location)) + } +} diff --git a/src/generate/registers.rs b/src/generate/registers.rs index 01ba5be..16f764c 100644 --- a/src/generate/registers.rs +++ b/src/generate/registers.rs @@ -1,6 +1,7 @@ use crate::{ analyze::expr::ConvExpr, error::{CompileError, UnexpectedTypeSizeStatus}, + unimplemented_err, }; #[derive(PartialOrd, Ord, PartialEq, Eq, Clone, Copy, Debug)] @@ -49,23 +50,39 @@ impl ToString for RegOrLit { } } +const NUM_REGISTER: usize = 15; #[derive(PartialOrd, Ord, PartialEq, Eq, Clone, Copy, Debug)] pub enum RegKind { - Rax, - Rdi, - Rsi, - Rdx, - Rcx, - Rbp, - Rsp, - R8, - R9, - R10, - R11, - R12, - R13, - R14, - R15, + Rax = 0, + Rdi = 1, + Rsi = 2, + Rdx = 3, + Rcx = 4, + Rbp = 5, + Rsp = 6, + R8 = 7, + R9 = 8, + R10 = 9, + R11 = 10, + R12 = 11, + R13 = 12, + R14 = 13, + R15 = 14, +} + +impl TryFrom for RegKind { + type Error = CompileError; + + fn try_from(value: usize) -> Result { + let u8_value = value as u8; + if u8_value > NUM_REGISTER as u8 { + return Err(unimplemented_err!(format!( + "Try to convert usize to RegKind, but it exceeds its number: {}", + value + ))); + } + Ok(unsafe { std::mem::transmute(u8_value) }) + } } impl ToString for RegKind { From 5b6206674c3887260caf9df5f166a70e95b73083 Mon Sep 17 00:00:00 2001 From: lemolatoon <63438515+lemolatoon@users.noreply.github.com> Date: Thu, 16 Mar 2023 18:27:22 +0900 Subject: [PATCH 3/6] Add register allocator unit tests --- src/generate/register_allocator.rs | 181 ++++++++++++++++++++++++++++- src/generate/registers.rs | 2 +- 2 files changed, 176 insertions(+), 7 deletions(-) diff --git a/src/generate/register_allocator.rs b/src/generate/register_allocator.rs index e627304..6d21628 100644 --- a/src/generate/register_allocator.rs +++ b/src/generate/register_allocator.rs @@ -2,7 +2,7 @@ use std::collections::BTreeMap; use crate::{error::CompileError, unimplemented_err}; -use super::registers::RegKind; +use super::registers::{RegKind, NUM_REGISTER}; #[derive(PartialOrd, Ord, PartialEq, Eq, Clone, Copy, Debug)] pub struct RegId(usize); @@ -33,7 +33,13 @@ pub enum DataLocation { } #[derive(PartialOrd, Ord, PartialEq, Eq, Clone, Debug)] -pub struct AllocatedRegisterTable([Option; 16]); +pub struct AllocatedRegisterTable([Option; NUM_REGISTER]); + +impl AllocatedRegisterTable { + pub fn new() -> Self { + Self([None; NUM_REGISTER]) + } +} #[derive(PartialOrd, Ord, PartialEq, Eq, Debug)] pub struct AllocatedRegisterTableEntry<'a> { @@ -145,6 +151,10 @@ impl AllocatedRegisterTable { pub struct LocationMap(BTreeMap); impl LocationMap { + pub fn new() -> Self { + Self(BTreeMap::new()) + } + /// Call this function under the consumption that the location is not occupied. Check it by looking at AllocatedRegisterTable pub fn allocate(&mut self, location: DataLocation) -> (RegId, DataLocation) { for id in (0..).map(RegId::from) { @@ -197,7 +207,7 @@ impl LocationMap { ))) } }; - self.0.insert(id2, location2); + self.0.insert(id1, location2); Ok(()) } @@ -285,8 +295,15 @@ impl<'a> RegIdEntry<'a> { } impl StackDepthTable { + pub fn new() -> Self { + Self { + reg_ids: Vec::new(), + } + } + pub fn allocate_entry(&mut self) -> Result { let stack_reg_idx = self.reg_ids.len(); + self.reg_ids.push(None); StackDepthTableEntry::new(StackRegIdx(stack_reg_idx), &mut self.reg_ids) } @@ -311,6 +328,14 @@ pub struct RegisterAllocator { } impl RegisterAllocator { + pub fn new() -> Self { + Self { + location_map: LocationMap::new(), + allocated: AllocatedRegisterTable::new(), + depth: StackDepthTable::new(), + } + } + /// High Layer abstraction of relocate data to specific register pub fn assign( &mut self, @@ -364,11 +389,15 @@ impl RegisterAllocator { } } + pub fn look_up_location_from_reg_id(&self, reg_id: &RegId) -> Option<&DataLocation> { + self.location_map.get(reg_id) + } + fn from_location_to_entry( &mut self, location: DataLocation, ) -> Result { - match location { + Ok(match location { DataLocation::Register(reg) => RegIdEntry::Reg(AllocatedRegisterTableEntry { kind: reg, entry: self.allocated.get_mut(reg), @@ -377,8 +406,7 @@ impl RegisterAllocator { stack_reg_idx, self.depth.get_vec_mut(), )?), - }; - todo!() + }) } fn replace( @@ -436,3 +464,144 @@ impl RegisterAllocator { Ok((id, location)) } } + +#[cfg(test)] +mod register_allocator_test { + use crate::generate::registers::NUM_REGISTER; + + use super::*; + #[test] + pub fn allocate_register_test() { + let mut allocator = RegisterAllocator::new(); + let (reg_id, location) = allocator.allocate(Some(RegKind::Rax)).unwrap(); + assert_eq!(location, DataLocation::Register(RegKind::Rax)); + assert_eq!( + *allocator.look_up_reg_id_from_location(location).unwrap(), + reg_id + ); + assert_eq!( + *allocator.look_up_location_from_reg_id(®_id).unwrap(), + location + ); + } + + #[test] + pub fn allocate_same_place_test() { + let mut allocator = RegisterAllocator::new(); + let (reg_id1, location1) = allocator.allocate(Some(RegKind::Rax)).unwrap(); + assert_eq!(location1, DataLocation::Register(RegKind::Rax)); + assert_eq!( + *allocator.look_up_reg_id_from_location(location1).unwrap(), + reg_id1 + ); + assert_eq!( + *allocator.look_up_location_from_reg_id(®_id1).unwrap(), + location1 + ); + let (reg_id2, location2) = allocator.allocate(Some(RegKind::Rax)).unwrap(); + assert_eq!(location1, DataLocation::Register(RegKind::Rax)); + assert_ne!(location2, DataLocation::Register(RegKind::Rax)); + assert_eq!( + *allocator.look_up_reg_id_from_location(location2).unwrap(), + reg_id2 + ); + assert_eq!( + *allocator.look_up_location_from_reg_id(®_id2).unwrap(), + location2 + ); + } + + #[test] + pub fn assign_test() { + let mut allocator = RegisterAllocator::new(); + let (reg_id1, location1) = allocator.allocate(Some(RegKind::Rax)).unwrap(); + assert_eq!(location1, DataLocation::Register(RegKind::Rax)); + let (reg_id2, location2) = allocator.allocate(Some(RegKind::Rax)).unwrap(); + allocator + .assign(reg_id2, DataLocation::Register(RegKind::Rax)) + .unwrap(); + assert_eq!( + *allocator.look_up_location_from_reg_id(®_id2).unwrap(), + DataLocation::Register(RegKind::Rax) + ); + assert_eq!( + *allocator.look_up_location_from_reg_id(®_id1).unwrap(), + location2 + ); + } + + #[test] + pub fn assign_to_not_allocated_location() { + let mut allocator = RegisterAllocator::new(); + let (reg_id1, _) = allocator.allocate(Some(RegKind::Rax)).unwrap(); + allocator + .assign(reg_id1, DataLocation::Register(RegKind::Rdi)) + .unwrap(); + assert_eq!( + *allocator.look_up_location_from_reg_id(®_id1).unwrap(), + DataLocation::Register(RegKind::Rdi) + ); + assert_eq!( + allocator.look_up_reg_id_from_location(DataLocation::Register(RegKind::Rax)), + None + ); + } + + #[test] + pub fn stack_allocate_test() { + let mut allocator = RegisterAllocator::new(); + let (rax_id, rax_location) = allocator.allocate(Some(RegKind::Rax)).unwrap(); + assert_eq!(rax_location, DataLocation::Register(RegKind::Rax)); + for _ in 0..(NUM_REGISTER - 1) { + allocator.allocate(None).unwrap(); + } + // all register might be allocated + for maybe_reg_id in allocator.allocated.0 { + eprintln!("{:?}", maybe_reg_id); + assert!(maybe_reg_id.is_some()); + } + + // allocate stack + let (maybe_in_stack_reg_id, maybe_stack_location) = allocator.allocate(None).unwrap(); + assert_eq!(maybe_stack_location, DataLocation::Stack(StackRegIdx(0))); + assert_eq!( + *allocator + .look_up_reg_id_from_location(maybe_stack_location) + .unwrap(), + maybe_in_stack_reg_id + ); + assert_eq!( + *allocator + .look_up_location_from_reg_id(&maybe_in_stack_reg_id) + .unwrap(), + maybe_stack_location + ); + + // assign value on stack to specific register + allocator + .assign(maybe_in_stack_reg_id, DataLocation::Register(RegKind::Rax)) + .unwrap(); + assert_eq!( + *allocator + .look_up_reg_id_from_location(maybe_stack_location) + .unwrap(), + rax_id + ); + assert_eq!( + *allocator.look_up_location_from_reg_id(&rax_id).unwrap(), + maybe_stack_location + ); + assert_eq!( + *allocator + .look_up_reg_id_from_location(DataLocation::Register(RegKind::Rax)) + .unwrap(), + maybe_in_stack_reg_id + ); + assert_eq!( + *allocator + .look_up_location_from_reg_id(&maybe_in_stack_reg_id) + .unwrap(), + DataLocation::Register(RegKind::Rax) + ); + } +} diff --git a/src/generate/registers.rs b/src/generate/registers.rs index 16f764c..6fa14ca 100644 --- a/src/generate/registers.rs +++ b/src/generate/registers.rs @@ -50,7 +50,7 @@ impl ToString for RegOrLit { } } -const NUM_REGISTER: usize = 15; +pub const NUM_REGISTER: usize = 15; #[derive(PartialOrd, Ord, PartialEq, Eq, Clone, Copy, Debug)] pub enum RegKind { Rax = 0, From 597d8ab4b0134bac3d38b6389e8c87575ea57e22 Mon Sep 17 00:00:00 2001 From: lemolatoon <63438515+lemolatoon@users.noreply.github.com> Date: Thu, 16 Mar 2023 19:30:35 +0900 Subject: [PATCH 4/6] Impl publish_reg_id in RegisterAllocator --- src/generate/register_allocator.rs | 105 +++++++++++++++++++++++++++-- 1 file changed, 101 insertions(+), 4 deletions(-) diff --git a/src/generate/register_allocator.rs b/src/generate/register_allocator.rs index 6d21628..ed87a55 100644 --- a/src/generate/register_allocator.rs +++ b/src/generate/register_allocator.rs @@ -32,6 +32,24 @@ pub enum DataLocation { Stack(StackRegIdx), } +impl From for DataLocation { + fn from(reg: RegKind) -> Self { + DataLocation::Register(reg) + } +} + +impl DataLocation { + pub fn new_from_depth(depth: usize) -> Result { + if depth % 8 != 0 { + return Err(unimplemented_err!(format!( + "try to new DataLocation from depth, but the provided depth was not multiple of 8." + ))); + } + + Ok(DataLocation::Stack(StackRegIdx(depth / 8))) + } +} + #[derive(PartialOrd, Ord, PartialEq, Eq, Clone, Debug)] pub struct AllocatedRegisterTable([Option; NUM_REGISTER]); @@ -307,6 +325,29 @@ impl StackDepthTable { StackDepthTableEntry::new(StackRegIdx(stack_reg_idx), &mut self.reg_ids) } + pub fn allocate_entry_of_specific_depth( + &mut self, + stack_reg_idx: StackRegIdx, + ) -> Result { + self.ensures_accessable_idx(stack_reg_idx.as_idx()); + if let Some(maybe_stack_reg_idx) = self.reg_ids.get(stack_reg_idx.as_idx()) { + if maybe_stack_reg_idx.is_some() { + return Err(unimplemented_err!(format!("INTERNAL COMPILER ERROR: try to allocate specific depth in stack area, the specified stack_reg_idx: {:?} was already used", stack_reg_idx))); + } + } else { + unreachable!(); + } + StackDepthTableEntry::new(stack_reg_idx, &mut self.reg_ids) + } + + pub fn ensures_accessable_idx(&mut self, idx: usize) { + for idx in 0..=idx { + if self.reg_ids.get(idx).is_none() { + self.reg_ids.push(None); + } + } + } + pub fn get(&self, idx: StackRegIdx) -> Option<&Option> { self.reg_ids.get(idx.as_idx()) } @@ -379,6 +420,25 @@ impl RegisterAllocator { } } + /// subscribe unmanaged place for this RegisterAllocator + pub fn publish_reg_id(&mut self, location: DataLocation) -> Result { + let (id, location) = self.location_map.allocate(location); + match location { + DataLocation::Register(reg) => { + if let Some(mut entry) = self.allocated.allocate_entry(reg) { + entry.insert(id); + } else { + return Err(unimplemented_err!(format!("INTERNAL COMPILER ERROR: try to subscribe specified location: {:?}, but the location was already used", location))); + } + } + DataLocation::Stack(stack_reg_idx) => { + let mut entry = self.depth.allocate_entry_of_specific_depth(stack_reg_idx)?; + entry.insert(id); + } + } + Ok(id) + } + fn look_up_reg_id_from_location(&self, location: DataLocation) -> Option<&RegId> { match location { DataLocation::Register(reg) => self.allocated.get(reg).as_ref(), @@ -402,10 +462,13 @@ impl RegisterAllocator { kind: reg, entry: self.allocated.get_mut(reg), }), - DataLocation::Stack(stack_reg_idx) => RegIdEntry::Stack(StackDepthTableEntry::new( - stack_reg_idx, - self.depth.get_vec_mut(), - )?), + DataLocation::Stack(stack_reg_idx) => { + self.depth.ensures_accessable_idx(stack_reg_idx.as_idx()); + RegIdEntry::Stack(StackDepthTableEntry::new( + stack_reg_idx, + self.depth.get_vec_mut(), + )?) + } }) } @@ -604,4 +667,38 @@ mod register_allocator_test { DataLocation::Register(RegKind::Rax) ); } + + #[test] + pub fn publish_reg_id_test() { + let mut allocator = RegisterAllocator::new(); + let (rax_id, _) = allocator.allocate(Some(RegKind::Rax)).unwrap(); + let rdi_id = allocator.publish_reg_id(RegKind::Rdi.into()).unwrap(); + assert_eq!(rdi_id, RegId(1)); + assert_eq!( + *allocator.look_up_location_from_reg_id(&rdi_id).unwrap(), + DataLocation::Register(RegKind::Rdi) + ); + assert!(allocator.publish_reg_id(RegKind::Rax.into()).is_err()); + allocator + .assign(rax_id, DataLocation::new_from_depth(0).unwrap()) + .unwrap(); + let stack_16_id = allocator + .publish_reg_id(DataLocation::new_from_depth(16).unwrap()) + .unwrap(); + assert_eq!( + *allocator + .look_up_location_from_reg_id(&stack_16_id) + .unwrap(), + DataLocation::new_from_depth(16).unwrap() + ); + assert!(allocator + .publish_reg_id(DataLocation::new_from_depth(0).unwrap()) + .is_err()); + assert!(allocator + .publish_reg_id(DataLocation::new_from_depth(8).unwrap()) + .is_ok()); + assert!(allocator + .publish_reg_id(DataLocation::new_from_depth(16).unwrap()) + .is_err()); + } } From 8b5fdc20f21e0a896ae90e87a2287f64107e9d74 Mon Sep 17 00:00:00 2001 From: lemolatoon <63438515+lemolatoon@users.noreply.github.com> Date: Thu, 27 Apr 2023 21:52:22 +0900 Subject: [PATCH 5/6] Impl RegisterAllocator::deallocate_stacks --- src/generate/generate.rs | 5 +++- src/generate/register_allocator.rs | 40 ++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/src/generate/generate.rs b/src/generate/generate.rs index 7211183..79ed172 100644 --- a/src/generate/generate.rs +++ b/src/generate/generate.rs @@ -16,7 +16,10 @@ use crate::{ unimplemented_err, }; -use super::registers::{RegKind, RegOrLit, RegSize}; +use super::{ + register_allocator::RegisterAllocator, + registers::{RegKind, RegOrLit, RegSize}, +}; #[derive(Debug, Clone)] pub struct Generator { diff --git a/src/generate/register_allocator.rs b/src/generate/register_allocator.rs index ed87a55..2c8bb67 100644 --- a/src/generate/register_allocator.rs +++ b/src/generate/register_allocator.rs @@ -359,6 +359,10 @@ impl StackDepthTable { pub fn get_vec_mut(&mut self) -> &mut Vec> { &mut self.reg_ids } + + pub fn deallocate_all(&mut self) { + self.reg_ids.clear(); + } } #[derive(PartialOrd, Ord, PartialEq, Eq, Clone, Debug)] @@ -420,6 +424,11 @@ impl RegisterAllocator { } } + /// free all allocated stacks + pub fn deallocate_stacks(&mut self) { + self.depth.deallocate_all(); + } + /// subscribe unmanaged place for this RegisterAllocator pub fn publish_reg_id(&mut self, location: DataLocation) -> Result { let (id, location) = self.location_map.allocate(location); @@ -702,3 +711,34 @@ mod register_allocator_test { .is_err()); } } + +#[test] +pub fn deallocate_stacks_test() { + let mut allocator = RegisterAllocator::new(); + let id1 = allocator + .publish_reg_id(DataLocation::new_from_depth(0).unwrap()) + .unwrap(); + assert_eq!( + allocator.look_up_reg_id_from_location(DataLocation::new_from_depth(0).unwrap()), + Some(&id1) + ); + let (id2, _) = allocator.allocate_if_possible(RegKind::Rax.into()).unwrap(); + let _id3 = allocator + .publish_reg_id(DataLocation::new_from_depth(8).unwrap()) + .unwrap(); + allocator.deallocate_stacks(); + assert_eq!( + allocator.look_up_reg_id_from_location(RegKind::Rax.into()), + Some(&id2) + ); + + assert_eq!( + allocator.look_up_reg_id_from_location(DataLocation::new_from_depth(0).unwrap()), + None + ); + + assert_eq!( + allocator.look_up_reg_id_from_location(DataLocation::new_from_depth(8).unwrap()), + None + ); +} From 2257510f52be5c91b01dbf7385bbd39c823a4a79 Mon Sep 17 00:00:00 2001 From: lemolatoon <63438515+lemolatoon@users.noreply.github.com> Date: Thu, 27 Apr 2023 22:43:37 +0900 Subject: [PATCH 6/6] Split into files of RegisterAllocator --- src/error.rs | 4 +- src/generate/generate.rs | 5 +- src/generate/register_allocator.rs | 751 +----------------- .../allocated_register_table.rs | 126 +++ src/generate/register_allocator/allocator.rs | 392 +++++++++ .../register_allocator/data_location.rs | 55 ++ .../register_allocator/location_map.rs | 71 ++ .../register_allocator/regid_entry.rs | 45 ++ .../register_allocator/stack_depth_table.rs | 97 +++ src/generate/register_allocator/stacks.rs | 1 + 10 files changed, 797 insertions(+), 750 deletions(-) create mode 100644 src/generate/register_allocator/allocated_register_table.rs create mode 100644 src/generate/register_allocator/allocator.rs create mode 100644 src/generate/register_allocator/data_location.rs create mode 100644 src/generate/register_allocator/location_map.rs create mode 100644 src/generate/register_allocator/regid_entry.rs create mode 100644 src/generate/register_allocator/stack_depth_table.rs create mode 100644 src/generate/register_allocator/stacks.rs diff --git a/src/error.rs b/src/error.rs index 00d9d21..0e3d7ea 100644 --- a/src/error.rs +++ b/src/error.rs @@ -4,8 +4,8 @@ use crate::analyze::expr::ConvExprKind; use crate::analyze::expr::FuncCallTargetKind; use crate::analyze::types::Type; use crate::analyze::variables::GVar; -use crate::generate::register_allocator::DataLocation; -use crate::generate::register_allocator::RegId; +use crate::generate::register_allocator::data_location::DataLocation; +use crate::generate::register_allocator::data_location::RegId; use crate::preprocess::tokenkind::TokenKind as PreprocessTokenKind; use crate::tokenize::debug_infos::DebugInfo; use crate::tokenize::debug_infos::Position; diff --git a/src/generate/generate.rs b/src/generate/generate.rs index 79ed172..7211183 100644 --- a/src/generate/generate.rs +++ b/src/generate/generate.rs @@ -16,10 +16,7 @@ use crate::{ unimplemented_err, }; -use super::{ - register_allocator::RegisterAllocator, - registers::{RegKind, RegOrLit, RegSize}, -}; +use super::registers::{RegKind, RegOrLit, RegSize}; #[derive(Debug, Clone)] pub struct Generator { diff --git a/src/generate/register_allocator.rs b/src/generate/register_allocator.rs index 2c8bb67..c77465e 100644 --- a/src/generate/register_allocator.rs +++ b/src/generate/register_allocator.rs @@ -1,744 +1,7 @@ -use std::collections::BTreeMap; - -use crate::{error::CompileError, unimplemented_err}; - -use super::registers::{RegKind, NUM_REGISTER}; - -#[derive(PartialOrd, Ord, PartialEq, Eq, Clone, Copy, Debug)] -pub struct RegId(usize); - -impl From for RegId { - fn from(value: usize) -> Self { - RegId(value) - } -} - -#[derive(PartialOrd, Ord, PartialEq, Eq, Clone, Copy, Debug)] -pub struct StackRegIdx(usize); - -impl StackRegIdx { - pub fn as_depth(&self) -> usize { - self.0 * 8 - } - - pub fn as_idx(&self) -> usize { - self.0 - } -} - -#[derive(PartialOrd, Ord, PartialEq, Eq, Clone, Copy, Debug)] -pub enum DataLocation { - Register(RegKind), - Stack(StackRegIdx), -} - -impl From for DataLocation { - fn from(reg: RegKind) -> Self { - DataLocation::Register(reg) - } -} - -impl DataLocation { - pub fn new_from_depth(depth: usize) -> Result { - if depth % 8 != 0 { - return Err(unimplemented_err!(format!( - "try to new DataLocation from depth, but the provided depth was not multiple of 8." - ))); - } - - Ok(DataLocation::Stack(StackRegIdx(depth / 8))) - } -} - -#[derive(PartialOrd, Ord, PartialEq, Eq, Clone, Debug)] -pub struct AllocatedRegisterTable([Option; NUM_REGISTER]); - -impl AllocatedRegisterTable { - pub fn new() -> Self { - Self([None; NUM_REGISTER]) - } -} - -#[derive(PartialOrd, Ord, PartialEq, Eq, Debug)] -pub struct AllocatedRegisterTableEntry<'a> { - kind: RegKind, - entry: &'a mut Option, -} - -impl<'a> AllocatedRegisterTableEntry<'a> { - pub fn new(kind: RegKind, muttable_ref: &'a mut Option) -> Self { - Self { - kind, - entry: muttable_ref, - } - } - - pub fn kind(&self) -> RegKind { - self.kind - } - - pub fn as_location(&self) -> DataLocation { - DataLocation::Register(self.kind) - } - - pub fn insert(&mut self, id: RegId) { - *(self.entry) = Some(id); - } - - pub fn deallocate(&mut self) { - *(self.entry) = None; - } -} - -impl AllocatedRegisterTable { - pub fn record_as_allocated(&mut self, reg: RegKind, id: RegId) -> Result<(), CompileError> { - if self.0[reg as usize].is_some() { - return Err(unimplemented_err!(format!( - "INTERNAL COMPILER ERROR: try to allocate register {}, but it was already used.", - reg.qword() - ))); - } else { - self.0[reg as usize] = Some(id); - } - - Ok(()) - } - - pub fn deallocate(&mut self, reg: RegKind) -> Result { - match self.0[reg as usize] { - Some(prev_id) => { - self.0[reg as usize] = None; - Ok(prev_id) - }, - None => Err(unimplemented_err!(format!( - "INTERNAL COMPILER ERROR: try to deallocate register {}, but it was already deallocated.", - reg.qword() - ))), - } - } - - pub fn allocate_any_entry( - &mut self, - ) -> Result, CompileError> { - let idx = self - .0 - .iter() - .enumerate() - .filter_map(|(idx, &allocated)| if allocated.is_some() { None } else { Some(idx) }) - .next(); - idx.map(|idx| { - Ok(AllocatedRegisterTableEntry::new( - RegKind::try_from(idx)?, - &mut self.0[idx], - )) - }) - .transpose() - } - - pub fn allocate_any(&mut self, id: RegId) -> Result, CompileError> { - Ok(if let Some(mut entry) = self.allocate_any_entry()? { - entry.insert(id); - Some(entry.kind()) - } else { - None - }) - } - - /// try to allocate given register return Entry if successfully allocated. - pub fn allocate_entry(&mut self, reg: RegKind) -> Option { - if self.0[reg as usize].is_none() { - Some(AllocatedRegisterTableEntry::new( - reg, - &mut self.0[reg as usize], - )) - } else { - None - } - } - - pub fn get(&self, reg: RegKind) -> &Option { - &self.0[reg as usize] - } - - pub fn get_mut(&mut self, reg: RegKind) -> &mut Option { - &mut self.0[reg as usize] - } -} - -#[derive(PartialOrd, Ord, PartialEq, Eq, Clone, Debug)] -pub struct LocationMap(BTreeMap); - -impl LocationMap { - pub fn new() -> Self { - Self(BTreeMap::new()) - } - - /// Call this function under the consumption that the location is not occupied. Check it by looking at AllocatedRegisterTable - pub fn allocate(&mut self, location: DataLocation) -> (RegId, DataLocation) { - for id in (0..).map(RegId::from) { - match self.0.entry(id) { - std::collections::btree_map::Entry::Occupied(_) => continue, - std::collections::btree_map::Entry::Vacant(entry) => { - entry.insert(location.clone()); - return (id, location); - } - } - } - unreachable!() - } - - pub fn relocate(&mut self, id: RegId, location: DataLocation) -> Result<(), CompileError> { - if self.0.insert(id, location).is_none() { - return Err(unimplemented_err!(format!("INTERNAL COMPILER ERROR: try to relocate data, but the id: {:?} was empty in location map.", id))); - } - Ok(()) - } - - pub fn delete(&mut self, id: RegId) -> Result<(), CompileError> { - if self.0.remove(&id).is_none() { - return Err(unimplemented_err!(format!("INTERNAL COMPILER ERROR: try to delete data from location map, but the id: {:?} was empty in location map.", id))); - } - Ok(()) - } - - pub fn get(&self, id: &RegId) -> Option<&DataLocation> { - self.0.get(id) - } - - pub fn replace(&mut self, id1: RegId, id2: RegId) -> Result<(), CompileError> { - let entry1 = match self.0.entry(id1) { - std::collections::btree_map::Entry::Vacant(_) => { - return Err(unimplemented_err!(format!( - "INTERNAL COMPILER ERROR: try to replace {:?}, {:?} but {:?} was empty", - id1, id2, id1 - ))) - } - std::collections::btree_map::Entry::Occupied(entry) => entry, - }; - let id1_location = entry1.get().clone(); - let location2 = match self.0.insert(id2, id1_location) { - Some(location2) => location2, - None => { - return Err(unimplemented_err!(format!( - "INTERNAL COMPILER ERROR: try to replace {:?}, {:?} but {:?} was empty", - id1, id2, id1 - ))) - } - }; - self.0.insert(id1, location2); - - Ok(()) - } -} - -#[derive(PartialOrd, Ord, PartialEq, Eq, Clone, Debug)] -pub struct StackDepthTable { - reg_ids: Vec>, -} - -#[derive(PartialOrd, Ord, PartialEq, Eq, Debug)] -pub struct StackDepthTableEntry<'a> { - stack_reg_idx: StackRegIdx, - stack: &'a mut Vec>, -} - -impl<'a> StackDepthTableEntry<'a> { - pub fn new( - stack_reg_idx: StackRegIdx, - reg_ids: &'a mut Vec>, - ) -> Result { - if reg_ids.get(stack_reg_idx.0).is_none() { - return Err(unimplemented_err!( - "INTERNAL COMPILER ERROR: try to create StackDepthTableEntry, but failed" - )); - } - Ok(Self { - stack_reg_idx, - stack: reg_ids, - }) - } - - pub fn as_location(&self) -> DataLocation { - DataLocation::Stack(self.stack_reg_idx) - } - - pub fn insert(&mut self, id: RegId) { - *(self.stack.get_mut(self.stack_reg_idx.0).unwrap()) = Some(id); - // TODO: StackDepthTableEntry について再考 - } - - pub fn deallocate(&mut self) { - *(self.stack.get_mut(self.stack_reg_idx.0).unwrap()) = None; - } -} - -#[derive(PartialOrd, Ord, PartialEq, Eq, Debug)] -pub enum RegIdEntry<'a> { - Reg(AllocatedRegisterTableEntry<'a>), - Stack(StackDepthTableEntry<'a>), -} -impl<'a> From> for RegIdEntry<'a> { - fn from(entry: AllocatedRegisterTableEntry<'a>) -> Self { - RegIdEntry::Reg(entry) - } -} - -impl<'a> From> for RegIdEntry<'a> { - fn from(entry: StackDepthTableEntry<'a>) -> Self { - RegIdEntry::Stack(entry) - } -} - -impl<'a> RegIdEntry<'a> { - pub fn insert(&mut self, id: RegId) { - match self { - RegIdEntry::Reg(entry) => entry.insert(id), - RegIdEntry::Stack(entry) => entry.insert(id), - } - } - - pub fn as_location(&self) -> DataLocation { - match self { - RegIdEntry::Reg(entry) => entry.as_location(), - RegIdEntry::Stack(entry) => entry.as_location(), - } - } - - pub fn deallocate(&mut self) { - match self { - RegIdEntry::Reg(entry) => entry.deallocate(), - RegIdEntry::Stack(entry) => entry.deallocate(), - } - } -} - -impl StackDepthTable { - pub fn new() -> Self { - Self { - reg_ids: Vec::new(), - } - } - - pub fn allocate_entry(&mut self) -> Result { - let stack_reg_idx = self.reg_ids.len(); - self.reg_ids.push(None); - StackDepthTableEntry::new(StackRegIdx(stack_reg_idx), &mut self.reg_ids) - } - - pub fn allocate_entry_of_specific_depth( - &mut self, - stack_reg_idx: StackRegIdx, - ) -> Result { - self.ensures_accessable_idx(stack_reg_idx.as_idx()); - if let Some(maybe_stack_reg_idx) = self.reg_ids.get(stack_reg_idx.as_idx()) { - if maybe_stack_reg_idx.is_some() { - return Err(unimplemented_err!(format!("INTERNAL COMPILER ERROR: try to allocate specific depth in stack area, the specified stack_reg_idx: {:?} was already used", stack_reg_idx))); - } - } else { - unreachable!(); - } - StackDepthTableEntry::new(stack_reg_idx, &mut self.reg_ids) - } - - pub fn ensures_accessable_idx(&mut self, idx: usize) { - for idx in 0..=idx { - if self.reg_ids.get(idx).is_none() { - self.reg_ids.push(None); - } - } - } - - pub fn get(&self, idx: StackRegIdx) -> Option<&Option> { - self.reg_ids.get(idx.as_idx()) - } - - pub fn get_vec(&self) -> &Vec> { - &self.reg_ids - } - - pub fn get_vec_mut(&mut self) -> &mut Vec> { - &mut self.reg_ids - } - - pub fn deallocate_all(&mut self) { - self.reg_ids.clear(); - } -} - -#[derive(PartialOrd, Ord, PartialEq, Eq, Clone, Debug)] -pub struct RegisterAllocator { - location_map: LocationMap, - allocated: AllocatedRegisterTable, - depth: StackDepthTable, -} - -impl RegisterAllocator { - pub fn new() -> Self { - Self { - location_map: LocationMap::new(), - allocated: AllocatedRegisterTable::new(), - depth: StackDepthTable::new(), - } - } - - /// High Layer abstraction of relocate data to specific register - pub fn assign( - &mut self, - id: RegId, - assign_to_location: DataLocation, - ) -> Result<(), CompileError> { - match self.location_map.get(&id) { - Some(&prev_allocated_location) if prev_allocated_location == assign_to_location => { - // already assigned - Ok(()) - } - Some(prev_location) => { - if let Some(using_assign_to_location_id) = - self.look_up_reg_id_from_location(assign_to_location) - { - // prev state | next state - // id -> prev_location | id -> assign_to - // using_assign_to_location_id -> assign_to | using_assign_to_location_id -> prev_location - self.replace( - (id, *prev_location), - (*using_assign_to_location_id, assign_to_location), - )?; - } else { - // prev state | next state - // id -> prev_location | id -> assign_to - // None -> assign_to | None -> prev_location (deallocated) - // TODO: entry のデータ構造を変えてもっと抽象化する - let mut prev_location_entry = self.from_location_to_entry(*prev_location)?; - prev_location_entry.deallocate(); - let mut assign_to_entry = self.from_location_to_entry(assign_to_location)?; - assign_to_entry.insert(id); - self.location_map.relocate(id, assign_to_location)?; - } - Ok(()) - } - None => Err(CompileError::new_register_assign_error( - id, - assign_to_location, - "given RegId is not yet allocated.", - )), - } - } - - /// free all allocated stacks - pub fn deallocate_stacks(&mut self) { - self.depth.deallocate_all(); - } - - /// subscribe unmanaged place for this RegisterAllocator - pub fn publish_reg_id(&mut self, location: DataLocation) -> Result { - let (id, location) = self.location_map.allocate(location); - match location { - DataLocation::Register(reg) => { - if let Some(mut entry) = self.allocated.allocate_entry(reg) { - entry.insert(id); - } else { - return Err(unimplemented_err!(format!("INTERNAL COMPILER ERROR: try to subscribe specified location: {:?}, but the location was already used", location))); - } - } - DataLocation::Stack(stack_reg_idx) => { - let mut entry = self.depth.allocate_entry_of_specific_depth(stack_reg_idx)?; - entry.insert(id); - } - } - Ok(id) - } - - fn look_up_reg_id_from_location(&self, location: DataLocation) -> Option<&RegId> { - match location { - DataLocation::Register(reg) => self.allocated.get(reg).as_ref(), - DataLocation::Stack(stack_reg_idx) => self - .depth - .get(stack_reg_idx) - .and_then(|maybe_reg_id| maybe_reg_id.as_ref()), - } - } - - pub fn look_up_location_from_reg_id(&self, reg_id: &RegId) -> Option<&DataLocation> { - self.location_map.get(reg_id) - } - - fn from_location_to_entry( - &mut self, - location: DataLocation, - ) -> Result { - Ok(match location { - DataLocation::Register(reg) => RegIdEntry::Reg(AllocatedRegisterTableEntry { - kind: reg, - entry: self.allocated.get_mut(reg), - }), - DataLocation::Stack(stack_reg_idx) => { - self.depth.ensures_accessable_idx(stack_reg_idx.as_idx()); - RegIdEntry::Stack(StackDepthTableEntry::new( - stack_reg_idx, - self.depth.get_vec_mut(), - )?) - } - }) - } - - fn replace( - &mut self, - (id1, location1): (RegId, DataLocation), - (id2, location2): (RegId, DataLocation), - ) -> Result<(), CompileError> { - let mut entry_at_location1 = self.from_location_to_entry(location1)?; - entry_at_location1.insert(id2); - let mut entry_at_location2 = self.from_location_to_entry(location2)?; - entry_at_location2.insert(id1); - self.location_map.replace(id1, id2)?; - Ok(()) - } - - fn allocate_any_entry(&mut self) -> Result { - if let Some(entry) = self.allocated.allocate_any_entry()? { - Ok(entry.into()) - } else { - Ok(self.depth.allocate_entry()?.into()) - } - } - - /// High Layer abstraction of allocate to specific register - pub fn allocate_if_possible(&mut self, reg: RegKind) -> Option<(RegId, DataLocation)> { - if let Some(mut entry) = self.allocated.allocate_entry(reg) { - let (id, location) = self.location_map.allocate(DataLocation::Register(reg)); - entry.insert(id); - Some((id, location)) - } else { - None - } - } - - /// High Layer abstraction of allocate to some location - pub fn allocate( - &mut self, - prioritized_reg: Option, - ) -> Result<(RegId, DataLocation), CompileError> { - if let Some(prioritized_reg) = prioritized_reg { - if let Some((id, location)) = self.allocate_if_possible(prioritized_reg) { - return Ok((id, location)); - } - } - /* allocate any entry inlined */ - let mut entry: RegIdEntry = if let Some(entry) = self.allocated.allocate_any_entry()? { - entry.into() - } else { - self.depth.allocate_entry()?.into() - }; - /* allocate any entry end */ - let location = entry.as_location(); - let (id, location) = self.location_map.allocate(location); - entry.insert(id); - Ok((id, location)) - } -} - -#[cfg(test)] -mod register_allocator_test { - use crate::generate::registers::NUM_REGISTER; - - use super::*; - #[test] - pub fn allocate_register_test() { - let mut allocator = RegisterAllocator::new(); - let (reg_id, location) = allocator.allocate(Some(RegKind::Rax)).unwrap(); - assert_eq!(location, DataLocation::Register(RegKind::Rax)); - assert_eq!( - *allocator.look_up_reg_id_from_location(location).unwrap(), - reg_id - ); - assert_eq!( - *allocator.look_up_location_from_reg_id(®_id).unwrap(), - location - ); - } - - #[test] - pub fn allocate_same_place_test() { - let mut allocator = RegisterAllocator::new(); - let (reg_id1, location1) = allocator.allocate(Some(RegKind::Rax)).unwrap(); - assert_eq!(location1, DataLocation::Register(RegKind::Rax)); - assert_eq!( - *allocator.look_up_reg_id_from_location(location1).unwrap(), - reg_id1 - ); - assert_eq!( - *allocator.look_up_location_from_reg_id(®_id1).unwrap(), - location1 - ); - let (reg_id2, location2) = allocator.allocate(Some(RegKind::Rax)).unwrap(); - assert_eq!(location1, DataLocation::Register(RegKind::Rax)); - assert_ne!(location2, DataLocation::Register(RegKind::Rax)); - assert_eq!( - *allocator.look_up_reg_id_from_location(location2).unwrap(), - reg_id2 - ); - assert_eq!( - *allocator.look_up_location_from_reg_id(®_id2).unwrap(), - location2 - ); - } - - #[test] - pub fn assign_test() { - let mut allocator = RegisterAllocator::new(); - let (reg_id1, location1) = allocator.allocate(Some(RegKind::Rax)).unwrap(); - assert_eq!(location1, DataLocation::Register(RegKind::Rax)); - let (reg_id2, location2) = allocator.allocate(Some(RegKind::Rax)).unwrap(); - allocator - .assign(reg_id2, DataLocation::Register(RegKind::Rax)) - .unwrap(); - assert_eq!( - *allocator.look_up_location_from_reg_id(®_id2).unwrap(), - DataLocation::Register(RegKind::Rax) - ); - assert_eq!( - *allocator.look_up_location_from_reg_id(®_id1).unwrap(), - location2 - ); - } - - #[test] - pub fn assign_to_not_allocated_location() { - let mut allocator = RegisterAllocator::new(); - let (reg_id1, _) = allocator.allocate(Some(RegKind::Rax)).unwrap(); - allocator - .assign(reg_id1, DataLocation::Register(RegKind::Rdi)) - .unwrap(); - assert_eq!( - *allocator.look_up_location_from_reg_id(®_id1).unwrap(), - DataLocation::Register(RegKind::Rdi) - ); - assert_eq!( - allocator.look_up_reg_id_from_location(DataLocation::Register(RegKind::Rax)), - None - ); - } - - #[test] - pub fn stack_allocate_test() { - let mut allocator = RegisterAllocator::new(); - let (rax_id, rax_location) = allocator.allocate(Some(RegKind::Rax)).unwrap(); - assert_eq!(rax_location, DataLocation::Register(RegKind::Rax)); - for _ in 0..(NUM_REGISTER - 1) { - allocator.allocate(None).unwrap(); - } - // all register might be allocated - for maybe_reg_id in allocator.allocated.0 { - eprintln!("{:?}", maybe_reg_id); - assert!(maybe_reg_id.is_some()); - } - - // allocate stack - let (maybe_in_stack_reg_id, maybe_stack_location) = allocator.allocate(None).unwrap(); - assert_eq!(maybe_stack_location, DataLocation::Stack(StackRegIdx(0))); - assert_eq!( - *allocator - .look_up_reg_id_from_location(maybe_stack_location) - .unwrap(), - maybe_in_stack_reg_id - ); - assert_eq!( - *allocator - .look_up_location_from_reg_id(&maybe_in_stack_reg_id) - .unwrap(), - maybe_stack_location - ); - - // assign value on stack to specific register - allocator - .assign(maybe_in_stack_reg_id, DataLocation::Register(RegKind::Rax)) - .unwrap(); - assert_eq!( - *allocator - .look_up_reg_id_from_location(maybe_stack_location) - .unwrap(), - rax_id - ); - assert_eq!( - *allocator.look_up_location_from_reg_id(&rax_id).unwrap(), - maybe_stack_location - ); - assert_eq!( - *allocator - .look_up_reg_id_from_location(DataLocation::Register(RegKind::Rax)) - .unwrap(), - maybe_in_stack_reg_id - ); - assert_eq!( - *allocator - .look_up_location_from_reg_id(&maybe_in_stack_reg_id) - .unwrap(), - DataLocation::Register(RegKind::Rax) - ); - } - - #[test] - pub fn publish_reg_id_test() { - let mut allocator = RegisterAllocator::new(); - let (rax_id, _) = allocator.allocate(Some(RegKind::Rax)).unwrap(); - let rdi_id = allocator.publish_reg_id(RegKind::Rdi.into()).unwrap(); - assert_eq!(rdi_id, RegId(1)); - assert_eq!( - *allocator.look_up_location_from_reg_id(&rdi_id).unwrap(), - DataLocation::Register(RegKind::Rdi) - ); - assert!(allocator.publish_reg_id(RegKind::Rax.into()).is_err()); - allocator - .assign(rax_id, DataLocation::new_from_depth(0).unwrap()) - .unwrap(); - let stack_16_id = allocator - .publish_reg_id(DataLocation::new_from_depth(16).unwrap()) - .unwrap(); - assert_eq!( - *allocator - .look_up_location_from_reg_id(&stack_16_id) - .unwrap(), - DataLocation::new_from_depth(16).unwrap() - ); - assert!(allocator - .publish_reg_id(DataLocation::new_from_depth(0).unwrap()) - .is_err()); - assert!(allocator - .publish_reg_id(DataLocation::new_from_depth(8).unwrap()) - .is_ok()); - assert!(allocator - .publish_reg_id(DataLocation::new_from_depth(16).unwrap()) - .is_err()); - } -} - -#[test] -pub fn deallocate_stacks_test() { - let mut allocator = RegisterAllocator::new(); - let id1 = allocator - .publish_reg_id(DataLocation::new_from_depth(0).unwrap()) - .unwrap(); - assert_eq!( - allocator.look_up_reg_id_from_location(DataLocation::new_from_depth(0).unwrap()), - Some(&id1) - ); - let (id2, _) = allocator.allocate_if_possible(RegKind::Rax.into()).unwrap(); - let _id3 = allocator - .publish_reg_id(DataLocation::new_from_depth(8).unwrap()) - .unwrap(); - allocator.deallocate_stacks(); - assert_eq!( - allocator.look_up_reg_id_from_location(RegKind::Rax.into()), - Some(&id2) - ); - - assert_eq!( - allocator.look_up_reg_id_from_location(DataLocation::new_from_depth(0).unwrap()), - None - ); - - assert_eq!( - allocator.look_up_reg_id_from_location(DataLocation::new_from_depth(8).unwrap()), - None - ); -} +pub mod allocated_register_table; +pub mod allocator; +pub mod data_location; +pub mod location_map; +pub mod regid_entry; +pub mod stack_depth_table; +pub mod stacks; diff --git a/src/generate/register_allocator/allocated_register_table.rs b/src/generate/register_allocator/allocated_register_table.rs new file mode 100644 index 0000000..f7f67fd --- /dev/null +++ b/src/generate/register_allocator/allocated_register_table.rs @@ -0,0 +1,126 @@ +use crate::{ + error::CompileError, + generate::registers::{RegKind, NUM_REGISTER}, + unimplemented_err, +}; + +use super::data_location::{DataLocation, RegId}; + +#[derive(PartialOrd, Ord, PartialEq, Eq, Clone, Debug)] +pub struct AllocatedRegisterTable([Option; NUM_REGISTER]); + +impl AllocatedRegisterTable { + pub fn new() -> Self { + Self([None; NUM_REGISTER]) + } + + pub fn iter(&self) -> impl Iterator> { + self.0.iter() + } +} + +#[derive(PartialOrd, Ord, PartialEq, Eq, Debug)] +pub struct AllocatedRegisterTableEntry<'a> { + pub(super) kind: RegKind, + pub(super) entry: &'a mut Option, +} + +impl<'a> AllocatedRegisterTableEntry<'a> { + pub fn new(kind: RegKind, muttable_ref: &'a mut Option) -> Self { + Self { + kind, + entry: muttable_ref, + } + } + + pub fn kind(&self) -> RegKind { + self.kind + } + + pub fn as_location(&self) -> DataLocation { + DataLocation::Register(self.kind) + } + + pub fn insert(&mut self, id: RegId) { + *(self.entry) = Some(id); + } + + pub fn deallocate(&mut self) { + *(self.entry) = None; + } +} + +impl AllocatedRegisterTable { + pub fn record_as_allocated(&mut self, reg: RegKind, id: RegId) -> Result<(), CompileError> { + if self.0[reg as usize].is_some() { + return Err(unimplemented_err!(format!( + "INTERNAL COMPILER ERROR: try to allocate register {}, but it was already used.", + reg.qword() + ))); + } else { + self.0[reg as usize] = Some(id); + } + + Ok(()) + } + + pub fn deallocate(&mut self, reg: RegKind) -> Result { + match self.0[reg as usize] { + Some(prev_id) => { + self.0[reg as usize] = None; + Ok(prev_id) + }, + None => Err(unimplemented_err!(format!( + "INTERNAL COMPILER ERROR: try to deallocate register {}, but it was already deallocated.", + reg.qword() + ))), + } + } + + pub fn allocate_any_entry( + &mut self, + ) -> Result, CompileError> { + let idx = self + .0 + .iter() + .enumerate() + .filter_map(|(idx, &allocated)| if allocated.is_some() { None } else { Some(idx) }) + .next(); + idx.map(|idx| { + Ok(AllocatedRegisterTableEntry::new( + RegKind::try_from(idx)?, + &mut self.0[idx], + )) + }) + .transpose() + } + + pub fn allocate_any(&mut self, id: RegId) -> Result, CompileError> { + Ok(if let Some(mut entry) = self.allocate_any_entry()? { + entry.insert(id); + Some(entry.kind()) + } else { + None + }) + } + + /// try to allocate given register return Entry if successfully allocated. + pub fn allocate_entry(&mut self, reg: RegKind) -> Option { + if self.0[reg as usize].is_none() { + Some(AllocatedRegisterTableEntry::new( + reg, + &mut self.0[reg as usize], + )) + } else { + None + } + } + + pub fn get(&self, reg: RegKind) -> &Option { + &self.0[reg as usize] + } + + pub fn get_mut(&mut self, reg: RegKind) -> &mut Option { + &mut self.0[reg as usize] + } +} diff --git a/src/generate/register_allocator/allocator.rs b/src/generate/register_allocator/allocator.rs new file mode 100644 index 0000000..a3390d4 --- /dev/null +++ b/src/generate/register_allocator/allocator.rs @@ -0,0 +1,392 @@ +use crate::{error::CompileError, generate::registers::RegKind, unimplemented_err}; + +use super::{ + allocated_register_table::{AllocatedRegisterTable, AllocatedRegisterTableEntry}, + data_location::{DataLocation, RegId}, + location_map::LocationMap, + regid_entry::RegIdEntry, + stack_depth_table::{StackDepthTable, StackDepthTableEntry}, +}; + +#[derive(PartialOrd, Ord, PartialEq, Eq, Clone, Debug)] +pub struct RegisterAllocator { + location_map: LocationMap, + allocated: AllocatedRegisterTable, + depth: StackDepthTable, +} + +impl RegisterAllocator { + pub fn new() -> Self { + Self { + location_map: LocationMap::new(), + allocated: AllocatedRegisterTable::new(), + depth: StackDepthTable::new(), + } + } + + /// High Layer abstraction of relocate data to specific register + pub fn assign( + &mut self, + id: RegId, + assign_to_location: DataLocation, + ) -> Result<(), CompileError> { + match self.location_map.get(&id) { + Some(&prev_allocated_location) if prev_allocated_location == assign_to_location => { + // already assigned + Ok(()) + } + Some(prev_location) => { + if let Some(using_assign_to_location_id) = + self.look_up_reg_id_from_location(assign_to_location) + { + // prev state | next state + // id -> prev_location | id -> assign_to + // using_assign_to_location_id -> assign_to | using_assign_to_location_id -> prev_location + self.replace( + (id, *prev_location), + (*using_assign_to_location_id, assign_to_location), + )?; + } else { + // prev state | next state + // id -> prev_location | id -> assign_to + // None -> assign_to | None -> prev_location (deallocated) + // TODO: entry のデータ構造を変えてもっと抽象化する + let mut prev_location_entry = self.from_location_to_entry(*prev_location)?; + prev_location_entry.deallocate(); + let mut assign_to_entry = self.from_location_to_entry(assign_to_location)?; + assign_to_entry.insert(id); + self.location_map.relocate(id, assign_to_location)?; + } + Ok(()) + } + None => Err(CompileError::new_register_assign_error( + id, + assign_to_location, + "given RegId is not yet allocated.", + )), + } + } + + /// free all allocated stacks + pub fn deallocate_stacks(&mut self) { + self.depth.deallocate_all(); + } + + /// subscribe unmanaged place for this RegisterAllocator + pub fn publish_reg_id(&mut self, location: DataLocation) -> Result { + let (id, location) = self.location_map.allocate(location); + match location { + DataLocation::Register(reg) => { + if let Some(mut entry) = self.allocated.allocate_entry(reg) { + entry.insert(id); + } else { + return Err(unimplemented_err!(format!("INTERNAL COMPILER ERROR: try to subscribe specified location: {:?}, but the location was already used", location))); + } + } + DataLocation::Stack(stack_reg_idx) => { + let mut entry = self.depth.allocate_entry_of_specific_depth(stack_reg_idx)?; + entry.insert(id); + } + } + Ok(id) + } + + fn look_up_reg_id_from_location(&self, location: DataLocation) -> Option<&RegId> { + match location { + DataLocation::Register(reg) => self.allocated.get(reg).as_ref(), + DataLocation::Stack(stack_reg_idx) => self + .depth + .get(stack_reg_idx) + .and_then(|maybe_reg_id| maybe_reg_id.as_ref()), + } + } + + pub fn look_up_location_from_reg_id(&self, reg_id: &RegId) -> Option<&DataLocation> { + self.location_map.get(reg_id) + } + + fn from_location_to_entry( + &mut self, + location: DataLocation, + ) -> Result { + Ok(match location { + DataLocation::Register(reg) => RegIdEntry::Reg(AllocatedRegisterTableEntry { + kind: reg, + entry: self.allocated.get_mut(reg), + }), + DataLocation::Stack(stack_reg_idx) => { + self.depth.ensures_accessable_idx(stack_reg_idx.as_idx()); + RegIdEntry::Stack(StackDepthTableEntry::new( + stack_reg_idx, + self.depth.get_vec_mut(), + )?) + } + }) + } + + fn replace( + &mut self, + (id1, location1): (RegId, DataLocation), + (id2, location2): (RegId, DataLocation), + ) -> Result<(), CompileError> { + let mut entry_at_location1 = self.from_location_to_entry(location1)?; + entry_at_location1.insert(id2); + let mut entry_at_location2 = self.from_location_to_entry(location2)?; + entry_at_location2.insert(id1); + self.location_map.replace(id1, id2)?; + Ok(()) + } + + fn allocate_any_entry(&mut self) -> Result { + if let Some(entry) = self.allocated.allocate_any_entry()? { + Ok(entry.into()) + } else { + Ok(self.depth.allocate_entry()?.into()) + } + } + + /// High Layer abstraction of allocate to specific register + pub fn allocate_if_possible(&mut self, reg: RegKind) -> Option<(RegId, DataLocation)> { + if let Some(mut entry) = self.allocated.allocate_entry(reg) { + let (id, location) = self.location_map.allocate(DataLocation::Register(reg)); + entry.insert(id); + Some((id, location)) + } else { + None + } + } + + /// High Layer abstraction of allocate to some location + pub fn allocate( + &mut self, + prioritized_reg: Option, + ) -> Result<(RegId, DataLocation), CompileError> { + if let Some(prioritized_reg) = prioritized_reg { + if let Some((id, location)) = self.allocate_if_possible(prioritized_reg) { + return Ok((id, location)); + } + } + /* allocate any entry inlined */ + let mut entry: RegIdEntry = if let Some(entry) = self.allocated.allocate_any_entry()? { + entry.into() + } else { + self.depth.allocate_entry()?.into() + }; + /* allocate any entry end */ + let location = entry.as_location(); + let (id, location) = self.location_map.allocate(location); + entry.insert(id); + Ok((id, location)) + } +} + +#[cfg(test)] +mod register_allocator_test { + use crate::generate::{ + register_allocator::data_location::StackRegIdx, registers::NUM_REGISTER, + }; + + use super::*; + #[test] + pub fn allocate_register_test() { + let mut allocator = RegisterAllocator::new(); + let (reg_id, location) = allocator.allocate(Some(RegKind::Rax)).unwrap(); + assert_eq!(location, DataLocation::Register(RegKind::Rax)); + assert_eq!( + *allocator.look_up_reg_id_from_location(location).unwrap(), + reg_id + ); + assert_eq!( + *allocator.look_up_location_from_reg_id(®_id).unwrap(), + location + ); + } + + #[test] + pub fn allocate_same_place_test() { + let mut allocator = RegisterAllocator::new(); + let (reg_id1, location1) = allocator.allocate(Some(RegKind::Rax)).unwrap(); + assert_eq!(location1, DataLocation::Register(RegKind::Rax)); + assert_eq!( + *allocator.look_up_reg_id_from_location(location1).unwrap(), + reg_id1 + ); + assert_eq!( + *allocator.look_up_location_from_reg_id(®_id1).unwrap(), + location1 + ); + let (reg_id2, location2) = allocator.allocate(Some(RegKind::Rax)).unwrap(); + assert_eq!(location1, DataLocation::Register(RegKind::Rax)); + assert_ne!(location2, DataLocation::Register(RegKind::Rax)); + assert_eq!( + *allocator.look_up_reg_id_from_location(location2).unwrap(), + reg_id2 + ); + assert_eq!( + *allocator.look_up_location_from_reg_id(®_id2).unwrap(), + location2 + ); + } + + #[test] + pub fn assign_test() { + let mut allocator = RegisterAllocator::new(); + let (reg_id1, location1) = allocator.allocate(Some(RegKind::Rax)).unwrap(); + assert_eq!(location1, DataLocation::Register(RegKind::Rax)); + let (reg_id2, location2) = allocator.allocate(Some(RegKind::Rax)).unwrap(); + allocator + .assign(reg_id2, DataLocation::Register(RegKind::Rax)) + .unwrap(); + assert_eq!( + *allocator.look_up_location_from_reg_id(®_id2).unwrap(), + DataLocation::Register(RegKind::Rax) + ); + assert_eq!( + *allocator.look_up_location_from_reg_id(®_id1).unwrap(), + location2 + ); + } + + #[test] + pub fn assign_to_not_allocated_location() { + let mut allocator = RegisterAllocator::new(); + let (reg_id1, _) = allocator.allocate(Some(RegKind::Rax)).unwrap(); + allocator + .assign(reg_id1, DataLocation::Register(RegKind::Rdi)) + .unwrap(); + assert_eq!( + *allocator.look_up_location_from_reg_id(®_id1).unwrap(), + DataLocation::Register(RegKind::Rdi) + ); + assert_eq!( + allocator.look_up_reg_id_from_location(DataLocation::Register(RegKind::Rax)), + None + ); + } + + #[test] + pub fn stack_allocate_test() { + let mut allocator = RegisterAllocator::new(); + let (rax_id, rax_location) = allocator.allocate(Some(RegKind::Rax)).unwrap(); + assert_eq!(rax_location, DataLocation::Register(RegKind::Rax)); + for _ in 0..(NUM_REGISTER - 1) { + allocator.allocate(None).unwrap(); + } + // all register might be allocated + for maybe_reg_id in allocator.allocated.iter() { + eprintln!("{:?}", maybe_reg_id); + assert!(maybe_reg_id.is_some()); + } + + // allocate stack + let (maybe_in_stack_reg_id, maybe_stack_location) = allocator.allocate(None).unwrap(); + assert_eq!( + maybe_stack_location, + DataLocation::Stack(StackRegIdx::from_idx(0)) + ); + assert_eq!( + *allocator + .look_up_reg_id_from_location(maybe_stack_location) + .unwrap(), + maybe_in_stack_reg_id + ); + assert_eq!( + *allocator + .look_up_location_from_reg_id(&maybe_in_stack_reg_id) + .unwrap(), + maybe_stack_location + ); + + // assign value on stack to specific register + allocator + .assign(maybe_in_stack_reg_id, DataLocation::Register(RegKind::Rax)) + .unwrap(); + assert_eq!( + *allocator + .look_up_reg_id_from_location(maybe_stack_location) + .unwrap(), + rax_id + ); + assert_eq!( + *allocator.look_up_location_from_reg_id(&rax_id).unwrap(), + maybe_stack_location + ); + assert_eq!( + *allocator + .look_up_reg_id_from_location(DataLocation::Register(RegKind::Rax)) + .unwrap(), + maybe_in_stack_reg_id + ); + assert_eq!( + *allocator + .look_up_location_from_reg_id(&maybe_in_stack_reg_id) + .unwrap(), + DataLocation::Register(RegKind::Rax) + ); + } + + #[test] + pub fn publish_reg_id_test() { + let mut allocator = RegisterAllocator::new(); + let (rax_id, _) = allocator.allocate(Some(RegKind::Rax)).unwrap(); + let rdi_id = allocator.publish_reg_id(RegKind::Rdi.into()).unwrap(); + assert_eq!(rdi_id, RegId::from(1)); + assert_eq!( + *allocator.look_up_location_from_reg_id(&rdi_id).unwrap(), + DataLocation::Register(RegKind::Rdi) + ); + assert!(allocator.publish_reg_id(RegKind::Rax.into()).is_err()); + allocator + .assign(rax_id, DataLocation::new_from_depth(0).unwrap()) + .unwrap(); + let stack_16_id = allocator + .publish_reg_id(DataLocation::new_from_depth(16).unwrap()) + .unwrap(); + assert_eq!( + *allocator + .look_up_location_from_reg_id(&stack_16_id) + .unwrap(), + DataLocation::new_from_depth(16).unwrap() + ); + assert!(allocator + .publish_reg_id(DataLocation::new_from_depth(0).unwrap()) + .is_err()); + assert!(allocator + .publish_reg_id(DataLocation::new_from_depth(8).unwrap()) + .is_ok()); + assert!(allocator + .publish_reg_id(DataLocation::new_from_depth(16).unwrap()) + .is_err()); + } +} + +#[test] +pub fn deallocate_stacks_test() { + let mut allocator = RegisterAllocator::new(); + let id1 = allocator + .publish_reg_id(DataLocation::new_from_depth(0).unwrap()) + .unwrap(); + assert_eq!( + allocator.look_up_reg_id_from_location(DataLocation::new_from_depth(0).unwrap()), + Some(&id1) + ); + let (id2, _) = allocator.allocate_if_possible(RegKind::Rax.into()).unwrap(); + let _id3 = allocator + .publish_reg_id(DataLocation::new_from_depth(8).unwrap()) + .unwrap(); + allocator.deallocate_stacks(); + assert_eq!( + allocator.look_up_reg_id_from_location(RegKind::Rax.into()), + Some(&id2) + ); + + assert_eq!( + allocator.look_up_reg_id_from_location(DataLocation::new_from_depth(0).unwrap()), + None + ); + + assert_eq!( + allocator.look_up_reg_id_from_location(DataLocation::new_from_depth(8).unwrap()), + None + ); +} diff --git a/src/generate/register_allocator/data_location.rs b/src/generate/register_allocator/data_location.rs new file mode 100644 index 0000000..c02c6f3 --- /dev/null +++ b/src/generate/register_allocator/data_location.rs @@ -0,0 +1,55 @@ +use crate::{ + error::CompileError, + generate::registers::{RegKind, NUM_REGISTER}, + unimplemented_err, +}; + +#[derive(PartialOrd, Ord, PartialEq, Eq, Clone, Copy, Debug)] +pub struct RegId(usize); + +impl From for RegId { + fn from(value: usize) -> Self { + RegId(value) + } +} + +#[derive(PartialOrd, Ord, PartialEq, Eq, Clone, Copy, Debug)] +pub struct StackRegIdx(usize); + +impl StackRegIdx { + pub fn as_depth(&self) -> usize { + self.0 * 8 + } + + pub fn as_idx(&self) -> usize { + self.0 + } + + pub fn from_idx(idx: usize) -> Self { + Self(idx) + } +} + +#[derive(PartialOrd, Ord, PartialEq, Eq, Clone, Copy, Debug)] +pub enum DataLocation { + Register(RegKind), + Stack(StackRegIdx), +} + +impl From for DataLocation { + fn from(reg: RegKind) -> Self { + DataLocation::Register(reg) + } +} + +impl DataLocation { + pub fn new_from_depth(depth: usize) -> Result { + if depth % 8 != 0 { + return Err(unimplemented_err!(format!( + "try to new DataLocation from depth, but the provided depth was not multiple of 8." + ))); + } + + Ok(DataLocation::Stack(StackRegIdx(depth / 8))) + } +} diff --git a/src/generate/register_allocator/location_map.rs b/src/generate/register_allocator/location_map.rs new file mode 100644 index 0000000..ec9f2c0 --- /dev/null +++ b/src/generate/register_allocator/location_map.rs @@ -0,0 +1,71 @@ +use std::collections::BTreeMap; + +use crate::{error::CompileError, unimplemented_err}; + +use super::data_location::{DataLocation, RegId}; + +#[derive(PartialOrd, Ord, PartialEq, Eq, Clone, Debug)] +pub struct LocationMap(BTreeMap); + +impl LocationMap { + pub fn new() -> Self { + Self(BTreeMap::new()) + } + + /// Call this function under the consumption that the location is not occupied. Check it by looking at AllocatedRegisterTable + pub fn allocate(&mut self, location: DataLocation) -> (RegId, DataLocation) { + for id in (0..).map(RegId::from) { + match self.0.entry(id) { + std::collections::btree_map::Entry::Occupied(_) => continue, + std::collections::btree_map::Entry::Vacant(entry) => { + entry.insert(location.clone()); + return (id, location); + } + } + } + unreachable!() + } + + pub fn relocate(&mut self, id: RegId, location: DataLocation) -> Result<(), CompileError> { + if self.0.insert(id, location).is_none() { + return Err(unimplemented_err!(format!("INTERNAL COMPILER ERROR: try to relocate data, but the id: {:?} was empty in location map.", id))); + } + Ok(()) + } + + pub fn delete(&mut self, id: RegId) -> Result<(), CompileError> { + if self.0.remove(&id).is_none() { + return Err(unimplemented_err!(format!("INTERNAL COMPILER ERROR: try to delete data from location map, but the id: {:?} was empty in location map.", id))); + } + Ok(()) + } + + pub fn get(&self, id: &RegId) -> Option<&DataLocation> { + self.0.get(id) + } + + pub fn replace(&mut self, id1: RegId, id2: RegId) -> Result<(), CompileError> { + let entry1 = match self.0.entry(id1) { + std::collections::btree_map::Entry::Vacant(_) => { + return Err(unimplemented_err!(format!( + "INTERNAL COMPILER ERROR: try to replace {:?}, {:?} but {:?} was empty", + id1, id2, id1 + ))) + } + std::collections::btree_map::Entry::Occupied(entry) => entry, + }; + let id1_location = entry1.get().clone(); + let location2 = match self.0.insert(id2, id1_location) { + Some(location2) => location2, + None => { + return Err(unimplemented_err!(format!( + "INTERNAL COMPILER ERROR: try to replace {:?}, {:?} but {:?} was empty", + id1, id2, id1 + ))) + } + }; + self.0.insert(id1, location2); + + Ok(()) + } +} diff --git a/src/generate/register_allocator/regid_entry.rs b/src/generate/register_allocator/regid_entry.rs new file mode 100644 index 0000000..6b37427 --- /dev/null +++ b/src/generate/register_allocator/regid_entry.rs @@ -0,0 +1,45 @@ +use super::{ + allocated_register_table::AllocatedRegisterTableEntry, + data_location::{DataLocation, RegId}, + stack_depth_table::StackDepthTableEntry, +}; + +#[derive(PartialOrd, Ord, PartialEq, Eq, Debug)] +pub enum RegIdEntry<'a> { + Reg(AllocatedRegisterTableEntry<'a>), + Stack(StackDepthTableEntry<'a>), +} +impl<'a> From> for RegIdEntry<'a> { + fn from(entry: AllocatedRegisterTableEntry<'a>) -> Self { + RegIdEntry::Reg(entry) + } +} + +impl<'a> From> for RegIdEntry<'a> { + fn from(entry: StackDepthTableEntry<'a>) -> Self { + RegIdEntry::Stack(entry) + } +} + +impl<'a> RegIdEntry<'a> { + pub fn insert(&mut self, id: RegId) { + match self { + RegIdEntry::Reg(entry) => entry.insert(id), + RegIdEntry::Stack(entry) => entry.insert(id), + } + } + + pub fn as_location(&self) -> DataLocation { + match self { + RegIdEntry::Reg(entry) => entry.as_location(), + RegIdEntry::Stack(entry) => entry.as_location(), + } + } + + pub fn deallocate(&mut self) { + match self { + RegIdEntry::Reg(entry) => entry.deallocate(), + RegIdEntry::Stack(entry) => entry.deallocate(), + } + } +} diff --git a/src/generate/register_allocator/stack_depth_table.rs b/src/generate/register_allocator/stack_depth_table.rs new file mode 100644 index 0000000..acc1616 --- /dev/null +++ b/src/generate/register_allocator/stack_depth_table.rs @@ -0,0 +1,97 @@ +use crate::{error::CompileError, unimplemented_err}; + +use super::data_location::{DataLocation, RegId, StackRegIdx}; + +#[derive(PartialOrd, Ord, PartialEq, Eq, Clone, Debug)] +pub struct StackDepthTable { + reg_ids: Vec>, +} + +#[derive(PartialOrd, Ord, PartialEq, Eq, Debug)] +pub struct StackDepthTableEntry<'a> { + stack_reg_idx: StackRegIdx, + stack: &'a mut Vec>, +} + +impl<'a> StackDepthTableEntry<'a> { + pub fn new( + stack_reg_idx: StackRegIdx, + reg_ids: &'a mut Vec>, + ) -> Result { + if reg_ids.get(stack_reg_idx.as_idx()).is_none() { + return Err(unimplemented_err!( + "INTERNAL COMPILER ERROR: try to create StackDepthTableEntry, but failed" + )); + } + Ok(Self { + stack_reg_idx, + stack: reg_ids, + }) + } + + pub fn as_location(&self) -> DataLocation { + DataLocation::Stack(self.stack_reg_idx) + } + + pub fn insert(&mut self, id: RegId) { + *(self.stack.get_mut(self.stack_reg_idx.as_idx()).unwrap()) = Some(id); + // TODO: StackDepthTableEntry について再考 + } + + pub fn deallocate(&mut self) { + *(self.stack.get_mut(self.stack_reg_idx.as_idx()).unwrap()) = None; + } +} + +impl StackDepthTable { + pub fn new() -> Self { + Self { + reg_ids: Vec::new(), + } + } + + pub fn allocate_entry(&mut self) -> Result { + let stack_reg_idx = self.reg_ids.len(); + self.reg_ids.push(None); + StackDepthTableEntry::new(StackRegIdx::from_idx(stack_reg_idx), &mut self.reg_ids) + } + + pub fn allocate_entry_of_specific_depth( + &mut self, + stack_reg_idx: StackRegIdx, + ) -> Result { + self.ensures_accessable_idx(stack_reg_idx.as_idx()); + if let Some(maybe_stack_reg_idx) = self.reg_ids.get(stack_reg_idx.as_idx()) { + if maybe_stack_reg_idx.is_some() { + return Err(unimplemented_err!(format!("INTERNAL COMPILER ERROR: try to allocate specific depth in stack area, the specified stack_reg_idx: {:?} was already used", stack_reg_idx))); + } + } else { + unreachable!(); + } + StackDepthTableEntry::new(stack_reg_idx, &mut self.reg_ids) + } + + pub fn ensures_accessable_idx(&mut self, idx: usize) { + for idx in 0..=idx { + if self.reg_ids.get(idx).is_none() { + self.reg_ids.push(None); + } + } + } + + pub fn get(&self, idx: StackRegIdx) -> Option<&Option> { + self.reg_ids.get(idx.as_idx()) + } + + pub fn get_vec(&self) -> &Vec> { + &self.reg_ids + } + + pub fn get_vec_mut(&mut self) -> &mut Vec> { + &mut self.reg_ids + } + + pub fn deallocate_all(&mut self) { + self.reg_ids.clear(); + } +} diff --git a/src/generate/register_allocator/stacks.rs b/src/generate/register_allocator/stacks.rs new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/src/generate/register_allocator/stacks.rs @@ -0,0 +1 @@ +