|
1 | 1 | use rustc_data_structures::captures::Captures;
|
2 |
| -use rustc_data_structures::fx::FxIndexSet; |
3 |
| -use rustc_index::bit_set::BitSet; |
4 |
| -use rustc_middle::mir::CoverageIdsInfo; |
5 | 2 | use rustc_middle::mir::coverage::{
|
6 |
| - CounterId, CovTerm, Expression, ExpressionId, FunctionCoverageInfo, Mapping, MappingKind, Op, |
| 3 | + CovTerm, CoverageIdsInfo, Expression, FunctionCoverageInfo, Mapping, MappingKind, Op, |
7 | 4 | SourceRegion,
|
8 | 5 | };
|
9 |
| -use rustc_middle::ty::Instance; |
10 |
| -use tracing::debug; |
11 | 6 |
|
12 | 7 | use crate::coverageinfo::ffi::{Counter, CounterExpression, ExprKind};
|
13 | 8 |
|
14 |
| -/// Holds all of the coverage mapping data associated with a function instance, |
15 |
| -/// collected during traversal of `Coverage` statements in the function's MIR. |
16 |
| -#[derive(Debug)] |
17 |
| -pub(crate) struct FunctionCoverageCollector<'tcx> { |
18 |
| - /// Coverage info that was attached to this function by the instrumentor. |
19 |
| - function_coverage_info: &'tcx FunctionCoverageInfo, |
20 |
| - ids_info: &'tcx CoverageIdsInfo, |
21 |
| - is_used: bool, |
| 9 | +pub(crate) struct FunctionCoverage<'tcx> { |
| 10 | + pub(crate) function_coverage_info: &'tcx FunctionCoverageInfo, |
| 11 | + /// If `None`, the corresponding function is unused. |
| 12 | + ids_info: Option<&'tcx CoverageIdsInfo>, |
22 | 13 | }
|
23 | 14 |
|
24 |
| -impl<'tcx> FunctionCoverageCollector<'tcx> { |
25 |
| - /// Creates a new set of coverage data for a used (called) function. |
26 |
| - pub(crate) fn new( |
27 |
| - instance: Instance<'tcx>, |
28 |
| - function_coverage_info: &'tcx FunctionCoverageInfo, |
29 |
| - ids_info: &'tcx CoverageIdsInfo, |
30 |
| - ) -> Self { |
31 |
| - Self::create(instance, function_coverage_info, ids_info, true) |
32 |
| - } |
33 |
| - |
34 |
| - /// Creates a new set of coverage data for an unused (never called) function. |
35 |
| - pub(crate) fn unused( |
36 |
| - instance: Instance<'tcx>, |
37 |
| - function_coverage_info: &'tcx FunctionCoverageInfo, |
38 |
| - ids_info: &'tcx CoverageIdsInfo, |
39 |
| - ) -> Self { |
40 |
| - Self::create(instance, function_coverage_info, ids_info, false) |
41 |
| - } |
42 |
| - |
43 |
| - fn create( |
44 |
| - instance: Instance<'tcx>, |
| 15 | +impl<'tcx> FunctionCoverage<'tcx> { |
| 16 | + pub(crate) fn new_used( |
45 | 17 | function_coverage_info: &'tcx FunctionCoverageInfo,
|
46 | 18 | ids_info: &'tcx CoverageIdsInfo,
|
47 |
| - is_used: bool, |
48 | 19 | ) -> Self {
|
49 |
| - let num_counters = function_coverage_info.num_counters; |
50 |
| - let num_expressions = function_coverage_info.expressions.len(); |
51 |
| - debug!( |
52 |
| - "FunctionCoverage::create(instance={instance:?}) has \ |
53 |
| - num_counters={num_counters}, num_expressions={num_expressions}, is_used={is_used}" |
54 |
| - ); |
55 |
| - |
56 |
| - Self { function_coverage_info, ids_info, is_used } |
57 |
| - } |
58 |
| - |
59 |
| - /// Identify expressions that will always have a value of zero, and note |
60 |
| - /// their IDs in [`ZeroExpressions`]. Mappings that refer to a zero expression |
61 |
| - /// can instead become mappings to a constant zero value. |
62 |
| - /// |
63 |
| - /// This method mainly exists to preserve the simplifications that were |
64 |
| - /// already being performed by the Rust-side expression renumbering, so that |
65 |
| - /// the resulting coverage mappings don't get worse. |
66 |
| - fn identify_zero_expressions(&self) -> ZeroExpressions { |
67 |
| - // The set of expressions that either were optimized out entirely, or |
68 |
| - // have zero as both of their operands, and will therefore always have |
69 |
| - // a value of zero. Other expressions that refer to these as operands |
70 |
| - // can have those operands replaced with `CovTerm::Zero`. |
71 |
| - let mut zero_expressions = ZeroExpressions::default(); |
72 |
| - |
73 |
| - // Simplify a copy of each expression based on lower-numbered expressions, |
74 |
| - // and then update the set of always-zero expressions if necessary. |
75 |
| - // (By construction, expressions can only refer to other expressions |
76 |
| - // that have lower IDs, so one pass is sufficient.) |
77 |
| - for (id, expression) in self.function_coverage_info.expressions.iter_enumerated() { |
78 |
| - if !self.is_used || !self.ids_info.expressions_seen.contains(id) { |
79 |
| - // If an expression was not seen, it must have been optimized away, |
80 |
| - // so any operand that refers to it can be replaced with zero. |
81 |
| - zero_expressions.insert(id); |
82 |
| - continue; |
83 |
| - } |
84 |
| - |
85 |
| - // We don't need to simplify the actual expression data in the |
86 |
| - // expressions list; we can just simplify a temporary copy and then |
87 |
| - // use that to update the set of always-zero expressions. |
88 |
| - let Expression { mut lhs, op, mut rhs } = *expression; |
89 |
| - |
90 |
| - // If an expression has an operand that is also an expression, the |
91 |
| - // operand's ID must be strictly lower. This is what lets us find |
92 |
| - // all zero expressions in one pass. |
93 |
| - let assert_operand_expression_is_lower = |operand_id: ExpressionId| { |
94 |
| - assert!( |
95 |
| - operand_id < id, |
96 |
| - "Operand {operand_id:?} should be less than {id:?} in {expression:?}", |
97 |
| - ) |
98 |
| - }; |
99 |
| - |
100 |
| - // If an operand refers to a counter or expression that is always |
101 |
| - // zero, then that operand can be replaced with `CovTerm::Zero`. |
102 |
| - let maybe_set_operand_to_zero = |operand: &mut CovTerm| { |
103 |
| - if let CovTerm::Expression(id) = *operand { |
104 |
| - assert_operand_expression_is_lower(id); |
105 |
| - } |
106 |
| - |
107 |
| - if is_zero_term(&self.ids_info.counters_seen, &zero_expressions, *operand) { |
108 |
| - *operand = CovTerm::Zero; |
109 |
| - } |
110 |
| - }; |
111 |
| - maybe_set_operand_to_zero(&mut lhs); |
112 |
| - maybe_set_operand_to_zero(&mut rhs); |
113 |
| - |
114 |
| - // Coverage counter values cannot be negative, so if an expression |
115 |
| - // involves subtraction from zero, assume that its RHS must also be zero. |
116 |
| - // (Do this after simplifications that could set the LHS to zero.) |
117 |
| - if lhs == CovTerm::Zero && op == Op::Subtract { |
118 |
| - rhs = CovTerm::Zero; |
119 |
| - } |
120 |
| - |
121 |
| - // After the above simplifications, if both operands are zero, then |
122 |
| - // we know that this expression is always zero too. |
123 |
| - if lhs == CovTerm::Zero && rhs == CovTerm::Zero { |
124 |
| - zero_expressions.insert(id); |
125 |
| - } |
126 |
| - } |
127 |
| - |
128 |
| - zero_expressions |
| 20 | + Self { function_coverage_info, ids_info: Some(ids_info) } |
129 | 21 | }
|
130 | 22 |
|
131 |
| - pub(crate) fn into_finished(self) -> FunctionCoverage<'tcx> { |
132 |
| - let zero_expressions = self.identify_zero_expressions(); |
133 |
| - let FunctionCoverageCollector { function_coverage_info, ids_info, is_used, .. } = self; |
134 |
| - |
135 |
| - FunctionCoverage { function_coverage_info, ids_info, is_used, zero_expressions } |
| 23 | + pub(crate) fn new_unused(function_coverage_info: &'tcx FunctionCoverageInfo) -> Self { |
| 24 | + Self { function_coverage_info, ids_info: None } |
136 | 25 | }
|
137 |
| -} |
138 |
| - |
139 |
| -pub(crate) struct FunctionCoverage<'tcx> { |
140 |
| - pub(crate) function_coverage_info: &'tcx FunctionCoverageInfo, |
141 |
| - ids_info: &'tcx CoverageIdsInfo, |
142 |
| - is_used: bool, |
143 |
| - |
144 |
| - zero_expressions: ZeroExpressions, |
145 |
| -} |
146 | 26 |
|
147 |
| -impl<'tcx> FunctionCoverage<'tcx> { |
148 | 27 | /// Returns true for a used (called) function, and false for an unused function.
|
149 | 28 | pub(crate) fn is_used(&self) -> bool {
|
150 |
| - self.is_used |
| 29 | + self.ids_info.is_some() |
151 | 30 | }
|
152 | 31 |
|
153 | 32 | /// Return the source hash, generated from the HIR node structure, and used to indicate whether
|
154 | 33 | /// or not the source code structure changed between different compilations.
|
155 | 34 | pub(crate) fn source_hash(&self) -> u64 {
|
156 |
| - if self.is_used { self.function_coverage_info.function_source_hash } else { 0 } |
| 35 | + if self.is_used() { self.function_coverage_info.function_source_hash } else { 0 } |
157 | 36 | }
|
158 | 37 |
|
159 | 38 | /// Convert this function's coverage expression data into a form that can be
|
@@ -196,37 +75,10 @@ impl<'tcx> FunctionCoverage<'tcx> {
|
196 | 75 | }
|
197 | 76 |
|
198 | 77 | fn is_zero_term(&self, term: CovTerm) -> bool {
|
199 |
| - !self.is_used || is_zero_term(&self.ids_info.counters_seen, &self.zero_expressions, term) |
200 |
| - } |
201 |
| -} |
202 |
| - |
203 |
| -/// Set of expression IDs that are known to always evaluate to zero. |
204 |
| -/// Any mapping or expression operand that refers to these expressions can have |
205 |
| -/// that reference replaced with a constant zero value. |
206 |
| -#[derive(Default)] |
207 |
| -struct ZeroExpressions(FxIndexSet<ExpressionId>); |
208 |
| - |
209 |
| -impl ZeroExpressions { |
210 |
| - fn insert(&mut self, id: ExpressionId) { |
211 |
| - self.0.insert(id); |
212 |
| - } |
213 |
| - |
214 |
| - fn contains(&self, id: ExpressionId) -> bool { |
215 |
| - self.0.contains(&id) |
216 |
| - } |
217 |
| -} |
218 |
| - |
219 |
| -/// Returns `true` if the given term is known to have a value of zero, taking |
220 |
| -/// into account knowledge of which counters are unused and which expressions |
221 |
| -/// are always zero. |
222 |
| -fn is_zero_term( |
223 |
| - counters_seen: &BitSet<CounterId>, |
224 |
| - zero_expressions: &ZeroExpressions, |
225 |
| - term: CovTerm, |
226 |
| -) -> bool { |
227 |
| - match term { |
228 |
| - CovTerm::Zero => true, |
229 |
| - CovTerm::Counter(id) => !counters_seen.contains(id), |
230 |
| - CovTerm::Expression(id) => zero_expressions.contains(id), |
| 78 | + match self.ids_info { |
| 79 | + Some(ids_info) => ids_info.is_zero_term(term), |
| 80 | + // This function is unused, so all coverage counters/expressions are zero. |
| 81 | + None => true, |
| 82 | + } |
231 | 83 | }
|
232 | 84 | }
|
0 commit comments