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";.*"* => ();