use crate::instruction::Instruction;
use crate::loc::{Located, SourceLoc};
use std::fmt;
#[derive(Debug, Clone)]
pub struct Block {
pub label: String,
pub instructions: Vec<Located>,
}
impl fmt::Display for Block {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
writeln!(f, "{}:", self.label)?;
for instr in &self.instructions {
writeln!(f, " {instr}")?;
}
Ok(())
}
}
impl PartialEq for Block {
fn eq(&self, other: &Self) -> bool {
self.label == other.label
}
}
impl Block {
pub fn new(label: impl Into<String>) -> Self {
Self {
label: label.into(),
instructions: Vec::new(),
}
}
pub fn push(&mut self, instr: impl Into<Located>) {
self.instructions.push(instr.into());
}
pub fn push_at(&mut self, instr: Instruction, loc: Option<SourceLoc>) {
self.instructions.push(Located::at(instr, loc));
}
pub fn terminator(&self) -> Option<&Instruction> {
self.instructions.last().map(|i| &i.instr)
}
pub fn has_terminator(&self) -> bool {
self.terminator().is_some_and(Instruction::is_terminator)
}
pub fn body(&self) -> &[Located] {
if self.has_terminator() {
&self.instructions[..self.instructions.len() - 1]
} else {
&self.instructions
}
}
pub fn len(&self) -> usize {
self.instructions.len()
}
pub fn is_empty(&self) -> bool {
self.instructions.is_empty()
}
pub fn successors(&self) -> Vec<&str> {
match self.terminator() {
Some(Instruction::Jmp { target }) => vec![target.as_str()],
Some(Instruction::Br {
true_target,
false_target,
..
}) => vec![true_target.as_str(), false_target.as_str()],
Some(Instruction::Switch { cases, default, .. }) => {
let mut targets = vec![default.as_str()];
targets.extend(cases.iter().map(|(_, t)| t.as_str()));
targets
}
_ => vec![],
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{
instruction::{Immediate, Instruction, IntBinOp, Operand},
register::{Register, VirtualReg},
types::{MirType, ScalarType},
};
#[test]
fn test_basic_block_push() {
let mut bb = Block::new("bb0");
let add = Instruction::IntBinary {
op: IntBinOp::Add,
ty: MirType::Scalar(ScalarType::I64),
dst: Register::Virtual(VirtualReg::gpr(0)),
lhs: Operand::Immediate(Immediate::I64(1)),
rhs: Operand::Immediate(Immediate::I64(2)),
};
bb.push(add);
assert_eq!(bb.len(), 1);
}
#[test]
fn test_basic_block_terminator() {
let mut bb = Block::new("bb0");
let add = Instruction::IntBinary {
op: IntBinOp::Add,
ty: MirType::Scalar(ScalarType::I64),
dst: Register::Virtual(VirtualReg::gpr(0)),
lhs: Operand::Immediate(Immediate::I64(1)),
rhs: Operand::Immediate(Immediate::I64(2)),
};
let ret = Instruction::Ret { value: None };
bb.push(add.clone());
assert!(!bb.has_terminator());
bb.push(ret);
assert!(bb.has_terminator());
assert_eq!(bb.body().len(), 1);
}
}