use crate::{
argument::*,
block::*,
entity::{Entity, EntityContext},
function::{Function, FunctionContext},
inst::*,
konst::*,
module::{Module, ModuleContext},
process::{Process, ProcessContext},
ty::*,
unit::*,
value::*,
visit::Visitor,
AggregateKind,
};
use std::{collections::HashMap, io::Write, rc::Rc};
pub struct Writer<'twr> {
sink: &'twr mut Write,
name_table: HashMap<ValueId, Rc<String>>,
name_stack: Vec<(usize, HashMap<String, usize>)>,
}
impl<'twr> Writer<'twr> {
pub fn new(sink: &mut Write) -> Writer {
Writer {
sink: sink,
name_table: HashMap::new(),
name_stack: vec![(0, HashMap::new())],
}
}
fn uniquify(&mut self, value: &Value) -> Rc<String> {
let id = value.id();
if let Some(name) = self.name_table.get(&id).cloned() {
name
} else {
let name = self.uniquify_name(value.name(), value.is_global());
self.name_table.insert(id, name.clone());
name
}
}
fn uniquify_name(&mut self, name: Option<&str>, global: bool) -> Rc<String> {
let prefix = if global { "@" } else { "%" };
if let Some(name) = name {
if let Some(index) = self.name_stack.last_mut().unwrap().1.get_mut(name) {
let n = Rc::new(format!("{}{}{}", prefix, name, index));
*index += 1;
return n;
}
let mut index: Option<usize> = None;
for &(_, ref names) in self.name_stack.iter().rev().skip(1) {
if let Some(&i) = names.get(name) {
index = Some(i);
break;
}
}
self.name_stack
.last_mut()
.unwrap()
.1
.insert(name.to_owned(), index.unwrap_or(0));
Rc::new(
index
.map(|i| format!("{}{}{}", prefix, name, i))
.unwrap_or_else(|| format!("{}{}", prefix, name)),
)
} else {
let ref mut index = self.name_stack.last_mut().unwrap().0;
let name = Rc::new(format!("%{}", index));
*index += 1;
name
}
}
fn push(&mut self) {
let index = self.name_stack.last().unwrap().0;
self.name_stack.push((index, HashMap::new()))
}
fn pop(&mut self) {
self.name_stack.pop();
assert!(!self.name_stack.is_empty())
}
fn write_value(
&mut self,
ctx: &Context,
value: &ValueRef,
need_explicit_type: bool,
) -> std::io::Result<()> {
match *value {
ValueRef::Const(ref k) => self.write_const(k, need_explicit_type),
ValueRef::Aggregate(ref a) => self.write_aggregate(ctx, a),
_ => {
let value = ctx.value(value);
let name = self.uniquify(value);
if need_explicit_type {
self.write_ty(&value.ty())?;
write!(self.sink, " ")?;
}
write!(self.sink, "{}", name)
}
}
}
fn write_ty(&mut self, ty: &Type) -> std::io::Result<()> {
write!(self.sink, "{}", ty)
}
fn write_const(&mut self, konst: &ConstKind, need_explicit_type: bool) -> std::io::Result<()> {
match *konst {
ConstKind::Int(ref k) => {
if need_explicit_type {
self.write_ty(&konst.ty())?;
write!(self.sink, " ")?;
}
write!(self.sink, "{}", k.value())
}
ConstKind::Time(ref k) => write!(self.sink, "{}", k),
}
}
fn write_aggregate(&mut self, ctx: &Context, aggregate: &AggregateKind) -> std::io::Result<()> {
match *aggregate {
AggregateKind::Struct(ref a) => {
write!(self.sink, "{{")?;
let mut iter = a.fields().iter();
if let Some(field) = iter.next() {
self.write_value(ctx, field, true)?;
}
for field in iter {
write!(self.sink, ", ")?;
self.write_value(ctx, field, true)?;
}
write!(self.sink, "}}")?;
}
AggregateKind::Array(ref a) => {
write!(self.sink, "[")?;
let elem_ty = a.ty().unwrap_array().1;
let mut iter = a.elements().iter();
let all_equal = if let Some(first) = iter.next() {
iter.all(|elem| elem == first) && a.elements().len() > 1
} else {
false
};
if all_equal {
write!(self.sink, "{} x ", a.elements().len())?;
}
let need_explicit_type = if elem_ty.is_int() {
self.write_ty(elem_ty)?;
if !a.elements().is_empty() {
write!(self.sink, " ")?;
}
false
} else {
true
};
let mut iter = a.elements().iter();
if let Some(elem) = iter.next() {
self.write_value(ctx, elem, need_explicit_type)?;
}
if !all_equal {
for elem in iter {
write!(self.sink, ", ")?;
self.write_value(ctx, elem, need_explicit_type)?;
}
}
write!(self.sink, "]")?;
}
}
Ok(())
}
}
impl<'twr> Visitor for Writer<'twr> {
fn visit_module(&mut self, module: &Module) {
let ctx = ModuleContext::new(module);
for (value, sep) in module
.values()
.zip(std::iter::once("").chain(std::iter::repeat("\n")))
{
write!(self.sink, "{}", sep).unwrap();
self.visit_module_value(&ctx, value);
}
}
fn visit_function(&mut self, ctx: &ModuleContext, func: &Function) {
let ctx = FunctionContext::new(ctx, func);
self.push();
write!(self.sink, "func @{} (", func.name()).unwrap();
self.visit_arguments(func.args());
write!(self.sink, ") {} {{\n", func.return_ty()).unwrap();
for block in func.body().blocks() {
self.visit_block(&ctx, block);
}
write!(self.sink, "}}\n").unwrap();
self.pop();
}
fn visit_process(&mut self, ctx: &ModuleContext, prok: &Process) {
let ctx = ProcessContext::new(ctx, prok);
self.push();
write!(self.sink, "proc @{} (", prok.name()).unwrap();
self.visit_arguments(prok.inputs());
write!(self.sink, ") (").unwrap();
self.visit_arguments(prok.outputs());
write!(self.sink, ") {{\n").unwrap();
for block in prok.body().blocks() {
self.visit_block(&ctx, block);
}
write!(self.sink, "}}\n").unwrap();
self.pop();
}
fn visit_entity(&mut self, ctx: &ModuleContext, entity: &Entity) {
let ctx = EntityContext::new(ctx, entity);
self.push();
write!(self.sink, "entity @{} (", entity.name()).unwrap();
self.visit_arguments(entity.inputs());
write!(self.sink, ") (").unwrap();
self.visit_arguments(entity.outputs());
write!(self.sink, ") {{\n").unwrap();
let uctx = ctx.as_unit_context();
for inst in entity.insts() {
self.visit_inst(uctx, inst);
}
write!(self.sink, "}}\n").unwrap();
self.pop();
}
fn visit_arguments(&mut self, args: &[Argument]) {
for (arg, sep) in args
.iter()
.zip(std::iter::once("").chain(std::iter::repeat(", ")))
{
write!(self.sink, "{}", sep).unwrap();
self.visit_argument(arg);
}
}
fn visit_argument(&mut self, arg: &Argument) {
write!(self.sink, "{}", arg.ty()).unwrap();
let name = self.uniquify(arg);
write!(self.sink, " {}", name).unwrap();
}
fn visit_block(&mut self, ctx: &SequentialContext, block: &Block) {
let name = self.uniquify(block);
write!(self.sink, "{}:\n", name).unwrap();
self.walk_block(ctx, block);
}
fn visit_inst(&mut self, ctx: &UnitContext, inst: &Inst) {
let name = self.uniquify(inst);
write!(self.sink, " ").unwrap();
if !inst.ty().is_void() || (inst.name().is_some() && inst.kind().is_instance()) {
write!(self.sink, "{} = ", name).unwrap();
}
write!(self.sink, "{}", inst.mnemonic().as_str()).unwrap();
match *inst.kind() {
UnaryInst(_op, ref ty, ref arg) => {
write!(self.sink, " ").unwrap();
self.write_ty(ty).unwrap();
write!(self.sink, " ").unwrap();
self.write_value(ctx.as_context(), arg, false).unwrap();
}
BinaryInst(_op, ref ty, ref lhs, ref rhs) => {
write!(self.sink, " ").unwrap();
self.write_ty(ty).unwrap();
write!(self.sink, " ").unwrap();
self.write_value(ctx.as_context(), lhs, false).unwrap();
write!(self.sink, " ").unwrap();
self.write_value(ctx.as_context(), rhs, false).unwrap();
}
CompareInst(op, ref ty, ref lhs, ref rhs) => {
write!(self.sink, " {} ", op.to_str()).unwrap();
self.write_ty(ty).unwrap();
write!(self.sink, " ").unwrap();
self.write_value(ctx.as_context(), lhs, false).unwrap();
write!(self.sink, " ").unwrap();
self.write_value(ctx.as_context(), rhs, false).unwrap();
}
CallInst(_, ref target, ref args) => {
write!(self.sink, " ").unwrap();
self.write_value(ctx.as_context(), target, false).unwrap();
write!(self.sink, " (").unwrap();
for (arg, sep) in args
.iter()
.zip(std::iter::once("").chain(std::iter::repeat(", ")))
{
write!(self.sink, "{}", sep).unwrap();
self.write_value(ctx.as_context(), arg, false).unwrap();
}
write!(self.sink, ")").unwrap();
}
InstanceInst(_, ref target, ref ins, ref outs) => {
write!(self.sink, " ").unwrap();
self.write_value(ctx.as_context(), target, false).unwrap();
write!(self.sink, " (").unwrap();
for (arg, sep) in ins
.iter()
.zip(std::iter::once("").chain(std::iter::repeat(", ")))
{
write!(self.sink, "{}", sep).unwrap();
self.write_value(ctx.as_context(), arg, false).unwrap();
}
write!(self.sink, ") (").unwrap();
for (arg, sep) in outs
.iter()
.zip(std::iter::once("").chain(std::iter::repeat(", ")))
{
write!(self.sink, "{}", sep).unwrap();
self.write_value(ctx.as_context(), arg, false).unwrap();
}
write!(self.sink, ")").unwrap();
}
WaitInst(target, ref time, ref signals) => {
write!(self.sink, " ").unwrap();
self.write_value(ctx.as_context(), &target.into(), false)
.unwrap();
if let Some(ref time) = *time {
write!(self.sink, " for ").unwrap();
self.write_value(ctx.as_context(), time, false).unwrap();
}
for signal in signals {
write!(self.sink, ", ").unwrap();
self.write_value(ctx.as_context(), signal, false).unwrap();
}
}
ReturnInst(ReturnKind::Void) => (),
ReturnInst(ReturnKind::Value(ref ty, ref value)) => {
write!(self.sink, " ").unwrap();
self.write_ty(ty).unwrap();
write!(self.sink, " ").unwrap();
self.write_value(ctx.as_context(), value, false).unwrap();
}
BranchInst(BranchKind::Uncond(target)) => {
write!(self.sink, " label ").unwrap();
self.write_value(ctx.as_context(), &target.into(), false)
.unwrap();
}
BranchInst(BranchKind::Cond(ref cond, if_true, if_false)) => {
write!(self.sink, " ").unwrap();
self.write_value(ctx.as_context(), cond, false).unwrap();
write!(self.sink, " label ").unwrap();
self.write_value(ctx.as_context(), &if_true.into(), false)
.unwrap();
write!(self.sink, " ").unwrap();
self.write_value(ctx.as_context(), &if_false.into(), false)
.unwrap();
}
SignalInst(ref ty, ref init) => {
write!(self.sink, " ").unwrap();
self.write_ty(ty).unwrap();
if let Some(ref init) = *init {
write!(self.sink, " ").unwrap();
self.write_value(ctx.as_context(), init, false).unwrap();
}
}
ProbeInst(_, ref signal) => {
write!(self.sink, " ").unwrap();
self.write_value(ctx.as_context(), signal, false).unwrap();
}
DriveInst(ref signal, ref value, ref delay) => {
write!(self.sink, " ").unwrap();
self.write_value(ctx.as_context(), signal, false).unwrap();
write!(self.sink, " ").unwrap();
self.write_value(ctx.as_context(), value, false).unwrap();
if let Some(ref delay) = *delay {
write!(self.sink, " ").unwrap();
self.write_value(ctx.as_context(), delay, false).unwrap();
}
}
VariableInst(ref ty) => {
write!(self.sink, " ").unwrap();
self.write_ty(ty).unwrap();
}
LoadInst(ref ty, ref ptr) => {
write!(self.sink, " ").unwrap();
self.write_ty(ty).unwrap();
write!(self.sink, " ").unwrap();
self.write_value(ctx.as_context(), ptr, false).unwrap();
}
StoreInst(ref ty, ref ptr, ref value) => {
write!(self.sink, " ").unwrap();
self.write_ty(ty).unwrap();
write!(self.sink, " ").unwrap();
self.write_value(ctx.as_context(), ptr, false).unwrap();
write!(self.sink, " ").unwrap();
self.write_value(ctx.as_context(), value, false).unwrap();
}
InsertInst(ref ty, ref target, mode, ref value) => {
match mode {
SliceMode::Element(..) => write!(self.sink, " element ").unwrap(),
SliceMode::Slice(..) => write!(self.sink, " slice ").unwrap(),
}
self.write_ty(ty).unwrap();
write!(self.sink, " ").unwrap();
self.write_value(ctx.as_context(), target, false).unwrap();
write!(self.sink, ", ").unwrap();
match mode {
SliceMode::Element(i) => write!(self.sink, "{}", i).unwrap(),
SliceMode::Slice(i, n) => write!(self.sink, "{}, {}", i, n).unwrap(),
}
write!(self.sink, ", ").unwrap();
self.write_value(ctx.as_context(), value, true).unwrap();
}
ExtractInst(ref ty, ref target, mode) => {
match mode {
SliceMode::Element(..) => write!(self.sink, " element ").unwrap(),
SliceMode::Slice(..) => write!(self.sink, " slice ").unwrap(),
}
self.write_ty(ty).unwrap();
write!(self.sink, " ").unwrap();
self.write_value(ctx.as_context(), target, false).unwrap();
write!(self.sink, ", ").unwrap();
match mode {
SliceMode::Element(i) => write!(self.sink, "{}", i).unwrap(),
SliceMode::Slice(i, n) => write!(self.sink, "{}, {}", i, n).unwrap(),
}
}
HaltInst => (),
}
write!(self.sink, "\n").unwrap();
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{Aggregate, ArrayAggregate, StructAggregate};
use num::BigInt;
fn with_writer(f: impl FnOnce(Writer)) -> String {
let mut asm = vec![];
f(Writer::new(&mut asm));
String::from_utf8(asm).expect("writer should emit proper utf8")
}
fn write_aggregate(agg: AggregateKind) -> String {
with_writer(|mut w| {
let module = Module::new();
let ctx = ModuleContext::new(&module);
let ent = Entity::new("foo", entity_ty(vec![], vec![]));
let ctx = EntityContext::new(&ctx, &ent);
w.write_value(&ctx, &Aggregate::new(agg).into(), false)
.unwrap();
})
}
fn write_array_aggregate(ty: Type, elements: Vec<Const>) -> String {
write_aggregate(
ArrayAggregate::new(
array_ty(elements.len(), ty),
elements.into_iter().map(Into::into).collect(),
)
.into(),
)
}
fn write_struct_aggregate(fields: Vec<Const>) -> String {
write_aggregate(
StructAggregate::new(
struct_ty(fields.iter().map(|f| f.ty()).collect()),
fields.into_iter().map(Into::into).collect(),
)
.into(),
)
}
#[test]
fn array_aggregate() {
assert_eq!(write_array_aggregate(int_ty(32), vec![]), "[i32]");
assert_eq!(
write_array_aggregate(int_ty(32), vec![const_int(32, BigInt::from(42)),]),
"[i32 42]"
);
assert_eq!(
write_array_aggregate(
int_ty(32),
vec![
const_int(32, BigInt::from(42)),
const_int(32, BigInt::from(9001)),
]
),
"[i32 42, 9001]"
);
assert_eq!(
write_array_aggregate(
int_ty(32),
vec![
const_int(32, BigInt::from(42)),
const_int(32, BigInt::from(42)),
]
),
"[2 x i32 42]"
);
}
#[test]
fn struct_aggregate() {
assert_eq!(write_struct_aggregate(vec![]), "{}");
assert_eq!(
write_struct_aggregate(vec![const_int(32, BigInt::from(42))]),
"{i32 42}"
);
assert_eq!(
write_struct_aggregate(vec![
const_int(32, BigInt::from(42)),
const_int(64, BigInt::from(9001))
]),
"{i32 42, i64 9001}"
);
}
}