-
-
Notifications
You must be signed in to change notification settings - Fork 421
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement register allocation (#3942)
* Implement register allocation * Apply review * Update core/engine/src/vm/opcode/rest_parameter/mod.rs Co-authored-by: raskad <[email protected]> --------- Co-authored-by: raskad <[email protected]>
- Loading branch information
Showing
16 changed files
with
338 additions
and
44 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,140 @@ | ||
use std::mem::forget; | ||
|
||
bitflags::bitflags! { | ||
#[derive(Debug, Default, Clone, Copy)] | ||
struct RegisterFlags: u8 { | ||
/// Whether the register is still in use (not deallocated). | ||
const USED = 0b0000_0001; | ||
|
||
/// Is the register presistent (not deallocatable). | ||
const PERSISTENT = 0b0000_0010; | ||
} | ||
} | ||
|
||
impl RegisterFlags { | ||
fn is_used(self) -> bool { | ||
self.contains(Self::USED) | ||
} | ||
fn is_persistent(self) -> bool { | ||
self.contains(Self::PERSISTENT) | ||
} | ||
} | ||
|
||
/// An entry in the [`RegisterAllocator`]. | ||
#[derive(Debug, Default)] | ||
pub(crate) struct RegisterEntry { | ||
flags: RegisterFlags, | ||
} | ||
|
||
/// Represent a VM register. | ||
/// | ||
/// This is intented to be passed by reference or to be moved, dropping this is a bug, | ||
/// it should only be dropped though the [`RegisterAllocator::dealloc()`] method. | ||
/// This doesn't apply to persistent registers. | ||
/// | ||
/// A [`Register`] is index into the register allocator, | ||
/// as well as an index into the registers on the stack using the register pointer (`rp`). | ||
#[derive(Debug)] | ||
pub(crate) struct Register { | ||
index: u32, | ||
flags: RegisterFlags, | ||
} | ||
|
||
impl Register { | ||
/// The index of the [`Register`]. | ||
pub(crate) fn index(&self) -> u32 { | ||
self.index | ||
} | ||
} | ||
|
||
impl Drop for Register { | ||
/// This method should never be called. | ||
/// It is used to detect when a register has not been deallocated. | ||
fn drop(&mut self) { | ||
if self.flags.is_persistent() { | ||
return; | ||
} | ||
|
||
// Prevent double panic. | ||
if std::thread::panicking() { | ||
return; | ||
} | ||
|
||
unreachable!("forgot to deallocate a register!") | ||
} | ||
} | ||
|
||
#[derive(Debug, Default)] | ||
pub(crate) struct RegisterAllocator { | ||
registers: Vec<RegisterEntry>, | ||
} | ||
|
||
impl RegisterAllocator { | ||
pub(crate) fn alloc(&mut self) -> Register { | ||
if let Some((i, register)) = self | ||
.registers | ||
.iter_mut() | ||
.filter(|reg| !reg.flags.is_used()) | ||
.enumerate() | ||
.next() | ||
{ | ||
assert!(!register.flags.is_persistent()); | ||
|
||
register.flags |= RegisterFlags::USED; | ||
return Register { | ||
index: i as u32, | ||
flags: register.flags, | ||
}; | ||
} | ||
|
||
let flags = RegisterFlags::USED; | ||
|
||
let index = self.registers.len() as u32; | ||
self.registers.push(RegisterEntry { flags }); | ||
|
||
Register { index, flags } | ||
} | ||
|
||
pub(crate) fn alloc_persistent(&mut self) -> Register { | ||
let mut reg = self.alloc(); | ||
|
||
let index = reg.index(); | ||
|
||
let register = &mut self.registers[index as usize]; | ||
|
||
register.flags |= RegisterFlags::PERSISTENT; | ||
|
||
reg.flags = register.flags; | ||
reg | ||
} | ||
|
||
#[allow(unused)] | ||
pub(crate) fn dealloc(&mut self, reg: Register) { | ||
assert!( | ||
!reg.flags.is_persistent(), | ||
"Trying to deallocate a persistent register" | ||
); | ||
|
||
let register = &mut self.registers[reg.index as usize]; | ||
|
||
assert!( | ||
register.flags.is_used(), | ||
"Cannot deallocate unused variable" | ||
); | ||
register.flags.set(RegisterFlags::USED, false); | ||
|
||
// NOTE: We should not drop it since, dropping it used to detect bugs. | ||
forget(reg); | ||
} | ||
|
||
pub(crate) fn finish(self) -> u32 { | ||
for register in &self.registers { | ||
debug_assert!( | ||
!register.flags.is_used() | ||
|| (register.flags.is_used() && register.flags.is_persistent()) | ||
); | ||
} | ||
|
||
self.registers.len() as u32 | ||
} | ||
} |
Oops, something went wrong.