use alloc::string::String;
use alloc::vec::Vec;
use core::fmt;
use crate::entity::{Block, Value};
use crate::inst::{Inst, Terminator};
use crate::ty::Type;
use crate::validate::ValidationError;
#[derive(Clone, PartialEq, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub(crate) enum ValueDef {
Param(Block),
Inst(Block, Inst),
}
#[derive(Clone, PartialEq, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub(crate) struct ValueData {
pub(crate) ty: Type,
pub(crate) def: ValueDef,
}
#[derive(Clone, PartialEq, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub(crate) struct BlockData {
pub(crate) params: Vec<Value>,
pub(crate) insts: Vec<Value>,
pub(crate) term: Option<Terminator>,
}
#[derive(Clone, PartialEq, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Function {
pub(crate) name: String,
pub(crate) params: Vec<Type>,
pub(crate) ret: Type,
pub(crate) entry: Block,
pub(crate) blocks: Vec<BlockData>,
pub(crate) values: Vec<ValueData>,
}
impl Function {
pub(crate) fn from_parts(
name: String,
params: Vec<Type>,
ret: Type,
entry: Block,
blocks: Vec<BlockData>,
values: Vec<ValueData>,
) -> Self {
Self {
name,
params,
ret,
entry,
blocks,
values,
}
}
#[must_use]
pub fn name(&self) -> &str {
&self.name
}
#[must_use]
pub fn params(&self) -> &[Type] {
&self.params
}
#[must_use]
pub const fn ret(&self) -> Type {
self.ret
}
#[must_use]
pub const fn entry(&self) -> Block {
self.entry
}
#[must_use]
pub fn block_count(&self) -> usize {
self.blocks.len()
}
#[must_use]
pub fn value_count(&self) -> usize {
self.values.len()
}
pub fn blocks(&self) -> impl Iterator<Item = Block> {
(0..self.blocks.len() as u32).map(Block::from_raw)
}
#[must_use]
pub fn block_params(&self, block: Block) -> &[Value] {
match self.blocks.get(block.index()) {
Some(data) => &data.params,
None => &[],
}
}
#[must_use]
pub fn insts(&self, block: Block) -> &[Value] {
match self.blocks.get(block.index()) {
Some(data) => &data.insts,
None => &[],
}
}
#[must_use]
pub fn terminator(&self, block: Block) -> Option<&Terminator> {
self.blocks.get(block.index())?.term.as_ref()
}
#[must_use]
pub fn inst(&self, value: Value) -> Option<&Inst> {
match &self.values.get(value.index())?.def {
ValueDef::Inst(_, inst) => Some(inst),
ValueDef::Param(_) => None,
}
}
#[must_use]
pub fn value_type(&self, value: Value) -> Option<Type> {
Some(self.values.get(value.index())?.ty)
}
#[must_use]
pub fn value_block(&self, value: Value) -> Option<Block> {
match self.values.get(value.index())?.def {
ValueDef::Param(block) | ValueDef::Inst(block, _) => Some(block),
}
}
pub fn validate(&self) -> Result<(), ValidationError> {
crate::validate::validate(self)
}
}
impl fmt::Display for Function {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "fn {}(", self.name)?;
for (i, ty) in self.params.iter().enumerate() {
if i != 0 {
f.write_str(", ")?;
}
write!(f, "{ty}")?;
}
writeln!(f, ") -> {} {{", self.ret)?;
for block in self.blocks() {
write_block(f, self, block)?;
}
f.write_str("}")
}
}
fn write_block(f: &mut fmt::Formatter<'_>, func: &Function, block: Block) -> fmt::Result {
write!(f, " {block}(")?;
for (i, ¶m) in func.block_params(block).iter().enumerate() {
if i != 0 {
f.write_str(", ")?;
}
let ty = func.value_type(param).unwrap_or(Type::Unit);
write!(f, "{param}: {ty}")?;
}
writeln!(f, "):")?;
for &value in func.insts(block) {
let ty = func.value_type(value).unwrap_or(Type::Unit);
match func.inst(value) {
Some(inst) => writeln!(f, " {value}: {ty} = {}", FmtInst(inst))?,
None => writeln!(f, " {value}: {ty} = ?")?,
}
}
match func.terminator(block) {
Some(term) => writeln!(f, " {}", FmtTerm(term))?,
None => writeln!(f, " <missing terminator>")?,
}
Ok(())
}
struct FmtInst<'a>(&'a Inst);
impl fmt::Display for FmtInst<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.0 {
Inst::Iconst(v) => write!(f, "iconst {v}"),
Inst::Fconst(v) => write!(f, "fconst {v}"),
Inst::Bconst(v) => write!(f, "bconst {v}"),
Inst::Bin(op, a, b) => write!(f, "{op} {a}, {b}"),
Inst::Un(op, a) => write!(f, "{op} {a}"),
}
}
}
struct FmtTerm<'a>(&'a Terminator);
impl fmt::Display for FmtTerm<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.0 {
Terminator::Return(None) => f.write_str("return"),
Terminator::Return(Some(v)) => write!(f, "return {v}"),
Terminator::Jump(target, args) => {
write!(f, "jump {target}")?;
write_args(f, args)
}
Terminator::Branch {
cond,
then_block,
then_args,
else_block,
else_args,
} => {
write!(f, "branch {cond}, {then_block}")?;
write_args(f, then_args)?;
write!(f, ", {else_block}")?;
write_args(f, else_args)
}
}
}
}
fn write_args(f: &mut fmt::Formatter<'_>, args: &[Value]) -> fmt::Result {
if args.is_empty() {
return Ok(());
}
f.write_str("(")?;
for (i, arg) in args.iter().enumerate() {
if i != 0 {
f.write_str(", ")?;
}
write!(f, "{arg}")?;
}
f.write_str(")")
}
#[cfg(test)]
mod tests {
use crate::{BinOp, Builder, Type};
#[test]
fn test_display_renders_signature_block_and_terminator() {
let mut b = Builder::new("double", &[Type::Int], Type::Int);
let x = b.block_params(b.entry())[0];
let sum = b.bin(BinOp::Add, x, x);
b.ret(Some(sum));
let text = b.finish().to_string();
assert!(text.starts_with("fn double(int) -> int {"));
assert!(text.contains("b0(v0: int):"));
assert!(text.contains("v1: int = add v0, v0"));
assert!(text.contains("return v1"));
}
#[test]
fn test_accessors_report_value_origin() {
let mut b = Builder::new("f", &[Type::Int], Type::Int);
let p = b.block_params(b.entry())[0];
let five = b.iconst(5);
b.ret(Some(p));
let func = b.finish();
assert_eq!(func.value_block(p), Some(func.entry()));
assert_eq!(func.value_block(five), Some(func.entry()));
assert!(func.inst(p).is_none());
assert!(func.inst(five).is_some());
assert_eq!(func.value_type(five), Some(Type::Int));
}
#[test]
fn test_out_of_range_handles_return_none_not_panic() {
let func = Builder::new("f", &[], Type::Unit).finish();
let bogus_value = crate::Value::from_raw(99);
let bogus_block = crate::Block::from_raw(99);
assert_eq!(func.value_type(bogus_value), None);
assert_eq!(func.value_block(bogus_value), None);
assert!(func.inst(bogus_value).is_none());
assert!(func.terminator(bogus_block).is_none());
assert!(func.block_params(bogus_block).is_empty());
assert!(func.insts(bogus_block).is_empty());
}
}