alduin 0.0.1

WIP: A toy compiler backend
Documentation
use std::{
    any::TypeId,
    fmt,
    hash::Hash,
    marker::{PhantomData, PhantomPinned},
    ops::{Deref, Index},
};

use crate::compiler::compiled_code::Symbol;

use super::{op::Const, ty::*, Graph, OpCode};

#[derive(Clone, Copy, Hash, Debug)]
pub struct NodeId<T: AnyTy = Top> {
    pub(super) index: usize,
    pub(super) _p: PhantomData<T>,
}

impl<T: AnyTy> From<usize> for NodeId<T> {
    fn from(value: usize) -> Self {
        Self {
            index: value,
            _p: PhantomData,
        }
    }
}

impl<T: AnyTy> NodeId<T> {
    pub fn index(&self) -> usize {
        self.index
    }
}

impl<T: AnyTy> NodeId<T> {
    pub fn cast<U: AnyTy>(&self) -> NodeId<U> {
        debug_assert!(T::TYPE == U::TYPE || T::TYPE == Top::TYPE || U::TYPE == Top::TYPE);
        if U::TYPE != Top::TYPE {
            // assert_eq!(self.ty, U::TYPE);
        }
        NodeId {
            index: self.index,
            // ty: self.ty,
            _p: PhantomData,
        }
    }
}

impl<T: AnyTy> PartialEq for NodeId<T> {
    fn eq(&self, other: &Self) -> bool {
        self.index == other.index
    }
}

impl<T: AnyTy> Eq for NodeId<T> {}

unsafe impl<T: AnyTy> Send for NodeId<T> {}
unsafe impl<T: AnyTy> Sync for NodeId<T> {}

pub struct NodeData {
    pub node: NodeId,
    pub cfg_id: usize,
    pub ty: Type,
    op: usize,
    pub(super) op_name: &'static str,
    op_type: TypeId,
    pub inputs: Vec<NodeId>,
    pub controls: Vec<NodeId<()>>,
    pub effects: Vec<NodeId>,
    pub literal: Option<Literal>,
    pub uses: Vec<Use>,
    pub control_uses: Vec<Use>,
    pub effect_uses: Vec<Use>,
    pub mark: usize,
    pub dead: bool,
    pub(crate) block: Option<usize>,
    pub(crate) fixed_reg: Option<usize>,
    pub(crate) stack_arg_index: Option<usize>,
    pub(crate) interval: usize,
    /// The temp register used for codegen, if the node is spilled.
    pub(crate) temp_reg: Option<u8>,
    /// Move ndoe scheduling constraints
    pub(crate) move_node_before: Option<NodeId>,
    /// Move ndoe scheduling constraints
    pub(crate) move_node_after: Option<NodeId>,
    pub(crate) never_binded: bool,
    pub(crate) has_call_indirect_ctx: bool,
    _pin: PhantomPinned,
}

impl NodeData {
    pub(super) fn new<Op: OpCode>(id: NodeId, ty: Type, op: Op) -> Self {
        Self {
            node: id,
            cfg_id: id.index(),
            ty,
            op: op.into(),
            op_name: op.name(),
            op_type: std::any::TypeId::of::<Op>(),
            inputs: Default::default(),
            controls: Default::default(),
            effects: Default::default(),
            uses: vec![],
            control_uses: vec![],
            effect_uses: vec![],
            literal: None,
            mark: 0,
            dead: false,
            block: None,
            fixed_reg: None,
            stack_arg_index: None,
            interval: usize::MAX,
            temp_reg: None,
            move_node_after: None,
            move_node_before: None,
            never_binded: true,
            has_call_indirect_ctx: false,
            _pin: PhantomPinned,
        }
    }

    #[inline(always)]
    pub fn op<Op: OpCode>(&self) -> Op {
        debug_assert_eq!(self.op_type, std::any::TypeId::of::<Op>());
        Op::from(self.op)
    }

    #[inline(always)]
    pub fn has_output<Op: OpCode>(&self) -> bool {
        debug_assert_eq!(self.op_type, std::any::TypeId::of::<Op>());
        self.op::<Op>().has_output()
    }
}

#[derive(Debug, Clone)]
pub enum Literal {
    Value(Const),
    Func(Symbol),
    ParamIndex(usize),
}

#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub enum InputKind {
    Data,
    Control,
    Effect,
}

pub struct Inputs<T: AnyTy = Top> {
    owner: NodeId,
    nodes: Vec<NodeId<T>>,
    #[allow(unused)]
    kind: InputKind,
}

impl<T: AnyTy> Inputs<T> {
    pub fn set(&mut self, g: &mut Graph, index: usize, node: NodeId<T>, remove_stale_uses: bool) {
        // Remove old_input's use edges
        let use_edge = Use {
            user: self.owner,
            input: index,
        };
        if remove_stale_uses {
            let remove_user = |uses: &mut Vec<Use>| {
                uses.retain(|u| u != &use_edge);
            };
            match self.kind {
                InputKind::Data => remove_user(&mut g[self.nodes[index]].uses),
                InputKind::Control => remove_user(&mut g[self.nodes[index]].control_uses),
                InputKind::Effect => remove_user(&mut g[self.nodes[index]].effect_uses),
            }
        }
        // Update input
        self.nodes[index] = node;
        // Set new use edges
        let add_user = |uses: &mut Vec<Use>| {
            uses.push(use_edge);
        };
        match self.kind {
            InputKind::Data => add_user(&mut g[node].uses),
            InputKind::Control => add_user(&mut g[node].control_uses),
            InputKind::Effect => add_user(&mut g[node].effect_uses),
        }
    }

    pub fn push(&mut self, g: &mut Graph, node: NodeId<T>) {
        let input = self.nodes.len();
        self.nodes.push(node);
        let user = self.owner;
        match self.kind {
            InputKind::Data => g[node].uses.push(Use { user, input }),
            InputKind::Control => g[node].control_uses.push(Use { user, input }),
            InputKind::Effect => g[node].effect_uses.push(Use { user, input }),
        }
    }
}

impl<T: AnyTy> Index<usize> for Inputs<T> {
    type Output = NodeId<T>;

    fn index(&self, index: usize) -> &Self::Output {
        &self.nodes[index]
    }
}

impl<T: AnyTy> Deref for Inputs<T> {
    type Target = [NodeId<T>];

    fn deref(&self) -> &Self::Target {
        &self.nodes
    }
}

/// Use edge of a data/control/effect input
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
pub struct Use {
    pub(crate) user: NodeId,
    pub(crate) input: usize,
}

impl Use {
    /// Get user node
    pub fn user(&self) -> NodeId {
        self.user
    }
    /// Get user node input index
    pub fn input(&self) -> usize {
        self.input
    }
}

impl fmt::Debug for NodeData {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "[%{}] {} {:?}", self.node.index, self.op_name, self.ty)?;
        if let Some(literal) = self.literal.as_ref() {
            write!(f, " <{:?}>", literal)?;
        }
        let n = self.controls.len();
        if n != 0 {
            write!(f, " ctrl=[")?;
            for i in 0..n {
                if i != 0 {
                    write!(f, ", ")?
                }
                write!(f, "%{}", self.controls[i].index)?;
            }
            write!(f, "]")?;
        }
        let n = self.inputs.len();
        if n != 0 {
            write!(f, " inputs=[")?;
            for i in 0..n {
                if i != 0 {
                    write!(f, ", ")?
                }
                write!(f, "%{}", self.inputs[i].index)?;
            }
            write!(f, "]")?;
        }
        let n = self.effects.len();
        if n != 0 {
            write!(f, " effect=[")?;
            for i in 0..n {
                if i != 0 {
                    write!(f, ", ")?
                }
                write!(f, "%{}", self.effects[i].index)?;
            }
            write!(f, "]")?;
        }
        const OUTPUT_USERS: bool = true;
        if OUTPUT_USERS && self.uses.len() != 0 {
            write!(f, " uses=[")?;
            for i in 0..self.uses.len() {
                if i != 0 {
                    write!(f, ", ")?
                }
                write!(f, "%{}:{}", self.uses[i].user.index, self.uses[i].input)?;
            }
            write!(f, "]")?;
        }

        write!(f, "")
    }
}