alduin 0.0.1

WIP: A toy compiler backend
Documentation
mod call_sites;
mod codegen;
mod compiled_code;
mod lazy_compilation_trampoline;
mod reg_alloc;
pub(super) mod rt;

use std::sync::Arc;

use crate::{
    compiler::Compiler,
    compiler::{
        compiled_code::{CompiledCode, Symbol},
        graph::{cfg::CFG, BaseOp, Graph, NodeId},
    },
};

use super::{reg_alloc::LinearScanRegisterAllocator, Reg, ISA};

#[repr(u8)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum X64Reg {
    RAX,
    RCX,
    RDX,
    RBX,
    RSP,
    RBP,
    RSI,
    RDI,
    R8,
    R9,
    R10,
    R11,
    R12,
    R13,
    R14,
    R15,

    XMM0,
    XMM1,
    XMM2,
    XMM3,
    XMM4,
    XMM5,
    XMM6,
    XMM7,
    XMM8,
    XMM9,
    XMM10,
    XMM11,
    XMM12,
    XMM13,
    XMM14,
    XMM15,
}

impl From<usize> for X64Reg {
    fn from(v: usize) -> Self {
        unsafe { std::mem::transmute(v as u8) }
    }
}

impl Into<usize> for X64Reg {
    fn into(self) -> usize {
        self as _
    }
}

impl Reg for X64Reg {
    const MAX_COUNT: usize = 32;

    const GPRS: &'static [Self] = &[
        Self::RAX,
        Self::RBX,
        Self::RCX,
        Self::RDX,
        Self::RSI,
        Self::RDI,
        Self::R8,
        Self::R9,
        Self::R10,
        Self::R11,
        Self::R12,
        Self::R13,
    ];

    const RESERVED_GPRS: &'static [Self] = &[Self::RBP, Self::RSP, Self::R14, Self::R15];

    const FPRS: &'static [Self] = &[
        Self::XMM0,
        Self::XMM1,
        Self::XMM2,
        Self::XMM3,
        Self::XMM4,
        Self::XMM5,
        Self::XMM6,
        Self::XMM7,
        Self::XMM8,
        Self::XMM9,
        Self::XMM10,
        Self::XMM11,
        Self::XMM12,
        Self::XMM13,
    ];

    const RESERVED_FPRS: &'static [Self] = &[Self::XMM14, Self::XMM15];
}

impl X64Reg {
    fn is_callee_saved(&self) -> bool {
        [Self::RBX, Self::R12, Self::R13, Self::R14, Self::R15].contains(self)
    }
}

pub struct X64;

impl ISA for X64 {
    type Reg = X64Reg;

    type Op = BaseOp;

    fn pre_assign_registers(graph: &mut Graph, node: NodeId) {
        reg_alloc::pre_assign_registers(graph, node)
    }

    fn codegen(
        compiler: &Compiler,
        symbol: Symbol,
        cfg: &mut CFG<Self::Op>,
    ) -> Arc<dyn CompiledCode> {
        // Register allocation
        let mut lsra = LinearScanRegisterAllocator::<Self>::new(cfg);
        lsra.allocate();
        // Generate machine code
        let codegen: codegen::CodeGen = codegen::CodeGen::new(compiler, symbol, cfg);
        codegen.gen()
    }

    fn coalesce_live_intervals(g: &Graph, n: NodeId, coalesce: impl FnMut(NodeId, NodeId)) {
        reg_alloc::coalesce_live_intervals(g, n, coalesce)
    }

    fn flush_cache(start: *const u8, bytes: usize) {
        unsafe {
            for i in (0..bytes).step_by(std::mem::size_of::<usize>()) {
                std::arch::x86_64::_mm_clflush(start.add(i));
            }
        }
    }

    fn gen_lazy_compilation_trampoline() -> Vec<u8> {
        lazy_compilation_trampoline::gen_lazy_compilation_trampoline()
    }
}