wave_compiler/mir/
basic_block.rs1use super::instruction::MirInst;
10use super::value::{BlockId, ValueId};
11use crate::mir::types::MirType;
12
13#[derive(Debug, Clone, PartialEq)]
15pub struct PhiNode {
16 pub dest: ValueId,
18 pub ty: MirType,
20 pub incoming: Vec<(BlockId, ValueId)>,
22}
23
24#[derive(Debug, Clone, PartialEq)]
26pub enum Terminator {
27 Branch {
29 target: BlockId,
31 },
32 CondBranch {
34 cond: ValueId,
36 true_target: BlockId,
38 false_target: BlockId,
40 },
41 Return,
43}
44
45impl Terminator {
46 #[must_use]
48 pub fn successors(&self) -> Vec<BlockId> {
49 match self {
50 Self::Branch { target } => vec![*target],
51 Self::CondBranch {
52 true_target,
53 false_target,
54 ..
55 } => vec![*true_target, *false_target],
56 Self::Return => vec![],
57 }
58 }
59
60 #[must_use]
62 pub fn operands(&self) -> Vec<ValueId> {
63 match self {
64 Self::Branch { .. } | Self::Return => vec![],
65 Self::CondBranch { cond, .. } => vec![*cond],
66 }
67 }
68}
69
70#[derive(Debug, Clone, PartialEq)]
72pub struct BasicBlock {
73 pub id: BlockId,
75 pub phis: Vec<PhiNode>,
77 pub instructions: Vec<MirInst>,
79 pub terminator: Terminator,
81}
82
83impl BasicBlock {
84 #[must_use]
86 pub fn new(id: BlockId) -> Self {
87 Self {
88 id,
89 phis: Vec::new(),
90 instructions: Vec::new(),
91 terminator: Terminator::Return,
92 }
93 }
94
95 #[must_use]
97 pub fn successors(&self) -> Vec<BlockId> {
98 self.terminator.successors()
99 }
100
101 #[must_use]
103 pub fn defs(&self) -> Vec<ValueId> {
104 let mut defs: Vec<ValueId> = self.phis.iter().map(|phi| phi.dest).collect();
105 for inst in &self.instructions {
106 if let Some(dest) = inst.dest() {
107 defs.push(dest);
108 }
109 }
110 defs
111 }
112
113 #[must_use]
115 pub fn uses(&self) -> Vec<ValueId> {
116 let mut uses = Vec::new();
117 for phi in &self.phis {
118 for (_, val) in &phi.incoming {
119 uses.push(*val);
120 }
121 }
122 for inst in &self.instructions {
123 uses.extend(inst.operands());
124 }
125 uses.extend(self.terminator.operands());
126 uses
127 }
128}
129
130#[cfg(test)]
131mod tests {
132 use super::*;
133 use crate::hir::expr::BinOp;
134 use crate::mir::types::MirType;
135
136 #[test]
137 fn test_basic_block_successors() {
138 let mut bb = BasicBlock::new(BlockId(0));
139 bb.terminator = Terminator::CondBranch {
140 cond: ValueId(0),
141 true_target: BlockId(1),
142 false_target: BlockId(2),
143 };
144 let succs = bb.successors();
145 assert_eq!(succs, vec![BlockId(1), BlockId(2)]);
146 }
147
148 #[test]
149 fn test_basic_block_defs_and_uses() {
150 let mut bb = BasicBlock::new(BlockId(0));
151 bb.instructions.push(MirInst::BinOp {
152 dest: ValueId(2),
153 op: BinOp::Add,
154 lhs: ValueId(0),
155 rhs: ValueId(1),
156 ty: MirType::I32,
157 });
158 bb.terminator = Terminator::Return;
159
160 let defs = bb.defs();
161 assert_eq!(defs, vec![ValueId(2)]);
162
163 let uses = bb.uses();
164 assert_eq!(uses, vec![ValueId(0), ValueId(1)]);
165 }
166
167 #[test]
168 fn test_phi_node() {
169 let phi = PhiNode {
170 dest: ValueId(3),
171 ty: MirType::I32,
172 incoming: vec![(BlockId(0), ValueId(1)), (BlockId(1), ValueId(2))],
173 };
174 assert_eq!(phi.dest, ValueId(3));
175 assert_eq!(phi.incoming.len(), 2);
176 }
177
178 #[test]
179 fn test_terminator_operands() {
180 let term = Terminator::CondBranch {
181 cond: ValueId(5),
182 true_target: BlockId(1),
183 false_target: BlockId(2),
184 };
185 assert_eq!(term.operands(), vec![ValueId(5)]);
186
187 let ret = Terminator::Return;
188 assert!(ret.operands().is_empty());
189 }
190}