rustc_mir_transform/coverage/
counters.rs
1use std::cmp::Ordering;
2
3use either::Either;
4use itertools::Itertools;
5use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
6use rustc_data_structures::graph::DirectedGraph;
7use rustc_index::IndexVec;
8use rustc_index::bit_set::DenseBitSet;
9use rustc_middle::mir::coverage::{CounterId, CovTerm, Expression, ExpressionId, Op};
10
11use crate::coverage::counters::balanced_flow::BalancedFlowGraph;
12use crate::coverage::counters::node_flow::{
13 CounterTerm, NodeCounters, NodeFlowData, node_flow_data_for_balanced_graph,
14};
15use crate::coverage::graph::{BasicCoverageBlock, CoverageGraph};
16
17mod balanced_flow;
18pub(crate) mod node_flow;
19mod union_find;
20
21pub(crate) struct BcbCountersData {
23 pub(crate) node_flow_data: NodeFlowData<BasicCoverageBlock>,
24 pub(crate) priority_list: Vec<BasicCoverageBlock>,
25}
26
27pub(crate) fn prepare_bcb_counters_data(graph: &CoverageGraph) -> BcbCountersData {
31 let balanced_graph = BalancedFlowGraph::for_graph(graph, |n| !graph[n].is_out_summable);
33 let node_flow_data = node_flow_data_for_balanced_graph(&balanced_graph);
34
35 let priority_list = make_node_flow_priority_list(graph, balanced_graph);
40
41 BcbCountersData { node_flow_data, priority_list }
42}
43
44fn make_node_flow_priority_list(
47 graph: &CoverageGraph,
48 balanced_graph: BalancedFlowGraph<&CoverageGraph>,
49) -> Vec<BasicCoverageBlock> {
50 let is_reloop_node = IndexVec::from_fn_n(
54 |node| match graph.successors[node].as_slice() {
55 &[succ] => graph.dominates(succ, node),
56 _ => false,
57 },
58 graph.num_nodes(),
59 );
60
61 let mut nodes = balanced_graph.iter_nodes().rev().collect::<Vec<_>>();
62 assert_eq!(nodes[0], balanced_graph.sink);
64 nodes[1..].sort_by(|&a, &b| {
67 Ordering::Equal
69 .then_with(|| Ord::cmp(&graph[a].is_out_summable, &graph[b].is_out_summable))
71 .then_with(|| Ord::cmp(&is_reloop_node[a], &is_reloop_node[b]).reverse())
73 .then_with(|| graph.cmp_in_dominator_order(a, b).reverse())
75 });
76 nodes
77}
78
79pub(crate) fn transcribe_counters(
81 old: &NodeCounters<BasicCoverageBlock>,
82 bcb_needs_counter: &DenseBitSet<BasicCoverageBlock>,
83 bcbs_seen: &DenseBitSet<BasicCoverageBlock>,
84) -> CoverageCounters {
85 let mut new = CoverageCounters::with_num_bcbs(bcb_needs_counter.domain_size());
86
87 for bcb in bcb_needs_counter.iter() {
88 if !bcbs_seen.contains(bcb) {
89 new.set_node_counter(bcb, CovTerm::Zero);
91 continue;
92 }
93
94 let (mut pos, mut neg): (Vec<_>, Vec<_>) = old.counter_terms[bcb]
98 .iter()
99 .filter(|term| bcbs_seen.contains(term.node))
102 .partition_map(|&CounterTerm { node, op }| match op {
103 Op::Add => Either::Left(node),
104 Op::Subtract => Either::Right(node),
105 });
106
107 pos.sort();
112 neg.sort();
113
114 let mut new_counters_for_sites = |sites: Vec<BasicCoverageBlock>| {
115 sites.into_iter().map(|node| new.ensure_phys_counter(node)).collect::<Vec<_>>()
116 };
117 let pos = new_counters_for_sites(pos);
118 let neg = new_counters_for_sites(neg);
119
120 let pos_counter = new.make_sum(&pos).unwrap_or(CovTerm::Zero);
121 let new_counter = new.make_subtracted_sum(pos_counter, &neg);
122 new.set_node_counter(bcb, new_counter);
123 }
124
125 new
126}
127
128pub(super) struct CoverageCounters {
131 pub(crate) phys_counter_for_node: FxIndexMap<BasicCoverageBlock, CounterId>,
134 pub(crate) next_counter_id: CounterId,
135
136 pub(crate) node_counters: IndexVec<BasicCoverageBlock, Option<CovTerm>>,
138
139 pub(crate) expressions: IndexVec<ExpressionId, Expression>,
142 expressions_memo: FxHashMap<Expression, CovTerm>,
145}
146
147impl CoverageCounters {
148 fn with_num_bcbs(num_bcbs: usize) -> Self {
149 Self {
150 phys_counter_for_node: FxIndexMap::default(),
151 next_counter_id: CounterId::ZERO,
152 node_counters: IndexVec::from_elem_n(None, num_bcbs),
153 expressions: IndexVec::new(),
154 expressions_memo: FxHashMap::default(),
155 }
156 }
157
158 fn ensure_phys_counter(&mut self, bcb: BasicCoverageBlock) -> CovTerm {
160 let id = *self.phys_counter_for_node.entry(bcb).or_insert_with(|| {
161 let id = self.next_counter_id;
162 self.next_counter_id = id + 1;
163 id
164 });
165 CovTerm::Counter(id)
166 }
167
168 fn make_expression(&mut self, lhs: CovTerm, op: Op, rhs: CovTerm) -> CovTerm {
169 let new_expr = Expression { lhs, op, rhs };
170 *self.expressions_memo.entry(new_expr.clone()).or_insert_with(|| {
171 let id = self.expressions.push(new_expr);
172 CovTerm::Expression(id)
173 })
174 }
175
176 fn make_sum(&mut self, counters: &[CovTerm]) -> Option<CovTerm> {
180 counters
181 .iter()
182 .copied()
183 .reduce(|accum, counter| self.make_expression(accum, Op::Add, counter))
184 }
185
186 fn make_subtracted_sum(&mut self, lhs: CovTerm, rhs: &[CovTerm]) -> CovTerm {
188 let Some(rhs_sum) = self.make_sum(rhs) else { return lhs };
189 self.make_expression(lhs, Op::Subtract, rhs_sum)
190 }
191
192 fn set_node_counter(&mut self, bcb: BasicCoverageBlock, counter: CovTerm) -> CovTerm {
193 let existing = self.node_counters[bcb].replace(counter);
194 assert!(
195 existing.is_none(),
196 "node {bcb:?} already has a counter: {existing:?} => {counter:?}"
197 );
198 counter
199 }
200}