1 pub use super::ffi::*; 2 3 use rustc_index::{IndexSlice, IndexVec}; 4 use rustc_middle::bug; 5 use rustc_middle::mir::coverage::{ 6 CodeRegion, CounterValueReference, ExpressionOperandId, InjectedExpressionId, 7 InjectedExpressionIndex, MappedExpressionIndex, Op, 8 }; 9 use rustc_middle::ty::Instance; 10 use rustc_middle::ty::TyCtxt; 11 12 #[derive(Clone, Debug, PartialEq)] 13 pub struct Expression { 14 lhs: ExpressionOperandId, 15 op: Op, 16 rhs: ExpressionOperandId, 17 region: Option<CodeRegion>, 18 } 19 20 /// Collects all of the coverage regions associated with (a) injected counters, (b) counter 21 /// expressions (additions or subtraction), and (c) unreachable regions (always counted as zero), 22 /// for a given Function. Counters and counter expressions have non-overlapping `id`s because they 23 /// can both be operands in an expression. This struct also stores the `function_source_hash`, 24 /// computed during instrumentation, and forwarded with counters. 25 /// 26 /// Note, it may be important to understand LLVM's definitions of `unreachable` regions versus "gap 27 /// regions" (or "gap areas"). A gap region is a code region within a counted region (either counter 28 /// or expression), but the line or lines in the gap region are not executable (such as lines with 29 /// only whitespace or comments). According to LLVM Code Coverage Mapping documentation, "A count 30 /// for a gap area is only used as the line execution count if there are no other regions on a 31 /// line." 32 #[derive(Debug)] 33 pub struct FunctionCoverage<'tcx> { 34 instance: Instance<'tcx>, 35 source_hash: u64, 36 is_used: bool, 37 counters: IndexVec<CounterValueReference, Option<CodeRegion>>, 38 expressions: IndexVec<InjectedExpressionIndex, Option<Expression>>, 39 unreachable_regions: Vec<CodeRegion>, 40 } 41 42 impl<'tcx> FunctionCoverage<'tcx> { 43 /// Creates a new set of coverage data for a used (called) function. new(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> Self44 pub fn new(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> Self { 45 Self::create(tcx, instance, true) 46 } 47 48 /// Creates a new set of coverage data for an unused (never called) function. unused(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> Self49 pub fn unused(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> Self { 50 Self::create(tcx, instance, false) 51 } 52 create(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>, is_used: bool) -> Self53 fn create(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>, is_used: bool) -> Self { 54 let coverageinfo = tcx.coverageinfo(instance.def); 55 debug!( 56 "FunctionCoverage::create(instance={:?}) has coverageinfo={:?}. is_used={}", 57 instance, coverageinfo, is_used 58 ); 59 Self { 60 instance, 61 source_hash: 0, // will be set with the first `add_counter()` 62 is_used, 63 counters: IndexVec::from_elem_n(None, coverageinfo.num_counters as usize), 64 expressions: IndexVec::from_elem_n(None, coverageinfo.num_expressions as usize), 65 unreachable_regions: Vec::new(), 66 } 67 } 68 69 /// Returns true for a used (called) function, and false for an unused function. is_used(&self) -> bool70 pub fn is_used(&self) -> bool { 71 self.is_used 72 } 73 74 /// Sets the function source hash value. If called multiple times for the same function, all 75 /// calls should have the same hash value. set_function_source_hash(&mut self, source_hash: u64)76 pub fn set_function_source_hash(&mut self, source_hash: u64) { 77 if self.source_hash == 0 { 78 self.source_hash = source_hash; 79 } else { 80 debug_assert_eq!(source_hash, self.source_hash); 81 } 82 } 83 84 /// Adds a code region to be counted by an injected counter intrinsic. add_counter(&mut self, id: CounterValueReference, region: CodeRegion)85 pub fn add_counter(&mut self, id: CounterValueReference, region: CodeRegion) { 86 if let Some(previous_region) = self.counters[id].replace(region.clone()) { 87 assert_eq!(previous_region, region, "add_counter: code region for id changed"); 88 } 89 } 90 91 /// Both counters and "counter expressions" (or simply, "expressions") can be operands in other 92 /// expressions. Expression IDs start from `u32::MAX` and go down, so the range of expression 93 /// IDs will not overlap with the range of counter IDs. Counters and expressions can be added in 94 /// any order, and expressions can still be assigned contiguous (though descending) IDs, without 95 /// knowing what the last counter ID will be. 96 /// 97 /// When storing the expression data in the `expressions` vector in the `FunctionCoverage` 98 /// struct, its vector index is computed, from the given expression ID, by subtracting from 99 /// `u32::MAX`. 100 /// 101 /// Since the expression operands (`lhs` and `rhs`) can reference either counters or 102 /// expressions, an operand that references an expression also uses its original ID, descending 103 /// from `u32::MAX`. Theses operands are translated only during code generation, after all 104 /// counters and expressions have been added. add_counter_expression( &mut self, expression_id: InjectedExpressionId, lhs: ExpressionOperandId, op: Op, rhs: ExpressionOperandId, region: Option<CodeRegion>, )105 pub fn add_counter_expression( 106 &mut self, 107 expression_id: InjectedExpressionId, 108 lhs: ExpressionOperandId, 109 op: Op, 110 rhs: ExpressionOperandId, 111 region: Option<CodeRegion>, 112 ) { 113 debug!( 114 "add_counter_expression({:?}, lhs={:?}, op={:?}, rhs={:?} at {:?}", 115 expression_id, lhs, op, rhs, region 116 ); 117 let expression_index = self.expression_index(u32::from(expression_id)); 118 debug_assert!( 119 expression_index.as_usize() < self.expressions.len(), 120 "expression_index {} is out of range for expressions.len() = {} 121 for {:?}", 122 expression_index.as_usize(), 123 self.expressions.len(), 124 self, 125 ); 126 if let Some(previous_expression) = self.expressions[expression_index].replace(Expression { 127 lhs, 128 op, 129 rhs, 130 region: region.clone(), 131 }) { 132 assert_eq!( 133 previous_expression, 134 Expression { lhs, op, rhs, region }, 135 "add_counter_expression: expression for id changed" 136 ); 137 } 138 } 139 140 /// Add a region that will be marked as "unreachable", with a constant "zero counter". add_unreachable_region(&mut self, region: CodeRegion)141 pub fn add_unreachable_region(&mut self, region: CodeRegion) { 142 self.unreachable_regions.push(region) 143 } 144 145 /// Return the source hash, generated from the HIR node structure, and used to indicate whether 146 /// or not the source code structure changed between different compilations. source_hash(&self) -> u64147 pub fn source_hash(&self) -> u64 { 148 self.source_hash 149 } 150 151 /// Generate an array of CounterExpressions, and an iterator over all `Counter`s and their 152 /// associated `Regions` (from which the LLVM-specific `CoverageMapGenerator` will create 153 /// `CounterMappingRegion`s. get_expressions_and_counter_regions( &self, ) -> (Vec<CounterExpression>, impl Iterator<Item = (Counter, &CodeRegion)>)154 pub fn get_expressions_and_counter_regions( 155 &self, 156 ) -> (Vec<CounterExpression>, impl Iterator<Item = (Counter, &CodeRegion)>) { 157 assert!( 158 self.source_hash != 0 || !self.is_used, 159 "No counters provided the source_hash for used function: {:?}", 160 self.instance 161 ); 162 163 let counter_regions = self.counter_regions(); 164 let (counter_expressions, expression_regions) = self.expressions_with_regions(); 165 let unreachable_regions = self.unreachable_regions(); 166 167 let counter_regions = 168 counter_regions.chain(expression_regions.into_iter().chain(unreachable_regions)); 169 (counter_expressions, counter_regions) 170 } 171 counter_regions(&self) -> impl Iterator<Item = (Counter, &CodeRegion)>172 fn counter_regions(&self) -> impl Iterator<Item = (Counter, &CodeRegion)> { 173 self.counters.iter_enumerated().filter_map(|(index, entry)| { 174 // Option::map() will return None to filter out missing counters. This may happen 175 // if, for example, a MIR-instrumented counter is removed during an optimization. 176 entry.as_ref().map(|region| (Counter::counter_value_reference(index), region)) 177 }) 178 } 179 expressions_with_regions( &self, ) -> (Vec<CounterExpression>, impl Iterator<Item = (Counter, &CodeRegion)>)180 fn expressions_with_regions( 181 &self, 182 ) -> (Vec<CounterExpression>, impl Iterator<Item = (Counter, &CodeRegion)>) { 183 let mut counter_expressions = Vec::with_capacity(self.expressions.len()); 184 let mut expression_regions = Vec::with_capacity(self.expressions.len()); 185 let mut new_indexes = IndexVec::from_elem_n(None, self.expressions.len()); 186 187 // This closure converts any `Expression` operand (`lhs` or `rhs` of the `Op::Add` or 188 // `Op::Subtract` operation) into its native `llvm::coverage::Counter::CounterKind` type 189 // and value. Operand ID value `0` maps to `CounterKind::Zero`; values in the known range 190 // of injected LLVM counters map to `CounterKind::CounterValueReference` (and the value 191 // matches the injected counter index); and any other value is converted into a 192 // `CounterKind::Expression` with the expression's `new_index`. 193 // 194 // Expressions will be returned from this function in a sequential vector (array) of 195 // `CounterExpression`, so the expression IDs must be mapped from their original, 196 // potentially sparse set of indexes, originally in reverse order from `u32::MAX`. 197 // 198 // An `Expression` as an operand will have already been encountered as an `Expression` with 199 // operands, so its new_index will already have been generated (as a 1-up index value). 200 // (If an `Expression` as an operand does not have a corresponding new_index, it was 201 // probably optimized out, after the expression was injected into the MIR, so it will 202 // get a `CounterKind::Zero` instead.) 203 // 204 // In other words, an `Expression`s at any given index can include other expressions as 205 // operands, but expression operands can only come from the subset of expressions having 206 // `expression_index`s lower than the referencing `Expression`. Therefore, it is 207 // reasonable to look up the new index of an expression operand while the `new_indexes` 208 // vector is only complete up to the current `ExpressionIndex`. 209 let id_to_counter = |new_indexes: &IndexSlice< 210 InjectedExpressionIndex, 211 Option<MappedExpressionIndex>, 212 >, 213 id: ExpressionOperandId| { 214 if id == ExpressionOperandId::ZERO { 215 Some(Counter::zero()) 216 } else if id.index() < self.counters.len() { 217 debug_assert!( 218 id.index() > 0, 219 "ExpressionOperandId indexes for counters are 1-based, but this id={}", 220 id.index() 221 ); 222 // Note: Some codegen-injected Counters may be only referenced by `Expression`s, 223 // and may not have their own `CodeRegion`s, 224 let index = CounterValueReference::from(id.index()); 225 // Note, the conversion to LLVM `Counter` adjusts the index to be zero-based. 226 Some(Counter::counter_value_reference(index)) 227 } else { 228 let index = self.expression_index(u32::from(id)); 229 self.expressions 230 .get(index) 231 .expect("expression id is out of range") 232 .as_ref() 233 // If an expression was optimized out, assume it would have produced a count 234 // of zero. This ensures that expressions dependent on optimized-out 235 // expressions are still valid. 236 .map_or(Some(Counter::zero()), |_| new_indexes[index].map(Counter::expression)) 237 } 238 }; 239 240 for (original_index, expression) in 241 self.expressions.iter_enumerated().filter_map(|(original_index, entry)| { 242 // Option::map() will return None to filter out missing expressions. This may happen 243 // if, for example, a MIR-instrumented expression is removed during an optimization. 244 entry.as_ref().map(|expression| (original_index, expression)) 245 }) 246 { 247 let optional_region = &expression.region; 248 let Expression { lhs, op, rhs, .. } = *expression; 249 250 if let Some(Some((lhs_counter, mut rhs_counter))) = id_to_counter(&new_indexes, lhs) 251 .map(|lhs_counter| { 252 id_to_counter(&new_indexes, rhs).map(|rhs_counter| (lhs_counter, rhs_counter)) 253 }) 254 { 255 if lhs_counter.is_zero() && op.is_subtract() { 256 // The left side of a subtraction was probably optimized out. As an example, 257 // a branch condition might be evaluated as a constant expression, and the 258 // branch could be removed, dropping unused counters in the process. 259 // 260 // Since counters are unsigned, we must assume the result of the expression 261 // can be no more and no less than zero. An expression known to evaluate to zero 262 // does not need to be added to the coverage map. 263 // 264 // Coverage test `loops_branches.rs` includes multiple variations of branches 265 // based on constant conditional (literal `true` or `false`), and demonstrates 266 // that the expected counts are still correct. 267 debug!( 268 "Expression subtracts from zero (assume unreachable): \ 269 original_index={:?}, lhs={:?}, op={:?}, rhs={:?}, region={:?}", 270 original_index, lhs, op, rhs, optional_region, 271 ); 272 rhs_counter = Counter::zero(); 273 } 274 debug_assert!( 275 lhs_counter.is_zero() 276 // Note: with `as usize` the ID _could_ overflow/wrap if `usize = u16` 277 || ((lhs_counter.zero_based_id() as usize) 278 <= usize::max(self.counters.len(), self.expressions.len())), 279 "lhs id={} > both counters.len()={} and expressions.len()={} 280 ({:?} {:?} {:?})", 281 lhs_counter.zero_based_id(), 282 self.counters.len(), 283 self.expressions.len(), 284 lhs_counter, 285 op, 286 rhs_counter, 287 ); 288 289 debug_assert!( 290 rhs_counter.is_zero() 291 // Note: with `as usize` the ID _could_ overflow/wrap if `usize = u16` 292 || ((rhs_counter.zero_based_id() as usize) 293 <= usize::max(self.counters.len(), self.expressions.len())), 294 "rhs id={} > both counters.len()={} and expressions.len()={} 295 ({:?} {:?} {:?})", 296 rhs_counter.zero_based_id(), 297 self.counters.len(), 298 self.expressions.len(), 299 lhs_counter, 300 op, 301 rhs_counter, 302 ); 303 304 // Both operands exist. `Expression` operands exist in `self.expressions` and have 305 // been assigned a `new_index`. 306 let mapped_expression_index = 307 MappedExpressionIndex::from(counter_expressions.len()); 308 let expression = CounterExpression::new( 309 lhs_counter, 310 match op { 311 Op::Add => ExprKind::Add, 312 Op::Subtract => ExprKind::Subtract, 313 }, 314 rhs_counter, 315 ); 316 debug!( 317 "Adding expression {:?} = {:?}, region: {:?}", 318 mapped_expression_index, expression, optional_region 319 ); 320 counter_expressions.push(expression); 321 new_indexes[original_index] = Some(mapped_expression_index); 322 if let Some(region) = optional_region { 323 expression_regions.push((Counter::expression(mapped_expression_index), region)); 324 } 325 } else { 326 bug!( 327 "expression has one or more missing operands \ 328 original_index={:?}, lhs={:?}, op={:?}, rhs={:?}, region={:?}", 329 original_index, 330 lhs, 331 op, 332 rhs, 333 optional_region, 334 ); 335 } 336 } 337 (counter_expressions, expression_regions.into_iter()) 338 } 339 unreachable_regions(&self) -> impl Iterator<Item = (Counter, &CodeRegion)>340 fn unreachable_regions(&self) -> impl Iterator<Item = (Counter, &CodeRegion)> { 341 self.unreachable_regions.iter().map(|region| (Counter::zero(), region)) 342 } 343 expression_index(&self, id_descending_from_max: u32) -> InjectedExpressionIndex344 fn expression_index(&self, id_descending_from_max: u32) -> InjectedExpressionIndex { 345 debug_assert!(id_descending_from_max >= self.counters.len() as u32); 346 InjectedExpressionIndex::from(u32::MAX - id_descending_from_max) 347 } 348 } 349