use {
crate::{
ir::{FreshRegister, Instruction, Variable},
reification::{ReifiedRegister, ReifyRegister},
},
std::{array, marker::PhantomData, mem},
};
pub type AtomicInstructionBlock = Vec<Instruction<FreshRegister>>;
pub struct Assembler {
pub instructions: Vec<AtomicInstructionBlock>,
}
impl Default for Assembler {
fn default() -> Self {
Self::new()
}
}
impl Assembler {
pub fn new() -> Self {
Self {
instructions: Vec::new(),
}
}
pub fn append_instruction(&mut self, inst: AtomicInstructionBlock) {
self.instructions.push(inst)
}
}
#[derive(Debug)]
pub struct FreshAllocator {
pub fresh: u64,
}
impl Default for FreshAllocator {
fn default() -> Self {
Self::new()
}
}
impl FreshAllocator {
pub fn fresh<T>(&mut self) -> Reg<T> {
let x = self.fresh;
self.fresh += 1;
Reg::new(x)
}
pub fn fresh_array<T, const N: usize>(&mut self) -> [Reg<T>; N] {
array::from_fn(|_| self.fresh())
}
pub fn new() -> Self {
Self { fresh: 0 }
}
pub fn allocated(&self) -> usize {
self.fresh as usize
}
}
pub type FreshVariable = Variable<ReifiedRegister<FreshRegister>>;
impl FreshVariable {
pub fn new<R>(label: &str, registers: &[R]) -> Self
where
R: ReifyRegister,
{
Self {
label: label.to_string(),
registers: registers.iter().map(|reg| reg.reify()).collect(),
}
}
}
pub struct Lazy<'a, T>(LazyInner<'a, T>);
enum LazyInner<'a, T> {
Thunk(Option<DelayedInstruction<'a, T>>),
Forced(T),
}
type DelayedInstruction<'a, T> = Box<dyn FnOnce(&mut FreshAllocator, &mut Assembler) -> T + 'a>;
impl<T> Lazy<'_, T> {
pub fn thunk<'a>(inst: DelayedInstruction<'a, T>) -> Lazy<'a, T> {
Lazy(LazyInner::Thunk(Some(inst)))
}
pub fn forced<'a>(val: T) -> Lazy<'a, T> {
Lazy(LazyInner::Forced(val))
}
fn force(&mut self, alloc: &mut FreshAllocator, asm: &mut Assembler) {
if let LazyInner::Thunk(optf) = &mut self.0 {
match optf.take() {
Some(f) => self.0 = LazyInner::Forced(f(alloc, asm)),
None => unreachable!(),
};
}
}
pub fn as_(&mut self, alloc: &mut FreshAllocator, asm: &mut Assembler) -> &T {
self.force(alloc, asm);
match &self.0 {
LazyInner::Forced(t) => t,
LazyInner::Thunk(_) => unreachable!(),
}
}
pub fn into_(mut self, alloc: &mut FreshAllocator, asm: &mut Assembler) -> T {
self.force(alloc, asm);
match self.0 {
LazyInner::Forced(t) => t,
LazyInner::Thunk(_) => unreachable!(),
}
}
}
pub struct Reg<T> {
pub(crate) reg: FreshRegister,
_marker: PhantomData<T>,
}
pub struct PointerReg<'a, T> {
pub(crate) reg: &'a Reg<T>,
pub(crate) offset: usize,
_marker: PhantomData<T>,
}
impl<T, const N: usize> Reg<*mut [T; N]> {
pub fn get(&self, index: usize) -> PointerReg<'_, *mut T> {
assert!(index < N, "out-of-bounds access");
PointerReg {
reg: self.as_pointer(),
offset: mem::size_of::<T>() * index,
_marker: PhantomData,
}
}
}
impl<T, const N: usize> Reg<*const [T; N]> {
pub fn get(&self, index: usize) -> PointerReg<'_, *const T> {
assert!(index < N, "out-of-bounds access");
PointerReg {
reg: self.as_(),
offset: mem::size_of::<T>() * index,
_marker: PhantomData,
}
}
}
pub trait Pointer: ReifyRegister {}
impl<T> Pointer for PointerReg<'_, T> {}
impl<T> Pointer for Reg<*mut T> {}
impl<T> Pointer for Reg<*const T> {}
pub trait MutablePointer: Pointer {}
impl<T> MutablePointer for PointerReg<'_, *mut T> {}
impl<T> MutablePointer for Reg<*mut T> {}
pub trait SIMD {}
impl<T, const N: usize> SIMD for Reg<Simd<T, N>> {}
impl<T: SIMD, const I: u8> SIMD for Idx<T, I> {}
pub struct Simd<T, const N: usize>(PhantomData<T>);
pub struct Idx<T, const I: u8>(pub(crate) T);
pub struct Sized<T, const L: u8>(pub(crate) T);
pub type SizedIdx<T, const L: u8, const I: u8> = Sized<Idx<T, I>, L>;
pub const D: u8 = 2;
pub trait Reg64Bit {}
impl Reg64Bit for u64 {}
impl Reg64Bit for f64 {}
impl<T> Reg<T> {
pub(crate) fn new(reg: u64) -> Self {
Self {
reg: reg.into(),
_marker: Default::default(),
}
}
}
impl Reg<f64> {
pub fn as_simd(&self) -> &Reg<Simd<f64, 2>> {
unsafe { std::mem::transmute(self) }
}
}
impl<T> Reg<Simd<T, 2>> {
pub fn into_<D>(self) -> Reg<Simd<D, 2>> {
unsafe { std::mem::transmute(self) }
}
pub fn as_<D>(&self) -> &Reg<Simd<D, 2>> {
unsafe { std::mem::transmute(self) }
}
pub fn _0(&self) -> &Idx<Reg<Simd<T, 2>>, 0> {
unsafe { std::mem::transmute(self) }
}
pub fn _1(&self) -> &Idx<Reg<Simd<T, 2>>, 1> {
unsafe { std::mem::transmute(self) }
}
pub fn _d0(&self) -> &SizedIdx<Reg<Simd<T, 2>>, D, 0> {
unsafe { std::mem::transmute(self) }
}
pub fn _d1(&self) -> &SizedIdx<Reg<Simd<T, 2>>, D, 1> {
unsafe { std::mem::transmute(self) }
}
}
impl<T, const N: usize> Reg<*mut [T; N]> {
pub fn as_pointer(&self) -> &Reg<*mut T> {
unsafe { std::mem::transmute(self) }
}
}
impl<T> Reg<*mut T> {
pub fn as_(&self) -> &Reg<*const T> {
unsafe { std::mem::transmute(self) }
}
}
impl<T, const N: usize> Reg<*const [T; N]> {
pub fn as_(&self) -> &Reg<*const T> {
unsafe { std::mem::transmute(self) }
}
}
impl std::fmt::Display for Reg<u64> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "x{}", self.reg)
}
}
impl std::fmt::Debug for Reg<u64> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "x{}", self.reg)
}
}