use crate::ir::{prelude::*, UnitKind};
use std::{
collections::{HashMap, HashSet},
io::{Result, Write},
rc::Rc,
};
pub struct Writer<T> {
sink: T,
}
impl<T: Write> Writer<T> {
pub fn new(sink: T) -> Self {
Self { sink }
}
pub fn write_module(&mut self, module: &Module) -> Result<()> {
let mut separate = false;
for unit in module.units() {
if separate {
write!(self.sink, "\n")?;
}
separate = true;
self.write_unit(unit)?;
}
for decl in module.decls() {
if separate {
write!(self.sink, "\n")?;
}
separate = false;
let data = &module[decl];
self.write_declaration(&data.sig, &data.name)?;
}
Ok(())
}
pub fn write_unit(&mut self, data: Unit) -> Result<()> {
let mut uw = UnitWriter::new(self, data);
write!(uw.writer.sink, "{} {} (", data.kind(), data.name())?;
let mut comma = false;
for arg in data.sig().inputs() {
if comma {
write!(uw.writer.sink, ", ")?;
}
comma = true;
write!(uw.writer.sink, "{} ", data.sig().arg_type(arg))?;
uw.write_value_name(data.arg_value(arg))?;
}
if data.kind() == UnitKind::Function {
write!(uw.writer.sink, ") {} {{\n", data.sig().return_type())?;
} else {
write!(uw.writer.sink, ") -> (")?;
let mut comma = false;
for arg in data.sig().outputs() {
if comma {
write!(uw.writer.sink, ", ")?;
}
comma = true;
write!(uw.writer.sink, "{} ", data.sig().arg_type(arg))?;
uw.write_value_name(data.arg_value(arg))?;
}
write!(uw.writer.sink, ") {{\n")?;
}
for block in data.blocks() {
if data.kind() != UnitKind::Entity {
uw.write_block_name(block)?;
write!(uw.writer.sink, ":\n")?;
}
for inst in data.insts(block) {
if data[inst].opcode().is_terminator() && data.is_entity() {
continue;
}
write!(uw.writer.sink, " ")?;
uw.write_inst(inst)?;
write!(uw.writer.sink, "\n")?;
}
}
write!(uw.writer.sink, "}}\n")?;
Ok(())
}
pub fn write_declaration(&mut self, sig: &Signature, name: &UnitName) -> Result<()> {
write!(self.sink, "declare {} {}\n", name, sig)?;
Ok(())
}
}
pub struct UnitWriter<'a, T> {
writer: &'a mut Writer<T>,
unit: Unit<'a>,
value_names: HashMap<Value, Rc<String>>,
block_names: HashMap<Block, Rc<String>>,
name_indices: HashMap<Rc<String>, usize>,
names: HashSet<Rc<String>>,
tmp_index: usize,
}
impl<'a, T: Write> UnitWriter<'a, T> {
pub fn new(writer: &'a mut Writer<T>, unit: Unit<'a>) -> Self {
Self {
writer,
unit,
value_names: Default::default(),
block_names: Default::default(),
name_indices: Default::default(),
names: Default::default(),
tmp_index: 0,
}
}
pub fn write_value_name(&mut self, value: Value) -> Result<()> {
if let Some(name) = self.value_names.get(&value) {
return write!(self.writer.sink, "%{}", name);
}
let name = self.uniquify_name(self.unit.get_name(value));
write!(self.writer.sink, "%{}", name)?;
self.value_names.insert(value, name);
Ok(())
}
pub fn write_block_name(&mut self, block: Block) -> Result<()> {
if let Some(name) = self.block_names.get(&block) {
return write!(self.writer.sink, "{}", name);
}
let name = self.uniquify_name(self.unit.get_block_name(block));
write!(self.writer.sink, "{}", name)?;
self.block_names.insert(block, name);
Ok(())
}
pub fn write_block_value(&mut self, block: Block) -> Result<()> {
write!(self.writer.sink, "%")?;
self.write_block_name(block)
}
fn uniquify_name(&mut self, name: Option<&str>) -> Rc<String> {
if let Some(requested_name) = name {
let requested_name = escape_name(requested_name);
let idx = self.name_indices.entry(requested_name.clone()).or_insert(0);
loop {
let name = if *idx == 0 {
requested_name.clone()
} else {
Rc::new(format!("{}{}", requested_name, idx))
};
*idx += 1;
if self.names.insert(name.clone()) {
break name;
}
}
} else {
loop {
let name = Rc::new(format!("{}", self.tmp_index));
self.tmp_index += 1;
if self.names.insert(name.clone()) {
break name;
}
}
}
}
pub fn write_value_use(&mut self, value: Value, with_type: bool) -> Result<()> {
if with_type {
write!(self.writer.sink, "{} ", self.unit.value_type(value))?;
}
self.write_value_name(value)
}
pub fn write_inst(&mut self, inst: Inst) -> Result<()> {
let unit = self.unit;
if unit.has_result(inst) {
self.write_value_name(unit.inst_result(inst))?;
write!(self.writer.sink, " = ")?;
}
let data = &unit[inst];
match data.opcode() {
Opcode::ConstInt => write!(
self.writer.sink,
"{} {} {}",
data.opcode(),
unit.value_type(unit.inst_result(inst)),
data.get_const_int().unwrap().value
)?,
Opcode::ConstTime => write!(
self.writer.sink,
"{} time {}",
data.opcode(),
data.get_const_time().unwrap()
)?,
Opcode::ArrayUniform => {
write!(self.writer.sink, "[{} x ", data.imms()[0])?;
self.write_value_use(data.args()[0], true)?;
write!(self.writer.sink, "]")?;
}
Opcode::Array => {
write!(self.writer.sink, "[")?;
let mut first = true;
for &arg in data.args() {
if !first {
write!(self.writer.sink, ", ")?;
}
self.write_value_use(arg, first)?;
first = false;
}
write!(self.writer.sink, "]")?;
}
Opcode::Struct => {
write!(self.writer.sink, "{{")?;
let mut first = true;
for &arg in data.args() {
if !first {
write!(self.writer.sink, ", ")?;
}
first = false;
self.write_value_use(arg, true)?;
}
write!(self.writer.sink, "}}")?;
}
Opcode::Alias
| Opcode::Not
| Opcode::Neg
| Opcode::Add
| Opcode::Sub
| Opcode::And
| Opcode::Or
| Opcode::Xor
| Opcode::Smul
| Opcode::Sdiv
| Opcode::Smod
| Opcode::Srem
| Opcode::Umul
| Opcode::Udiv
| Opcode::Umod
| Opcode::Urem
| Opcode::Eq
| Opcode::Neq
| Opcode::Slt
| Opcode::Sgt
| Opcode::Sle
| Opcode::Sge
| Opcode::Ult
| Opcode::Ugt
| Opcode::Ule
| Opcode::Uge
| Opcode::Con
| Opcode::Del
| Opcode::Sig
| Opcode::Prb
| Opcode::Drv
| Opcode::Var
| Opcode::Ld
| Opcode::St
| Opcode::RetValue => {
write!(self.writer.sink, "{} ", data.opcode())?;
let mut first = true;
for &arg in data.args() {
if !first {
write!(self.writer.sink, ", ")?;
}
self.write_value_use(arg, first)?;
first = false;
}
}
Opcode::DrvCond => {
write!(self.writer.sink, "{} ", data.opcode())?;
let args = data.args();
self.write_value_use(args[0], true)?;
write!(self.writer.sink, " if ")?;
self.write_value_use(args[3], false)?;
write!(self.writer.sink, ", ")?;
self.write_value_use(args[1], false)?;
write!(self.writer.sink, ", ")?;
self.write_value_use(args[2], false)?;
}
Opcode::Shl | Opcode::Shr | Opcode::Mux => {
write!(self.writer.sink, "{} ", data.opcode())?;
let mut comma = false;
for &arg in data.args() {
if comma {
write!(self.writer.sink, ", ")?;
}
comma = true;
self.write_value_use(arg, true)?;
}
}
Opcode::Reg => {
write!(self.writer.sink, "{} ", data.opcode())?;
self.write_value_use(data.args()[0], true)?;
for t in data.triggers() {
write!(self.writer.sink, ", [")?;
self.write_value_use(t.data, false)?;
write!(self.writer.sink, ", {} ", t.mode)?;
self.write_value_use(t.trigger, false)?;
if let Some(gate) = t.gate {
write!(self.writer.sink, ", if ")?;
self.write_value_use(gate, false)?;
}
write!(self.writer.sink, "]")?;
}
}
Opcode::InsField | Opcode::InsSlice => {
write!(self.writer.sink, "{} ", data.opcode())?;
let mut first = true;
for &arg in data.args() {
if !first {
write!(self.writer.sink, ", ")?;
}
self.write_value_use(arg, true)?;
first = false;
}
for &imm in data.imms() {
write!(self.writer.sink, ", {}", imm)?;
}
}
Opcode::ExtField | Opcode::ExtSlice => {
write!(
self.writer.sink,
"{} {}",
data.opcode(),
unit.inst_type(inst)
)?;
for &arg in data.args() {
write!(self.writer.sink, ", ")?;
self.write_value_use(arg, true)?;
}
for &imm in data.imms() {
write!(self.writer.sink, ", {}", imm)?;
}
}
Opcode::Call => {
write!(
self.writer.sink,
"{} {} {} (",
data.opcode(),
if unit.has_result(inst) {
unit.value_type(unit.inst_result(inst))
} else {
crate::void_ty()
},
unit[data.get_ext_unit().unwrap()].name,
)?;
let mut comma = false;
for &arg in data.input_args() {
if comma {
write!(self.writer.sink, ", ")?;
}
comma = true;
self.write_value_use(arg, true)?;
}
write!(self.writer.sink, ")")?;
}
Opcode::Inst => {
write!(
self.writer.sink,
"{} {} (",
data.opcode(),
unit[data.get_ext_unit().unwrap()].name,
)?;
let mut comma = false;
for &arg in data.input_args() {
if comma {
write!(self.writer.sink, ", ")?;
}
comma = true;
self.write_value_use(arg, true)?;
}
write!(self.writer.sink, ") -> (")?;
let mut comma = false;
for &arg in data.output_args() {
if comma {
write!(self.writer.sink, ", ")?;
}
comma = true;
self.write_value_use(arg, true)?;
}
write!(self.writer.sink, ")")?;
}
Opcode::Halt | Opcode::Ret => write!(self.writer.sink, "{}", data.opcode())?,
Opcode::Phi => {
write!(
self.writer.sink,
"{} {} ",
data.opcode(),
unit.value_type(unit.inst_result(inst))
)?;
let mut comma = false;
for (&arg, &block) in data.args().iter().zip(data.blocks().iter()) {
if comma {
write!(self.writer.sink, ", ")?;
}
comma = true;
write!(self.writer.sink, "[")?;
self.write_value_use(arg, false)?;
write!(self.writer.sink, ", ")?;
self.write_block_value(block)?;
write!(self.writer.sink, "]")?;
}
}
Opcode::Br => {
write!(self.writer.sink, "{} ", data.opcode())?;
self.write_block_value(data.blocks()[0])?;
}
Opcode::BrCond => {
write!(self.writer.sink, "{} ", data.opcode())?;
self.write_value_use(data.args()[0], false)?;
write!(self.writer.sink, ", ")?;
self.write_block_value(data.blocks()[0])?;
write!(self.writer.sink, ", ")?;
self.write_block_value(data.blocks()[1])?;
}
Opcode::Wait => {
write!(self.writer.sink, "{} ", data.opcode())?;
self.write_block_value(data.blocks()[0])?;
for &arg in data.args() {
write!(self.writer.sink, ", ")?;
self.write_value_use(arg, false)?;
}
}
Opcode::WaitTime => {
write!(self.writer.sink, "{} ", data.opcode())?;
self.write_block_value(data.blocks()[0])?;
write!(self.writer.sink, " for ")?;
self.write_value_use(data.args()[0], false)?;
for &arg in &data.args()[1..] {
write!(self.writer.sink, ", ")?;
self.write_value_use(arg, false)?;
}
}
}
Ok(())
}
}
fn is_acceptable_name_char(c: char) -> bool {
c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c >= '0' && c <= '9' || c == '_' || c == '.'
}
fn escape_name(input: &str) -> Rc<String> {
let mut s = String::with_capacity(input.len());
for c in input.chars() {
if is_acceptable_name_char(c) {
s.push(c);
} else {
s.push_str(&format!("\\{:x}", c as u32));
}
}
Rc::new(s)
}