2025-08-12 11:33:48 +00:00
|
|
|
/*!
|
|
|
|
|
* MIR Basic Block - Control Flow Graph Building Block
|
2025-09-17 07:43:07 +09:00
|
|
|
*
|
2025-08-12 11:33:48 +00:00
|
|
|
* SSA-form basic blocks with phi functions and terminator instructions
|
|
|
|
|
*/
|
|
|
|
|
|
2025-09-17 07:43:07 +09:00
|
|
|
use super::{EffectMask, MirInstruction, ValueId};
|
2025-08-12 11:33:48 +00:00
|
|
|
use std::collections::HashSet;
|
|
|
|
|
use std::fmt;
|
|
|
|
|
|
|
|
|
|
/// Unique identifier for basic blocks within a function
|
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
|
|
|
|
pub struct BasicBlockId(pub u32);
|
|
|
|
|
|
|
|
|
|
impl BasicBlockId {
|
|
|
|
|
/// Create a new BasicBlockId
|
|
|
|
|
pub fn new(id: u32) -> Self {
|
|
|
|
|
BasicBlockId(id)
|
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
2025-08-12 11:33:48 +00:00
|
|
|
/// Get the raw ID value
|
|
|
|
|
pub fn as_u32(self) -> u32 {
|
|
|
|
|
self.0
|
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
2025-08-12 11:33:48 +00:00
|
|
|
/// Create BasicBlockId from usize (for array indexing)
|
|
|
|
|
pub fn from_usize(id: usize) -> Self {
|
|
|
|
|
BasicBlockId(id as u32)
|
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
2025-08-12 11:33:48 +00:00
|
|
|
/// Convert to usize (for array indexing)
|
|
|
|
|
pub fn to_usize(self) -> usize {
|
|
|
|
|
self.0 as usize
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl fmt::Display for BasicBlockId {
|
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
|
|
|
write!(f, "bb{}", self.0)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// A basic block in SSA form
|
|
|
|
|
#[derive(Debug, Clone)]
|
|
|
|
|
pub struct BasicBlock {
|
|
|
|
|
/// Unique identifier for this block
|
|
|
|
|
pub id: BasicBlockId,
|
2025-09-17 07:43:07 +09:00
|
|
|
|
2025-08-12 11:33:48 +00:00
|
|
|
/// Instructions in this block (excluding terminator)
|
|
|
|
|
pub instructions: Vec<MirInstruction>,
|
2025-09-17 07:43:07 +09:00
|
|
|
|
2025-08-12 11:33:48 +00:00
|
|
|
/// Terminator instruction (branch, jump, or return)
|
|
|
|
|
pub terminator: Option<MirInstruction>,
|
2025-09-17 07:43:07 +09:00
|
|
|
|
2025-08-12 11:33:48 +00:00
|
|
|
/// Predecessors in the control flow graph
|
|
|
|
|
pub predecessors: HashSet<BasicBlockId>,
|
2025-09-17 07:43:07 +09:00
|
|
|
|
2025-08-12 11:33:48 +00:00
|
|
|
/// Successors in the control flow graph
|
|
|
|
|
pub successors: HashSet<BasicBlockId>,
|
2025-09-17 07:43:07 +09:00
|
|
|
|
2025-08-12 11:33:48 +00:00
|
|
|
/// Combined effect mask for all instructions in this block
|
|
|
|
|
pub effects: EffectMask,
|
2025-09-17 07:43:07 +09:00
|
|
|
|
2025-08-12 11:33:48 +00:00
|
|
|
/// Whether this block is reachable from the entry block
|
|
|
|
|
pub reachable: bool,
|
2025-09-17 07:43:07 +09:00
|
|
|
|
2025-08-18 23:36:40 +09:00
|
|
|
/// Is this block sealed? (all predecessors are known)
|
|
|
|
|
pub sealed: bool,
|
2025-08-12 11:33:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl BasicBlock {
|
|
|
|
|
/// Create a new basic block
|
|
|
|
|
pub fn new(id: BasicBlockId) -> Self {
|
|
|
|
|
Self {
|
|
|
|
|
id,
|
|
|
|
|
instructions: Vec::new(),
|
|
|
|
|
terminator: None,
|
|
|
|
|
predecessors: HashSet::new(),
|
|
|
|
|
successors: HashSet::new(),
|
|
|
|
|
effects: EffectMask::PURE,
|
|
|
|
|
reachable: false,
|
2025-08-18 23:36:40 +09:00
|
|
|
sealed: false,
|
2025-08-12 11:33:48 +00:00
|
|
|
}
|
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
2025-08-12 11:33:48 +00:00
|
|
|
/// Add an instruction to this block
|
|
|
|
|
pub fn add_instruction(&mut self, instruction: MirInstruction) {
|
|
|
|
|
// Update effect mask
|
|
|
|
|
self.effects = self.effects | instruction.effects();
|
2025-09-17 07:43:07 +09:00
|
|
|
|
2025-08-12 11:33:48 +00:00
|
|
|
// Check if this is a terminator instruction
|
|
|
|
|
if self.is_terminator(&instruction) {
|
|
|
|
|
if self.terminator.is_some() {
|
|
|
|
|
panic!("Basic block {} already has a terminator", self.id);
|
|
|
|
|
}
|
|
|
|
|
self.terminator = Some(instruction);
|
2025-09-17 07:43:07 +09:00
|
|
|
|
2025-08-12 11:33:48 +00:00
|
|
|
// Update successors based on terminator
|
|
|
|
|
self.update_successors_from_terminator();
|
|
|
|
|
} else {
|
|
|
|
|
self.instructions.push(instruction);
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
2025-08-12 11:33:48 +00:00
|
|
|
/// Check if an instruction is a terminator
|
|
|
|
|
fn is_terminator(&self, instruction: &MirInstruction) -> bool {
|
2025-09-17 07:43:07 +09:00
|
|
|
matches!(
|
|
|
|
|
instruction,
|
|
|
|
|
MirInstruction::Branch { .. }
|
|
|
|
|
| MirInstruction::Jump { .. }
|
|
|
|
|
| MirInstruction::Return { .. }
|
|
|
|
|
| MirInstruction::Throw { .. }
|
2025-08-12 11:33:48 +00:00
|
|
|
)
|
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
2025-08-12 11:33:48 +00:00
|
|
|
/// Update successors based on the terminator instruction
|
|
|
|
|
fn update_successors_from_terminator(&mut self) {
|
|
|
|
|
self.successors.clear();
|
2025-09-17 07:43:07 +09:00
|
|
|
|
2025-08-12 11:33:48 +00:00
|
|
|
if let Some(ref terminator) = self.terminator {
|
|
|
|
|
match terminator {
|
2025-09-17 07:43:07 +09:00
|
|
|
MirInstruction::Branch {
|
|
|
|
|
then_bb, else_bb, ..
|
|
|
|
|
} => {
|
2025-08-12 11:33:48 +00:00
|
|
|
self.successors.insert(*then_bb);
|
|
|
|
|
self.successors.insert(*else_bb);
|
2025-09-17 07:43:07 +09:00
|
|
|
}
|
2025-08-12 11:33:48 +00:00
|
|
|
MirInstruction::Jump { target } => {
|
|
|
|
|
self.successors.insert(*target);
|
2025-09-17 07:43:07 +09:00
|
|
|
}
|
2025-08-12 11:33:48 +00:00
|
|
|
MirInstruction::Return { .. } => {
|
|
|
|
|
// No successors for return
|
2025-09-17 07:43:07 +09:00
|
|
|
}
|
2025-08-13 07:13:53 +00:00
|
|
|
MirInstruction::Throw { .. } => {
|
|
|
|
|
// No normal successors for throw - control goes to exception handlers
|
|
|
|
|
// Exception edges are handled separately from normal control flow
|
2025-09-17 07:43:07 +09:00
|
|
|
}
|
2025-08-12 11:33:48 +00:00
|
|
|
_ => unreachable!("Non-terminator instruction in terminator position"),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
2025-08-12 11:33:48 +00:00
|
|
|
/// Add a predecessor
|
|
|
|
|
pub fn add_predecessor(&mut self, pred: BasicBlockId) {
|
|
|
|
|
self.predecessors.insert(pred);
|
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
2025-08-12 11:33:48 +00:00
|
|
|
/// Remove a predecessor
|
|
|
|
|
pub fn remove_predecessor(&mut self, pred: BasicBlockId) {
|
|
|
|
|
self.predecessors.remove(&pred);
|
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
2025-08-12 11:33:48 +00:00
|
|
|
/// Get all instructions including terminator
|
|
|
|
|
pub fn all_instructions(&self) -> impl Iterator<Item = &MirInstruction> {
|
|
|
|
|
self.instructions.iter().chain(self.terminator.iter())
|
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
2025-08-12 11:33:48 +00:00
|
|
|
/// Get all values defined in this block
|
|
|
|
|
pub fn defined_values(&self) -> Vec<ValueId> {
|
|
|
|
|
self.all_instructions()
|
|
|
|
|
.filter_map(|inst| inst.dst_value())
|
|
|
|
|
.collect()
|
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
2025-08-12 11:33:48 +00:00
|
|
|
/// Get all values used in this block
|
|
|
|
|
pub fn used_values(&self) -> Vec<ValueId> {
|
|
|
|
|
self.all_instructions()
|
|
|
|
|
.flat_map(|inst| inst.used_values())
|
|
|
|
|
.collect()
|
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
2025-08-12 11:33:48 +00:00
|
|
|
/// Check if this block is empty (no instructions)
|
|
|
|
|
pub fn is_empty(&self) -> bool {
|
|
|
|
|
self.instructions.is_empty() && self.terminator.is_none()
|
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
2025-08-12 11:33:48 +00:00
|
|
|
/// Check if this block has a terminator
|
|
|
|
|
pub fn is_terminated(&self) -> bool {
|
|
|
|
|
self.terminator.is_some()
|
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
2025-08-12 11:33:48 +00:00
|
|
|
/// Check if this block ends with a return
|
|
|
|
|
pub fn ends_with_return(&self) -> bool {
|
|
|
|
|
matches!(self.terminator, Some(MirInstruction::Return { .. }))
|
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
2025-08-12 11:33:48 +00:00
|
|
|
/// Get the phi instructions at the beginning of this block
|
|
|
|
|
pub fn phi_instructions(&self) -> impl Iterator<Item = &MirInstruction> {
|
2025-09-17 07:43:07 +09:00
|
|
|
self.instructions
|
|
|
|
|
.iter()
|
2025-08-12 11:33:48 +00:00
|
|
|
.take_while(|inst| matches!(inst, MirInstruction::Phi { .. }))
|
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
2025-08-12 11:33:48 +00:00
|
|
|
/// Get non-phi instructions
|
|
|
|
|
pub fn non_phi_instructions(&self) -> impl Iterator<Item = &MirInstruction> {
|
2025-09-17 07:43:07 +09:00
|
|
|
self.instructions
|
|
|
|
|
.iter()
|
2025-08-12 11:33:48 +00:00
|
|
|
.skip_while(|inst| matches!(inst, MirInstruction::Phi { .. }))
|
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
2025-08-12 11:33:48 +00:00
|
|
|
/// Insert instruction at the beginning (after phi instructions)
|
|
|
|
|
pub fn insert_instruction_after_phis(&mut self, instruction: MirInstruction) {
|
|
|
|
|
let phi_count = self.phi_instructions().count();
|
|
|
|
|
self.effects = self.effects | instruction.effects();
|
|
|
|
|
self.instructions.insert(phi_count, instruction);
|
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
2025-08-12 11:33:48 +00:00
|
|
|
/// Replace terminator instruction
|
|
|
|
|
pub fn set_terminator(&mut self, terminator: MirInstruction) {
|
|
|
|
|
if !self.is_terminator(&terminator) {
|
|
|
|
|
panic!("Instruction is not a valid terminator: {:?}", terminator);
|
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
2025-08-12 11:33:48 +00:00
|
|
|
self.effects = self.effects | terminator.effects();
|
|
|
|
|
self.terminator = Some(terminator);
|
|
|
|
|
self.update_successors_from_terminator();
|
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
2025-08-12 11:33:48 +00:00
|
|
|
/// Mark this block as reachable
|
|
|
|
|
pub fn mark_reachable(&mut self) {
|
|
|
|
|
self.reachable = true;
|
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
2025-08-18 23:36:40 +09:00
|
|
|
/// Seal this block (all predecessors are known)
|
|
|
|
|
pub fn seal(&mut self) {
|
|
|
|
|
self.sealed = true;
|
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
2025-08-18 23:36:40 +09:00
|
|
|
/// Check if this block is sealed
|
|
|
|
|
pub fn is_sealed(&self) -> bool {
|
|
|
|
|
self.sealed
|
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
2025-08-12 11:33:48 +00:00
|
|
|
/// Check if this block dominates another block (simplified check)
|
|
|
|
|
pub fn dominates(&self, other: BasicBlockId, dominators: &[HashSet<BasicBlockId>]) -> bool {
|
|
|
|
|
if let Some(dom_set) = dominators.get(other.to_usize()) {
|
|
|
|
|
dom_set.contains(&self.id)
|
|
|
|
|
} else {
|
|
|
|
|
false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Basic block ID generator
|
|
|
|
|
#[derive(Debug, Clone)]
|
|
|
|
|
pub struct BasicBlockIdGenerator {
|
|
|
|
|
next_id: u32,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl BasicBlockIdGenerator {
|
|
|
|
|
/// Create a new generator starting from 0
|
|
|
|
|
pub fn new() -> Self {
|
|
|
|
|
Self { next_id: 0 }
|
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
2025-08-12 11:33:48 +00:00
|
|
|
/// Generate the next unique BasicBlockId
|
|
|
|
|
pub fn next(&mut self) -> BasicBlockId {
|
|
|
|
|
let id = BasicBlockId(self.next_id);
|
|
|
|
|
self.next_id += 1;
|
|
|
|
|
id
|
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
2025-08-12 11:33:48 +00:00
|
|
|
/// Peek at the next ID without consuming it
|
|
|
|
|
pub fn peek_next(&self) -> BasicBlockId {
|
|
|
|
|
BasicBlockId(self.next_id)
|
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
2025-08-12 11:33:48 +00:00
|
|
|
/// Reset the generator (for testing)
|
|
|
|
|
pub fn reset(&mut self) {
|
|
|
|
|
self.next_id = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Default for BasicBlockIdGenerator {
|
|
|
|
|
fn default() -> Self {
|
|
|
|
|
Self::new()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl fmt::Display for BasicBlock {
|
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
|
|
|
writeln!(f, "{}:", self.id)?;
|
2025-09-17 07:43:07 +09:00
|
|
|
|
2025-08-12 11:33:48 +00:00
|
|
|
// Show predecessors
|
|
|
|
|
if !self.predecessors.is_empty() {
|
2025-09-17 07:43:07 +09:00
|
|
|
let preds: Vec<String> = self.predecessors.iter().map(|p| format!("{}", p)).collect();
|
2025-08-12 11:33:48 +00:00
|
|
|
writeln!(f, " ; preds: {}", preds.join(", "))?;
|
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
2025-08-12 11:33:48 +00:00
|
|
|
// Show instructions
|
|
|
|
|
for instruction in &self.instructions {
|
|
|
|
|
writeln!(f, " {}", instruction)?;
|
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
2025-08-12 11:33:48 +00:00
|
|
|
// Show terminator
|
|
|
|
|
if let Some(ref terminator) = self.terminator {
|
|
|
|
|
writeln!(f, " {}", terminator)?;
|
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
2025-08-12 11:33:48 +00:00
|
|
|
// Show effects if not pure
|
|
|
|
|
if !self.effects.is_pure() {
|
|
|
|
|
writeln!(f, " ; effects: {}", self.effects)?;
|
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
2025-08-12 11:33:48 +00:00
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
|
mod tests {
|
|
|
|
|
use super::*;
|
2025-09-17 07:43:07 +09:00
|
|
|
use crate::mir::{BinaryOp, ConstValue};
|
|
|
|
|
|
2025-08-12 11:33:48 +00:00
|
|
|
#[test]
|
|
|
|
|
fn test_basic_block_creation() {
|
|
|
|
|
let bb_id = BasicBlockId::new(0);
|
|
|
|
|
let mut bb = BasicBlock::new(bb_id);
|
2025-09-17 07:43:07 +09:00
|
|
|
|
2025-08-12 11:33:48 +00:00
|
|
|
assert_eq!(bb.id, bb_id);
|
|
|
|
|
assert!(bb.is_empty());
|
|
|
|
|
assert!(!bb.is_terminated());
|
|
|
|
|
assert!(bb.effects.is_pure());
|
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
2025-08-12 11:33:48 +00:00
|
|
|
#[test]
|
|
|
|
|
fn test_instruction_addition() {
|
|
|
|
|
let bb_id = BasicBlockId::new(0);
|
|
|
|
|
let mut bb = BasicBlock::new(bb_id);
|
2025-09-17 07:43:07 +09:00
|
|
|
|
2025-08-12 11:33:48 +00:00
|
|
|
let const_inst = MirInstruction::Const {
|
|
|
|
|
dst: ValueId::new(0),
|
|
|
|
|
value: ConstValue::Integer(42),
|
|
|
|
|
};
|
2025-09-17 07:43:07 +09:00
|
|
|
|
2025-08-12 11:33:48 +00:00
|
|
|
bb.add_instruction(const_inst);
|
2025-09-17 07:43:07 +09:00
|
|
|
|
2025-08-12 11:33:48 +00:00
|
|
|
assert_eq!(bb.instructions.len(), 1);
|
|
|
|
|
assert!(!bb.is_empty());
|
|
|
|
|
assert!(bb.effects.is_pure());
|
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
2025-08-12 11:33:48 +00:00
|
|
|
#[test]
|
|
|
|
|
fn test_terminator_addition() {
|
|
|
|
|
let bb_id = BasicBlockId::new(0);
|
|
|
|
|
let mut bb = BasicBlock::new(bb_id);
|
2025-09-17 07:43:07 +09:00
|
|
|
|
2025-08-12 11:33:48 +00:00
|
|
|
let return_inst = MirInstruction::Return {
|
|
|
|
|
value: Some(ValueId::new(0)),
|
|
|
|
|
};
|
2025-09-17 07:43:07 +09:00
|
|
|
|
2025-08-12 11:33:48 +00:00
|
|
|
bb.add_instruction(return_inst);
|
2025-09-17 07:43:07 +09:00
|
|
|
|
2025-08-12 11:33:48 +00:00
|
|
|
assert!(bb.is_terminated());
|
|
|
|
|
assert!(bb.ends_with_return());
|
|
|
|
|
assert_eq!(bb.instructions.len(), 0); // Terminator not in instructions
|
|
|
|
|
assert!(bb.terminator.is_some());
|
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
2025-08-12 11:33:48 +00:00
|
|
|
#[test]
|
|
|
|
|
fn test_branch_successors() {
|
|
|
|
|
let bb_id = BasicBlockId::new(0);
|
|
|
|
|
let mut bb = BasicBlock::new(bb_id);
|
2025-09-17 07:43:07 +09:00
|
|
|
|
2025-08-12 11:33:48 +00:00
|
|
|
let then_bb = BasicBlockId::new(1);
|
|
|
|
|
let else_bb = BasicBlockId::new(2);
|
2025-09-17 07:43:07 +09:00
|
|
|
|
2025-08-12 11:33:48 +00:00
|
|
|
let branch_inst = MirInstruction::Branch {
|
|
|
|
|
condition: ValueId::new(0),
|
|
|
|
|
then_bb,
|
|
|
|
|
else_bb,
|
|
|
|
|
};
|
2025-09-17 07:43:07 +09:00
|
|
|
|
2025-08-12 11:33:48 +00:00
|
|
|
bb.add_instruction(branch_inst);
|
2025-09-17 07:43:07 +09:00
|
|
|
|
2025-08-12 11:33:48 +00:00
|
|
|
assert_eq!(bb.successors.len(), 2);
|
|
|
|
|
assert!(bb.successors.contains(&then_bb));
|
|
|
|
|
assert!(bb.successors.contains(&else_bb));
|
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
2025-08-12 11:33:48 +00:00
|
|
|
#[test]
|
|
|
|
|
fn test_basic_block_id_generator() {
|
|
|
|
|
let mut gen = BasicBlockIdGenerator::new();
|
2025-09-17 07:43:07 +09:00
|
|
|
|
2025-08-12 11:33:48 +00:00
|
|
|
let bb1 = gen.next();
|
|
|
|
|
let bb2 = gen.next();
|
|
|
|
|
let bb3 = gen.next();
|
2025-09-17 07:43:07 +09:00
|
|
|
|
2025-08-12 11:33:48 +00:00
|
|
|
assert_eq!(bb1, BasicBlockId(0));
|
|
|
|
|
assert_eq!(bb2, BasicBlockId(1));
|
|
|
|
|
assert_eq!(bb3, BasicBlockId(2));
|
2025-09-17 07:43:07 +09:00
|
|
|
|
2025-08-12 11:33:48 +00:00
|
|
|
assert_eq!(gen.peek_next(), BasicBlockId(3));
|
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
2025-08-12 11:33:48 +00:00
|
|
|
#[test]
|
|
|
|
|
fn test_value_tracking() {
|
|
|
|
|
let bb_id = BasicBlockId::new(0);
|
|
|
|
|
let mut bb = BasicBlock::new(bb_id);
|
2025-09-17 07:43:07 +09:00
|
|
|
|
2025-08-12 11:33:48 +00:00
|
|
|
let val1 = ValueId::new(1);
|
|
|
|
|
let val2 = ValueId::new(2);
|
|
|
|
|
let val3 = ValueId::new(3);
|
2025-09-17 07:43:07 +09:00
|
|
|
|
2025-08-12 11:33:48 +00:00
|
|
|
// Add instruction that defines val3 and uses val1, val2
|
|
|
|
|
bb.add_instruction(MirInstruction::BinOp {
|
|
|
|
|
dst: val3,
|
|
|
|
|
op: BinaryOp::Add,
|
|
|
|
|
lhs: val1,
|
|
|
|
|
rhs: val2,
|
|
|
|
|
});
|
2025-09-17 07:43:07 +09:00
|
|
|
|
2025-08-12 11:33:48 +00:00
|
|
|
let defined = bb.defined_values();
|
|
|
|
|
let used = bb.used_values();
|
2025-09-17 07:43:07 +09:00
|
|
|
|
2025-08-12 11:33:48 +00:00
|
|
|
assert_eq!(defined, vec![val3]);
|
|
|
|
|
assert_eq!(used, vec![val1, val2]);
|
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
2025-08-12 11:33:48 +00:00
|
|
|
#[test]
|
|
|
|
|
fn test_phi_instruction_ordering() {
|
|
|
|
|
let bb_id = BasicBlockId::new(0);
|
|
|
|
|
let mut bb = BasicBlock::new(bb_id);
|
2025-09-17 07:43:07 +09:00
|
|
|
|
2025-08-12 11:33:48 +00:00
|
|
|
// Add phi instruction
|
|
|
|
|
let phi_inst = MirInstruction::Phi {
|
|
|
|
|
dst: ValueId::new(0),
|
|
|
|
|
inputs: vec![(BasicBlockId::new(1), ValueId::new(1))],
|
|
|
|
|
};
|
|
|
|
|
bb.add_instruction(phi_inst);
|
2025-09-17 07:43:07 +09:00
|
|
|
|
2025-08-12 11:33:48 +00:00
|
|
|
// Add regular instruction
|
|
|
|
|
let const_inst = MirInstruction::Const {
|
|
|
|
|
dst: ValueId::new(2),
|
|
|
|
|
value: ConstValue::Integer(42),
|
|
|
|
|
};
|
|
|
|
|
bb.add_instruction(const_inst);
|
2025-09-17 07:43:07 +09:00
|
|
|
|
2025-08-12 11:33:48 +00:00
|
|
|
// Phi instructions should come first
|
|
|
|
|
let phi_count = bb.phi_instructions().count();
|
|
|
|
|
assert_eq!(phi_count, 1);
|
2025-09-17 07:43:07 +09:00
|
|
|
|
2025-08-12 11:33:48 +00:00
|
|
|
let non_phi_count = bb.non_phi_instructions().count();
|
|
|
|
|
assert_eq!(non_phi_count, 1);
|
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
}
|