rucc_opt/uses.rs
1//! Who reads what, counted by occurrence.
2//!
3//! Two passes want the same question answered and neither of them wants to be the place the
4//! answer is defined. Dead code elimination asks whether anything reads a value, and width
5//! narrowing asks whether exactly one thing does, which is the difference between a rewrite that
6//! replaces an instruction and a rewrite that adds a second one beside it.
7//!
8//! This is a count and not an analysis. It is built by walking the function, it goes stale the
9//! moment anything is rewritten, and the pass that rewrites is the one that keeps it in step.
10//! When there is an analysis manager it will hold a real use list with the instruction on the
11//! other end of each use, and this will be the thing that list replaces.
12
13use std::collections::HashMap;
14
15use rucc_ir::{Block, Func, Inst, Value};
16
17/// How many times each value is used, indexed by [`Value::index`].
18///
19/// By position rather than by instruction, because `x + x` uses `x` twice and a reader who wanted
20/// to know whether removing one use leaves any needs both of them counted.
21#[must_use]
22pub fn count(func: &Func) -> Vec<u32> {
23 let mut uses = vec![0u32; func.counts().values];
24 for block in func.blocks().collect::<Vec<Block>>() {
25 for inst in func.insts(block).collect::<Vec<Inst>>() {
26 operands(func, inst, |value| uses[value.index()] += 1);
27 }
28 }
29 uses
30}
31
32/// Every value this instruction reads, with a repeat for each time it reads it.
33///
34/// The arguments, and the arguments of the blocks it branches to. That is the whole of what an
35/// instruction can use, and it is the same pair the verifier walks, so a use this misses is a use
36/// the verifier would already be looking at from the other side.
37pub fn operands(func: &Func, inst: Inst, mut each: impl FnMut(Value)) {
38 for &value in &func[func[inst].args] {
39 each(value);
40 }
41 for call in func.successors(inst) {
42 for &value in &func[call.args] {
43 each(value);
44 }
45 }
46}
47
48/// Points every reader of a value at another value, for every pair in the map.
49///
50/// The arguments of each instruction and the arguments of the blocks it branches to, which is the
51/// whole of what an instruction can read and is the same pair [`operands`] walks. It is here
52/// rather than in the pass that wanted it first because two passes want it: the peephole points a
53/// reader at what a rule said the value is, and control flow simplification points a reader of a
54/// block parameter at the argument the one branch to that block passed.
55///
56/// One walk over the function for the whole map rather than one walk per pair. A pass that
57/// rewrote a hundred values would otherwise walk the function a hundred times, and the map is
58/// what makes the cost of the walk independent of how much the pass did.
59pub fn substitute(func: &mut Func, forward: &HashMap<Value, Value>) {
60 let with = |value: Value| chase(forward, value);
61 for block in func.blocks().collect::<Vec<Block>>() {
62 for inst in func.insts(block).collect::<Vec<Inst>>() {
63 let args = func[inst].args;
64 func.rewrite(args, with);
65 for call in func.successors(inst).collect::<Vec<_>>() {
66 func.rewrite(call.args, with);
67 }
68 }
69 }
70}
71
72/// Where a redirection ends up, following the ones already in the map.
73///
74/// A chain forms whenever one rewrite feeds another, `x + 0` read by `y * 1` in the peephole, and
75/// a block parameter bound to an argument that is itself a parameter of a block merged a moment
76/// earlier. Following it is what makes the second rewrite worth as much as the first.
77///
78/// The caller is what keeps this from running forever, by only ever pointing a value at one that
79/// was already defined before it. Both callers do: a rule points a result at one of its own
80/// operands, and a merge points a block's parameter at an argument passed by the block above it.
81#[must_use]
82pub fn chase(forward: &HashMap<Value, Value>, value: Value) -> Value {
83 let mut value = value;
84 while let Some(&next) = forward.get(&value) {
85 value = next;
86 }
87 value
88}