1use llvm_ir::{BlockId, Function, InstrId, InstrKind, ValueRef};
24use std::collections::HashMap;
25
26pub struct UseDefInfo {
28 instr_block: HashMap<InstrId, BlockId>,
30 uses: HashMap<ValueRef, Vec<(BlockId, InstrId)>>,
37 phi_uses: HashMap<ValueRef, Vec<(BlockId, InstrId)>>,
45}
46
47impl UseDefInfo {
48 pub fn compute(func: &Function) -> Self {
50 let mut instr_block: HashMap<InstrId, BlockId> = HashMap::new();
51 let mut uses: HashMap<ValueRef, Vec<(BlockId, InstrId)>> = HashMap::new();
52 let mut phi_uses: HashMap<ValueRef, Vec<(BlockId, InstrId)>> = HashMap::new();
53
54 for (bi, bb) in func.blocks.iter().enumerate() {
55 let bid = BlockId(bi as u32);
56 for iid in bb.instrs() {
57 instr_block.insert(iid, bid);
58 let instr = func.instr(iid);
59 match &instr.kind {
60 InstrKind::Phi { incoming, .. } => {
61 for (val, pred) in incoming {
62 uses.entry(*val).or_default().push((bid, iid));
64 phi_uses.entry(*val).or_default().push((*pred, iid));
66 }
67 }
68 _ => {
69 for operand in instr.kind.operands() {
70 uses.entry(operand).or_default().push((bid, iid));
71 }
72 }
73 }
74 }
75 }
76
77 UseDefInfo {
78 instr_block,
79 uses,
80 phi_uses,
81 }
82 }
83
84 pub fn def_block(&self, id: InstrId) -> Option<BlockId> {
86 self.instr_block.get(&id).copied()
87 }
88
89 pub fn uses_of(&self, vref: ValueRef) -> &[(BlockId, InstrId)] {
97 self.uses.get(&vref).map(Vec::as_slice).unwrap_or(&[])
98 }
99
100 pub fn phi_uses_of(&self, vref: ValueRef) -> &[(BlockId, InstrId)] {
109 self.phi_uses.get(&vref).map(Vec::as_slice).unwrap_or(&[])
110 }
111
112 pub fn is_dead(&self, vref: ValueRef) -> bool {
114 self.uses_of(vref).is_empty()
115 }
116}
117
118#[cfg(test)]
119mod tests {
120 use super::*;
121 use llvm_ir::{ArgId, Builder, Context, Linkage, Module};
122
123 fn make_add_fn() -> (Context, Module) {
124 let mut ctx = Context::new();
125 let mut module = Module::new("test");
126 let mut b = Builder::new(&mut ctx, &mut module);
127 b.add_function(
128 "add",
129 b.ctx.i32_ty,
130 vec![b.ctx.i32_ty, b.ctx.i32_ty],
131 vec!["a".into(), "b".into()],
132 false,
133 Linkage::External,
134 );
135 let entry = b.add_block("entry");
136 b.position_at_end(entry);
137 let a = b.get_arg(0);
138 let bv = b.get_arg(1);
139 let sum = b.build_add("sum", a, bv);
140 b.build_ret(sum);
141 (ctx, module)
142 }
143
144 #[test]
145 fn use_def_basic() {
146 let (_ctx, module) = make_add_fn();
147 let func = &module.functions[0];
148 let info = UseDefInfo::compute(func);
149
150 assert_eq!(info.def_block(InstrId(0)), Some(BlockId(0)));
152
153 let sum_ref = ValueRef::Instruction(InstrId(0));
155 assert_eq!(info.uses_of(sum_ref).len(), 1);
156
157 assert_eq!(info.uses_of(ValueRef::Argument(ArgId(0))).len(), 1);
159 assert_eq!(info.uses_of(ValueRef::Argument(ArgId(1))).len(), 1);
160 }
161
162 #[test]
163 fn use_def_dead_value() {
164 let mut ctx = Context::new();
165 let mut module = Module::new("test");
166 let mut b = Builder::new(&mut ctx, &mut module);
167 b.add_function(
168 "f",
169 b.ctx.i32_ty,
170 vec![b.ctx.i32_ty],
171 vec!["x".into()],
172 false,
173 Linkage::External,
174 );
175 let entry = b.add_block("entry");
176 b.position_at_end(entry);
177 let x = b.get_arg(0);
178 let _dead = b.build_add("dead", x, x); b.build_ret(x);
180
181 let func = &module.functions[0];
182 let info = UseDefInfo::compute(func);
183
184 assert!(info.is_dead(ValueRef::Instruction(InstrId(0))));
186 assert_eq!(info.uses_of(ValueRef::Argument(ArgId(0))).len(), 3);
188 }
189
190 #[test]
191 fn use_def_multi_block() {
192 let mut ctx = Context::new();
193 let mut module = Module::new("test");
194 let mut b = Builder::new(&mut ctx, &mut module);
195 b.add_function(
196 "f",
197 b.ctx.void_ty,
198 vec![b.ctx.i1_ty],
199 vec!["c".into()],
200 false,
201 Linkage::External,
202 );
203 let entry = b.add_block("entry");
204 let then_bb = b.add_block("then");
205 let else_bb = b.add_block("else");
206 b.position_at_end(entry);
207 let cond = b.get_arg(0);
208 b.build_cond_br(cond, then_bb, else_bb);
209 b.position_at_end(then_bb);
210 b.build_ret_void();
211 b.position_at_end(else_bb);
212 b.build_ret_void();
213
214 let func = &module.functions[0];
215 let info = UseDefInfo::compute(func);
216
217 let uses = info.uses_of(ValueRef::Argument(ArgId(0)));
219 assert_eq!(uses.len(), 1);
220 assert_eq!(uses[0].0, BlockId(0));
221 }
222
223 fn make_phi_fn() -> (Context, Module) {
233 let mut ctx = Context::new();
234 let mut module = Module::new("test");
235 let mut b = Builder::new(&mut ctx, &mut module);
236 b.add_function(
237 "phi_fn",
238 b.ctx.i32_ty,
239 vec![b.ctx.i1_ty, b.ctx.i32_ty, b.ctx.i32_ty],
240 vec!["cond".into(), "a".into(), "bv".into()],
241 false,
242 Linkage::External,
243 );
244 let entry = b.add_block("entry");
245 let then_bb = b.add_block("then");
246 let else_bb = b.add_block("else");
247 let merge = b.add_block("merge");
248
249 b.position_at_end(entry);
250 let cond = b.get_arg(0);
251 b.build_cond_br(cond, then_bb, else_bb);
252
253 b.position_at_end(then_bb);
254 b.build_br(merge);
255
256 b.position_at_end(else_bb);
257 b.build_br(merge);
258
259 b.position_at_end(merge);
260 let a = b.get_arg(1);
261 let bv = b.get_arg(2);
262 let v = b.build_phi("v", b.ctx.i32_ty, vec![(a, then_bb), (bv, else_bb)]);
263 b.build_ret(v);
264
265 (ctx, module)
266 }
267
268 #[test]
269 fn phi_uses_of_records_predecessor_block() {
270 let (_ctx, module) = make_phi_fn();
271 let func = &module.functions[0];
272 let info = UseDefInfo::compute(func);
273
274 let a_ref = ValueRef::Argument(ArgId(1));
277 let b_ref = ValueRef::Argument(ArgId(2));
278
279 let a_phi_uses = info.phi_uses_of(a_ref);
280 assert_eq!(
281 a_phi_uses.len(),
282 1,
283 "a should appear in exactly one phi incoming list"
284 );
285 assert_eq!(
286 a_phi_uses[0].0,
287 BlockId(1),
288 "a's phi use should be at predecessor then_bb (block 1), not merge"
289 );
290
291 let b_phi_uses = info.phi_uses_of(b_ref);
292 assert_eq!(b_phi_uses.len(), 1);
293 assert_eq!(
294 b_phi_uses[0].0,
295 BlockId(2),
296 "bv's phi use should be at predecessor else_bb (block 2), not merge"
297 );
298 }
299
300 #[test]
301 fn uses_of_phi_operand_still_at_phi_block() {
302 let (_ctx, module) = make_phi_fn();
305 let func = &module.functions[0];
306 let info = UseDefInfo::compute(func);
307
308 let a_ref = ValueRef::Argument(ArgId(1));
309 let uses = info.uses_of(a_ref);
310 assert_eq!(uses.len(), 1);
312 assert_eq!(
313 uses[0].0,
314 BlockId(3),
315 "uses_of should record phi operands at the phi's own block (merge = block 3)"
316 );
317
318 assert!(!info.is_dead(a_ref));
320 }
321
322 #[test]
323 fn non_phi_operands_absent_from_phi_uses() {
324 let (_ctx, module) = make_add_fn();
326 let func = &module.functions[0];
327 let info = UseDefInfo::compute(func);
328
329 assert!(info.phi_uses_of(ValueRef::Argument(ArgId(0))).is_empty());
331 assert!(info.phi_uses_of(ValueRef::Argument(ArgId(1))).is_empty());
332 }
333}