llhd 0.16.0

A Low Level Hardware Description that acts as a foundation for building hardware design tools.
Documentation
use crate::assembly::reader as ast;
use crate::{ty::*, ir::prelude::*, value::{IntValue, TimeValue}};
use num::BigInt;

grammar;

pub Module: Module = <units: Unit*> Comment => {
    let mut module = Module::new();
    for unit in units {
        match unit {
            ast::Unit::Data(x, loc) => {
                let unit = module.add_unit(x);
                module.set_location_hint(unit, loc);
            }
            ast::Unit::Declare(name, sig, loc) => {
                let decl = module.add_decl(DeclData { name, sig, loc: Some(loc) });
            }
        }
    }
    module
};

// A unit.
Unit: ast::Unit = {
    Comment <loc:@L> <u:Function> => ast::Unit::Data(u, loc),
    Comment <loc:@L> <u:Process> => ast::Unit::Data(u, loc),
    Comment <loc:@L> <u:Entity> => ast::Unit::Data(u, loc),
    Comment <loc:@L> "declare" <name: UnitName> <sig: Signature> => ast::Unit::Declare(name, sig, loc),
};

Signature: Signature = {
    "(" <args: Comma<Type>> ")" <retty: Type> => {
        let mut sig = Signature::new();
        for arg in args {
            sig.add_input(arg);
        }
        sig.set_return_type(retty);
        sig
    },
    "(" <input_args: Comma<Type>> ")" "->" "(" <output_args: Comma<Type>> ")" => {
        let mut sig = Signature::new();
        for arg in input_args {
            sig.add_input(arg);
        }
        for arg in output_args {
            sig.add_output(arg);
        }
        sig
    },
};

// A function.
Function: UnitData =
    "func" <name: UnitName>
    "(" <args: Args> ")"
    <retty: Type>
    "{" Comment <blocks: Block*> "}" => {

    let mut sig = Signature::new();
    let args: Vec<_> = args
        .into_iter()
        .map(|(ty, name)| (sig.add_input(ty), name))
        .collect();
    sig.set_return_type(retty);
    let mut func = UnitData::new(UnitKind::Function, name, sig);
    let mut builder = UnitBuilder::new_anonymous(&mut func);
    let mut context = ast::Context::default();
    for (arg, name) in args {
        let v = builder.arg_value(arg);
        context.value_names.insert(name, v);
        if let ast::LocalName::Named(name) = name {
            builder.set_name(v, name.to_owned());
        }
    }
    for block in blocks {
        block.build(&mut builder, &mut context);
    }
    func
};

// A process.
Process: UnitData =
    "proc" <name: UnitName>
    "(" <input_args: Args> ")"
    "->"
    "(" <output_args: Args> ")"
    "{" Comment <blocks: Block*> "}" => {

    let mut sig = Signature::new();
    let input_args: Vec<_> = input_args
        .into_iter()
        .map(|(ty, name)| (sig.add_input(ty), name))
        .collect();
    let output_args: Vec<_> = output_args
        .into_iter()
        .map(|(ty, name)| (sig.add_output(ty), name))
        .collect();
    let mut prok = UnitData::new(UnitKind::Process, name, sig);
    let mut builder = UnitBuilder::new_anonymous(&mut prok);
    let mut context = ast::Context::default();
    for (arg, name) in input_args {
        let v = builder.arg_value(arg);
        context.value_names.insert(name, v);
        if let ast::LocalName::Named(name) = name {
            builder.set_name(v, name.to_owned());
        }
    }
    for (arg, name) in output_args {
        let v = builder.arg_value(arg);
        context.value_names.insert(name, v);
        if let ast::LocalName::Named(name) = name {
            builder.set_name(v, name.to_owned());
        }
    }
    for block in blocks {
        block.build(&mut builder, &mut context);
    }
    prok
};

// An entity.
Entity: UnitData =
    "entity" <name: UnitName>
    "(" <input_args: Args> ")"
    "->"
    "(" <output_args: Args> ")"
    "{" Comment <insts: Inst*> "}" => {

    let mut sig = Signature::new();
    let input_args: Vec<_> = input_args
        .into_iter()
        .map(|(ty, name)| (sig.add_input(ty), name))
        .collect();
    let output_args: Vec<_> = output_args
        .into_iter()
        .map(|(ty, name)| (sig.add_output(ty), name))
        .collect();
    let mut ent = UnitData::new(UnitKind::Entity, name, sig);
    let mut builder = UnitBuilder::new_anonymous(&mut ent);
    let mut context = ast::Context::default();
    for (arg, name) in input_args {
        let v = builder.arg_value(arg);
        context.value_names.insert(name, v);
        if let ast::LocalName::Named(name) = name {
            builder.set_name(v, name.to_owned());
        }
    }
    for (arg, name) in output_args {
        let v = builder.arg_value(arg);
        context.value_names.insert(name, v);
        if let ast::LocalName::Named(name) = name {
            builder.set_name(v, name.to_owned());
        }
    }
    for inst in insts {
        inst.build(&mut builder, &mut context);
    }
    ent
};

// A unit argument.
Args = Comma<(Type LocalName)>;

// A basic block.
Block: ast::Block<'input> = <name: BlockLabel> Comment <insts: Inst*> => ast::Block {
    name: name,
    insts
};

// An instruction.
Inst: ast::Inst<'input> = {
    <loc:@L> <name: LocalName> "=" <inst: InstWithRequiredResult> Comment => inst.name(name).location(loc),
    <loc:@L> <name: LocalName> "=" <inst: InstWithResult> Comment => inst.name(name).location(loc),
    <loc:@L> <inst: InstWithResult> Comment => inst.location(loc),
    <loc:@L> <inst: InstWithoutResult> Comment => inst.location(loc),
};

// An instruction which yields a result.
InstWithResult: ast::Inst<'input> = {
    "const" <ty: IntType> <imm: BigInt> => ast::Inst::new(Opcode::ConstInt)
        .data(ast::InstData::ConstInt(IntValue::from_signed(ty.unwrap_int(), imm))),
    "const" <ty: TimeType> <imm: TimeValue> => ast::Inst::new(Opcode::ConstTime)
        .data(ast::InstData::ConstTime(imm)),
    "alias" <arg: TypedValue> => ast::Inst::new(Opcode::Alias)
        .data(ast::InstData::Unary(arg)),
    <opc: UnaryOpcode> <arg: TypedValue> => ast::Inst::new(opc)
        .data(ast::InstData::Unary(arg)),
    <opc: BinaryOpcode> <arg0: TypedValue> "," <arg1: Value> => {
        let arg1 = arg1.ty(arg0.ty.clone());
        ast::Inst::new(opc)
            .data(ast::InstData::Binary(arg0, arg1))
    },
    <opc: ShiftOpcode> <base: TypedValue> "," <hidden: TypedValue> "," <amount: TypedValue> =>
        ast::Inst::new(opc).data(ast::InstData::Ternary(base, hidden, amount)),
    "mux" <array: TypedValue> "," <sel: TypedValue> => ast::Inst::new(Opcode::Mux)
        .data(ast::InstData::Binary(array, sel)),
    "reg" <target: TypedValue> <triggers: ("," "[" <Value> "," <RegMode> <Value> <("," "if" <Value>)?> "]")*> => {
        let ty = target.ty.unwrap_signal();
        let triggers = triggers.into_iter().map(|(data, mode, trigger, gate)| (
            data.ty(ty.clone()),
            mode,
            trigger.ty(int_ty(1)),
            gate.map(|gate| gate.ty(int_ty(1))),
        )).collect();
        ast::Inst::new(Opcode::Reg)
            .data(ast::InstData::Reg(target, triggers))
    },
    "insf" <target: TypedValue> "," <arg: TypedValue> "," <imm: Usize> => {
        ast::Inst::new(Opcode::InsField)
            .data(ast::InstData::Ins(target, arg, [imm, 0]))
    },
    "inss" <target: TypedValue> "," <arg: TypedValue> "," <imm0: Usize> "," <imm1: Usize> => {
        ast::Inst::new(Opcode::InsSlice)
            .data(ast::InstData::Ins(target, arg, [imm0, imm1]))
    },
    "extf" <ty: Type> "," <target: TypedValue> "," <imm: Usize> => {
        ast::Inst::new(Opcode::ExtField)
            .data(ast::InstData::Ext(ty, target, [imm, 0]))
    },
    "exts" <ty: Type> "," <target: TypedValue> "," <imm0: Usize> "," <imm1: Usize> => {
        ast::Inst::new(Opcode::ExtSlice)
            .data(ast::InstData::Ext(ty, target, [imm0, imm1]))
    },
    "del" <target: TypedValue> "," <source: Value> "," <delay: Value> => {
        let source = source.ty(target.ty.clone());
        let delay = delay.ty(time_ty());
        ast::Inst::new(Opcode::Del)
            .data(ast::InstData::Ternary(target, source, delay))
    },
    "call" <retty: Type> <unit: UnitName> "(" <args: Comma<TypedValue>> ")" => ast::Inst::new(Opcode::Call)
        .data(ast::InstData::Call(retty, unit, args)),
    "phi" <ty: Type> <edges: Comma<("[" <Value> "," <Label> "]")>> => {
        let edges = edges.into_iter().map(|(v, bb)| (v.ty(ty.clone()), bb)).collect();
        ast::Inst::new(Opcode::Phi).data(ast::InstData::Phi(ty, edges))
    },
};

// An instruction which yields a result that must be used.
InstWithRequiredResult: ast::Inst<'input> = {
    "[" <length: Usize> "x" <arg: TypedValue> "]" => ast::Inst::new(Opcode::ArrayUniform)
        .data(ast::InstData::Aggregate(length, vec![arg])),
    "[" <head: TypedValue> <tail: ("," <Value>)*> "]" => {
        let ty = head.ty.clone();
        let mut args = vec![head];
        args.extend(tail.into_iter().map(|a| a.ty(ty.clone())));
        ast::Inst::new(Opcode::Array).data(ast::InstData::Aggregate(0, args))
    },
    "{" <args: Comma<TypedValue>> "}" => ast::Inst::new(Opcode::Struct)
        .data(ast::InstData::Aggregate(0, args)),
}

// An instruction which does not yield a result.
InstWithoutResult: ast::Inst<'input> = {
    "con" <arg0: TypedValue> "," <arg1: Value> => {
        let arg1 = arg1.ty(arg0.ty.clone());
        ast::Inst::new(Opcode::Con)
            .data(ast::InstData::Binary(arg0, arg1))
    },
    "inst" <unit: UnitName> "(" <input_args: Comma<TypedValue>> ")" "->" "(" <output_args: Comma<TypedValue>> ")" => ast::Inst::new(Opcode::Inst)
        .data(ast::InstData::Inst(unit, input_args, output_args)),
    "drv" <target: TypedValue> "," <value: Value> "," <delay: Value> => {
        let value = value.ty(target.ty.unwrap_signal().clone());
        let delay = delay.ty(time_ty());
        ast::Inst::new(Opcode::Drv)
            .data(ast::InstData::Ternary(target, value, delay))
    },
    "drv" <target: TypedValue> "if" <cond: Value> "," <value: Value> "," <delay: Value> => {
        let value = value.ty(target.ty.unwrap_signal().clone());
        let delay = delay.ty(time_ty());
        let cond = cond.ty(int_ty(1));
        ast::Inst::new(Opcode::DrvCond)
            .data(ast::InstData::Quaternary(target, value, delay, cond))
    },
    "st" <target: TypedValue> "," <value: Value> => {
        let value = value.ty(target.ty.unwrap_pointer().clone());
        ast::Inst::new(Opcode::St)
            .data(ast::InstData::Binary(target, value))
    },
    "halt" => ast::Inst::new(Opcode::Halt),
    "ret" => ast::Inst::new(Opcode::Ret),
    "ret" <arg: TypedValue> => ast::Inst::new(Opcode::RetValue)
        .data(ast::InstData::Unary(arg)),
    "br" <bb: Label> => ast::Inst::new(Opcode::Br)
        .data(ast::InstData::Branch(None, bb, None)),
    "br" <cond: Value> "," <bb0: Label> "," <bb1: Label> => {
        let cond = cond.ty(int_ty(1));
        ast::Inst::new(Opcode::BrCond)
            .data(ast::InstData::Branch(Some(cond), bb0, Some(bb1)))
    },
    "wait" <bb: Label> <time: ("for" <Value>)?> <args: ("," <Value>)*> => {
        let time = time.map(|t| t.ty(time_ty()));
        ast::Inst::new(match time {
            Some(_) => Opcode::WaitTime,
            None => Opcode::Wait,
        }).data(ast::InstData::Wait(bb, time, args))
    }
}

// A regular unary opcode.
UnaryOpcode: Opcode = {
    "not" => Opcode::Not,
    "neg" => Opcode::Neg,
    "sig" => Opcode::Sig,
    "prb" => Opcode::Prb,
    "var" => Opcode::Var,
    "ld" => Opcode::Ld,
};

// A regular binary opcode.
BinaryOpcode: Opcode = {
    "add" => Opcode::Add,
    "sub" => Opcode::Sub,
    "and" => Opcode::And,
    "or" => Opcode::Or,
    "xor" => Opcode::Xor,
    "smul" => Opcode::Smul,
    "sdiv" => Opcode::Sdiv,
    "smod" => Opcode::Smod,
    "srem" => Opcode::Srem,
    "umul" => Opcode::Umul,
    "udiv" => Opcode::Udiv,
    "umod" => Opcode::Umod,
    "urem" => Opcode::Urem,
    "eq" => Opcode::Eq,
    "neq" => Opcode::Neq,
    "slt" => Opcode::Slt,
    "sgt" => Opcode::Sgt,
    "sle" => Opcode::Sle,
    "sge" => Opcode::Sge,
    "ult" => Opcode::Ult,
    "ugt" => Opcode::Ugt,
    "ule" => Opcode::Ule,
    "uge" => Opcode::Uge,
};

// A shift opcode.
ShiftOpcode: Opcode = {
    "shl" => Opcode::Shl,
    "shr" => Opcode::Shr,
};

// A register trigger mode.
RegMode: RegMode = {
    "low" => RegMode::Low,
    "high" => RegMode::High,
    "rise" => RegMode::Rise,
    "fall" => RegMode::Fall,
    "both" => RegMode::Both,
}

// A mentioning of a block as instruction argument.
Label: ast::Label<'input> = LocalName => ast::Label(<>);

// A mentioning of a value as instruction argument.
Value: ast::Value<'input> = LocalName => ast::Value(<>);
TypedValue: ast::TypedValue<'input> = <ty: Type> <value: Value> => value.ty(ty);

// Any of the LLHD types.
pub Type: Type = {
    "void" => void_ty(),
    TimeType,
    IntType,
    EnumType,
    <Type> "$" => signal_ty(<>),
    <Type> "*" => pointer_ty(<>),
    "[" <Usize> "x" <Type> "]" => array_ty(<>),
    "{" <Comma<Type>> "}" => struct_ty(<>),
};

TimeType: Type = "time" => time_ty();
IntType: Type = r"i\d+" => int_ty(<>[1..].parse().unwrap());
EnumType: Type = r"n\d+" => enum_ty(<>[1..].parse().unwrap());

// A local name.
LocalName: ast::LocalName<'input> = <name: Name> => {
    match &name[0..1] {
        "%" => name[1..].into(),
        _ => panic!("expected local name (`%...`), got `{}`", name),
    }
};

// A unit name, which is basically a name wrapped up in a different package.
UnitName: UnitName = <name: Name> => {
    let (first, tail) = (&name[0..1], &name[1..]);
    let all_digits = tail.chars().all(|c| c.is_digit(10));
    match first {
        "@" => UnitName::global(tail),
        "%" if all_digits => UnitName::anonymous(tail.parse().unwrap()),
        "%" => UnitName::local(tail),
        _ => unreachable!("regex should not match names starting with `{}`", first),
    }
};

// A basic block label.
BlockLabel: ast::LocalName<'input> = r"%?[a-zA-Z0-9_\.\\]+:" => {
    <>.trim_start_matches('%').trim_end_matches(':').into()
};

// Any temporary, local, or global name.
Name = r"[@%][a-zA-Z0-9_\.\\]+";
Usize: usize = r"[-+]?\d+" => <>.parse().unwrap();
BigInt: BigInt = r"[-+]?\d+" => <>.parse().unwrap();
pub TimeValue: TimeValue = <time: RegularTime> <delta: DeltaTime?> <epsilon: EpsilonTime?> => {
    let (v, delta, epsilon) = ast::parse_time_triple(time, delta, epsilon);
    TimeValue::new(v, delta, epsilon)
};
RegularTime = r"[-+]?\d+(\.\d+)?[afpnumkMGTPE]?s";
DeltaTime = r"[0-9]+d";
EpsilonTime = r"[0-9]+e";

// A macro for comma-separated items.
Comma<T>: Vec<T> = <head: (<T> ",")*> <tail: T?> => match tail {
    Some(tail) => {
        let mut head = head;
        head.push(tail);
        head
    }
    None => head,
};

// A comment.
Comment: () = r";.*"* => ();