alduin 0.0.1

WIP: A toy compiler backend
Documentation
use std::fmt::Debug;
use std::hash::{Hash, Hasher};
use strum_macros::IntoStaticStr;

#[repr(C, u8)]
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Const {
    Bool(bool),
    I8(i8),
    I16(i16),
    I32(i32),
    I64(i64),
    I128(i128),
    F32(f32),
    F64(f64),
}

impl Eq for Const {}

impl Hash for Const {
    fn hash<H: Hasher>(&self, state: &mut H) {
        let tag = unsafe { *(self as *const Self as *const u8) };
        tag.hash(state);
        match *self {
            Const::Bool(v) => v.hash(state),
            Const::I8(v) => v.hash(state),
            Const::I16(v) => v.hash(state),
            Const::I32(v) => v.hash(state),
            Const::I64(v) => v.hash(state),
            Const::I128(v) => v.hash(state),
            Const::F32(v) => v.to_bits().hash(state),
            Const::F64(v) => v.to_bits().hash(state),
        }
    }
}

pub trait OpCode:
    From<usize> + Into<usize> + Copy + Clone + Debug + PartialEq + Eq + Hash + 'static
{
    /// The name of the op
    fn name(&self) -> &'static str;
    /// Is this a control op?
    fn ctrl_op(&self) -> Option<CtrlOp>;
    /// Is this a effect phi
    fn is_effect_phi(&self) -> bool;
    /// Is this a parameter node?
    fn is_param(&self) -> bool;
    /// Is this a move node?
    fn is_move(&self) -> bool;
    /// Does this node have an output value?
    fn has_output(&self) -> bool;
    /// Is this a phi node?
    fn is_phi(&self) -> bool {
        self.ctrl_op() == Some(CtrlOp::Phi)
    }
    /// Is this a label or terminal node?
    fn is_label_or_terminal(&self) -> bool {
        self.ctrl_op()
            .map(|op| op.is_label_or_terminal())
            .unwrap_or(false)
    }
}

#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, IntoStaticStr)]
#[repr(u8)]
pub enum BaseOp {
    // Control flow and arguments //
    Start,
    Param,
    Region,
    EffectPhi,
    Phi,
    Branch,
    Jump,
    BrTable,
    Return,
    DebugBreak,
    // Constants //
    Const,
    // Integer arithmetic //
    Neg,
    Not,
    And,
    Xor,
    Or,
    Shl,
    ShrS,
    ShrU,
    Rotl,
    Rotr,
    Add,
    Sub,
    Mul,
    DivS,
    DivU,
    RemS,
    RemU,
    Clz,
    Ctz,
    Popcnt,
    // Floating pointer arithmetic //
    FAdd,
    FDiv,
    FSqrt,
    FRound,
    FCeil,
    FFloor,
    FTrunc,
    FAbs,
    FCopysign,
    // Conversions //
    SExt,
    ZExt,
    ITruncU,
    CvtF2SI,
    CvtF2UI,
    CvtSI2F,
    CvtUI2F,
    CvtF2F,
    Bitcast,
    Wrap,
    // Comparisons //
    Eq,
    Ne,
    LtS,
    LtU,
    LeS,
    LeU,
    GtS,
    GtU,
    GeS,
    GeU,
    LtF,
    LeF,
    IsNan,
    GtF,
    GeF,
    /// Call a static function
    Call,
    /// Call a function pointer
    CallIndirect,
    Move,
    Load,
    Store,
}

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

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

impl OpCode for BaseOp {
    fn name(&self) -> &'static str {
        self.into()
    }

    fn ctrl_op(&self) -> Option<CtrlOp> {
        match self {
            BaseOp::Phi => Some(CtrlOp::Phi),
            BaseOp::Start => Some(CtrlOp::Start),
            BaseOp::Region => Some(CtrlOp::Region),
            BaseOp::Branch => Some(CtrlOp::Branch),
            BaseOp::BrTable => Some(CtrlOp::BrTable),
            BaseOp::Jump => Some(CtrlOp::Jump),
            BaseOp::Return => Some(CtrlOp::Return),
            _ => None,
        }
    }

    fn is_param(&self) -> bool {
        *self == Self::Param
    }

    fn is_move(&self) -> bool {
        *self == Self::Move
    }

    fn is_effect_phi(&self) -> bool {
        *self == BaseOp::EffectPhi
    }

    fn has_output(&self) -> bool {
        match self {
            BaseOp::Start
            | BaseOp::Region
            | BaseOp::EffectPhi
            | BaseOp::Branch
            | BaseOp::BrTable
            | BaseOp::Jump
            | BaseOp::Return
            | BaseOp::DebugBreak
            | BaseOp::Store => false,
            _ => true,
        }
    }
}

/// Common OpCodes for constructing the control flow.
/// All opcode implementations should contain these opcodes.
#[derive(Debug, Clone, PartialEq, Hash)]
#[repr(u8)]
pub enum CtrlOp {
    Start,
    Region,
    Phi,
    Branch,
    BrTable,
    Jump,
    Return,
}

impl CtrlOp {
    pub fn is_label(&self) -> bool {
        *self == CtrlOp::Start || *self == CtrlOp::Region
    }

    pub fn is_terminal(&self) -> bool {
        *self == CtrlOp::Jump
            || *self == CtrlOp::Return
            || *self == CtrlOp::Branch
            || *self == CtrlOp::BrTable
    }

    pub fn is_label_or_terminal(&self) -> bool {
        self.is_label() || self.is_terminal()
    }
}