slonik/src/ir/write.rs

209 lines
6 KiB
Rust

//! Text formatting for Slonik IR.
use std::fmt;
use crate::ir::{Block, BlockCall, Body, Stmt, StmtData, Value};
/// Writes `body` in textual form.
pub fn write_body(f: &mut fmt::Formatter<'_>, body: &Body) -> fmt::Result {
Printer { body }.write_body(f)
}
impl fmt::Display for Body {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write_body(f, self)
}
}
struct Printer<'a> {
body: &'a Body,
}
impl Printer<'_> {
fn write_body(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
writeln!(f, "body {{")?;
let mut first = true;
for block in self.body.blocks() {
if !first {
writeln!(f)?;
}
first = false;
self.write_block(f, block)?;
}
writeln!(f, "}}")
}
fn write_block(&self, f: &mut fmt::Formatter<'_>, block: Block) -> fmt::Result {
write!(f, " ^{block}")?;
let params = self.body.block_params(block);
if !params.is_empty() {
write!(f, "(")?;
for (i, &param) in params.iter().enumerate() {
if i != 0 {
write!(f, ", ")?;
}
write!(f, "%{}: {}", param, self.body.value_type(param))?;
}
write!(f, ")")?;
}
writeln!(f, ":")?;
for &stmt in self.body.block_stmts(block) {
self.write_stmt(f, stmt)?;
}
Ok(())
}
fn write_stmt(&self, f: &mut fmt::Formatter<'_>, stmt: Stmt) -> fmt::Result {
write!(f, " ")?;
match *self.body.stmt_data(stmt) {
StmtData::IConst { imm, result } => {
writeln!(f, "%{result} = iconst {imm} : {}", self.body.value_type(result))
}
StmtData::F32Const { bits, result } => {
writeln!(f, "%{result} = f32const 0x{bits:08x} : f32")
}
StmtData::F64Const { bits, result } => {
writeln!(f, "%{result} = f64const 0x{bits:016x} : f64")
}
StmtData::BConst { value, result } => {
writeln!(f, "%{result} = bconst {value} : bool")
}
StmtData::Unary { op, arg, result } => {
writeln!(
f,
"%{result} = {op} %{arg} : {}",
self.body.value_type(result)
)
}
StmtData::Binary { op, lhs, rhs, result } => {
writeln!(
f,
"%{result} = {op} %{lhs}, %{rhs} : {}",
self.body.value_type(result)
)
}
StmtData::Cast { op, arg, result } => {
writeln!(
f,
"%{result} = {op} %{arg} : {} -> {}",
self.body.value_type(arg),
self.body.value_type(result)
)
}
StmtData::Icmp { cc, lhs, rhs, result } => {
writeln!(
f,
"%{result} = icmp {cc} %{lhs}, %{rhs} : {}",
self.body.value_type(lhs)
)
}
StmtData::Fcmp { cc, lhs, rhs, result } => {
writeln!(
f,
"%{result} = fcmp {cc} %{lhs}, %{rhs} : {}",
self.body.value_type(lhs)
)
}
StmtData::Select {
cond,
if_true,
if_false,
result,
} => {
writeln!(
f,
"%{result} = select %{cond}, %{if_true}, %{if_false} : {}",
self.body.value_type(result)
)
}
StmtData::Load { addr, size, result } => {
writeln!(
f,
"%{result} = load %{addr} : {size} -> {}",
self.body.value_type(result)
)
}
StmtData::Store { addr, value, size } => {
writeln!(f, "store %{value}, %{addr} : {size}")
}
StmtData::Call { callee, args } => {
write!(f, "call %{callee}")?;
let args = args.as_slice(&self.body.value_lists);
if !args.is_empty() {
write!(f, "(")?;
self.write_value_list(f, args)?;
write!(f, ")")?;
}
writeln!(f)
}
StmtData::Jump { dst } => {
write!(f, "br ")?;
self.write_block_call(f, dst)?;
writeln!(f)
}
StmtData::BrIf {
cond,
then_dst,
else_dst,
} => {
write!(f, "cond_br %{cond}, ")?;
self.write_block_call(f, then_dst)?;
write!(f, ", ")?;
self.write_block_call(f, else_dst)?;
writeln!(f)
}
StmtData::Return { values } => {
write!(f, "return")?;
let values = values.as_slice(&self.body.value_lists);
if !values.is_empty() {
write!(f, " ")?;
self.write_value_list(f, values)?;
}
writeln!(f)
}
}
}
fn write_block_call(&self, f: &mut fmt::Formatter<'_>, call: BlockCall) -> fmt::Result {
write!(f, "^{}", call.block)?;
let args = call.args.as_slice(&self.body.value_lists);
if !args.is_empty() {
write!(f, "(")?;
self.write_value_list(f, args)?;
write!(f, ")")?;
}
Ok(())
}
fn write_value_list(&self, f: &mut fmt::Formatter<'_>, values: &[Value]) -> fmt::Result {
for (i, value) in values.iter().enumerate() {
if i != 0 {
write!(f, ", ")?;
}
write!(f, "%{value}")?;
}
Ok(())
}
}