Skip to main content

llvm_analysis/
use_def.rs

1//! Use-def and def-use chains.
2//!
3//! `UseDefInfo::compute` walks every instruction in a function and builds:
4//! - A map from each `InstrId` to the `BlockId` that contains its definition.
5//! - A map from each `ValueRef` to the list of `(BlockId, InstrId)` pairs
6//!   that use it as an operand.
7//!
8//! Only instruction-produced values are tracked as definitions. Arguments are
9//! always available at function entry; constants and globals have no
10//! definition block.
11//!
12//! # Phi operand semantics
13//!
14//! In SSA form, a phi incoming value `[v, %pred]` is semantically *used at
15//! the end of the predecessor block `%pred`*, not at the phi's own block.
16//! `UseDefInfo` exposes both views:
17//!
18//! | Accessor | Block recorded | Correct for |
19//! |----------|---------------|-------------|
20//! | `uses_of` | phi's own block | DCE (`is_dead`), simple def-use traversal |
21//! | `phi_uses_of` | predecessor block | liveness analysis, mem2reg, SSA destruction |
22
23use llvm_ir::{BlockId, Function, InstrId, InstrKind, ValueRef};
24use std::collections::HashMap;
25
26/// Use-def / def-use information for a single function.
27pub struct UseDefInfo {
28    /// Block in which each instruction is defined.
29    instr_block: HashMap<InstrId, BlockId>,
30    /// All use sites of each SSA value: `(block, instruction)` pairs.
31    ///
32    /// For phi incoming values the block is the **phi's own block**, not the
33    /// predecessor. This is correct for dead-code elimination but not for
34    /// liveness analysis. Use [`phi_uses_of`](Self::phi_uses_of) when
35    /// predecessor-block semantics are required.
36    uses: HashMap<ValueRef, Vec<(BlockId, InstrId)>>,
37    /// Phi-specific use sites with **predecessor-block** semantics.
38    ///
39    /// For a phi `%v = phi [%a, %pred0], [%b, %pred1]`, this map records:
40    /// - `%a` → `[(pred0, phi_iid)]`
41    /// - `%b` → `[(pred1, phi_iid)]`
42    ///
43    /// Only phi incoming values appear here. Non-phi operands are absent.
44    phi_uses: HashMap<ValueRef, Vec<(BlockId, InstrId)>>,
45}
46
47impl UseDefInfo {
48    /// Walk all instructions in `func` and collect definition and use info.
49    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                            // Record at phi's block for DCE / is_dead correctness.
63                            uses.entry(*val).or_default().push((bid, iid));
64                            // Record at predecessor block for liveness / mem2reg.
65                            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    /// The block in which `id` is defined, or `None` if not found.
85    pub fn def_block(&self, id: InstrId) -> Option<BlockId> {
86        self.instr_block.get(&id).copied()
87    }
88
89    /// All use sites of `vref`: `(block, instruction)` pairs.
90    ///
91    /// For phi incoming values the block is the **phi's own block**, not the
92    /// predecessor. Use [`phi_uses_of`](Self::phi_uses_of) when correct
93    /// predecessor-block semantics are needed (liveness, mem2reg).
94    ///
95    /// Returns an empty slice if the value has no uses.
96    pub fn uses_of(&self, vref: ValueRef) -> &[(BlockId, InstrId)] {
97        self.uses.get(&vref).map(Vec::as_slice).unwrap_or(&[])
98    }
99
100    /// Phi-specific use sites of `vref` with **predecessor-block** semantics.
101    ///
102    /// For each `phi [vref, %pred]` instruction that uses `vref`, returns a
103    /// `(pred, phi_instr_id)` pair. This is the correct view for SSA liveness
104    /// analysis (the value must be live-out of `pred`) and for phi elimination
105    /// (copies are inserted at the end of `pred`).
106    ///
107    /// Returns an empty slice if `vref` does not appear in any phi incoming list.
108    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    /// `true` if `vref` has no recorded uses (dead value).
113    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        // sum (InstrId 0) is defined in block 0.
151        assert_eq!(info.def_block(InstrId(0)), Some(BlockId(0)));
152
153        // ret uses sum.
154        let sum_ref = ValueRef::Instruction(InstrId(0));
155        assert_eq!(info.uses_of(sum_ref).len(), 1);
156
157        // sum uses both args.
158        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); // never used
179        b.build_ret(x);
180
181        let func = &module.functions[0];
182        let info = UseDefInfo::compute(func);
183
184        // dead (InstrId 0) has no uses.
185        assert!(info.is_dead(ValueRef::Instruction(InstrId(0))));
186        // x is used by dead (×2) and ret (×1).
187        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        // cond (ArgId 0) is used by the br in block 0.
218        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    // Build a function with a phi node:
224    //
225    //   entry (b0):  br i1 %cond, then, else
226    //   then  (b1):  br merge
227    //   else  (b2):  br merge
228    //   merge (b3):  %v = phi i32 [%a, %then], [%bv, %else]
229    //                ret %v
230    //
231    // %a (ArgId 1) flows from b1; %bv (ArgId 2) flows from b2.
232    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        // %a (ArgId 1) flows into phi from then_bb (BlockId 1).
275        // %bv (ArgId 2) flows into phi from else_bb (BlockId 2).
276        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        // uses_of() must still report phi operands at the phi's own block so
303        // that is_dead() and DCE-style consumers continue to work correctly.
304        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        // %a is used by the phi in merge (BlockId 3).
311        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        // %a is not dead — it appears in a phi.
319        assert!(!info.is_dead(a_ref));
320    }
321
322    #[test]
323    fn non_phi_operands_absent_from_phi_uses() {
324        // Non-phi instructions must not pollute the phi_uses map.
325        let (_ctx, module) = make_add_fn();
326        let func = &module.functions[0];
327        let info = UseDefInfo::compute(func);
328
329        // Neither arg appears in any phi — phi_uses_of must return empty.
330        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}