diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs index 30f125ca3beae..768914d1ecba0 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs @@ -61,7 +61,7 @@ pub fn finalize<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>) { // Encode coverage mappings and generate function records let mut function_data = Vec::new(); - for (instance, function_coverage) in function_coverage_map { + for (instance, mut function_coverage) in function_coverage_map { debug!("Generate function coverage for {}, {:?}", cx.codegen_unit.name(), instance); let mangled_function_name = tcx.symbol_name(instance).to_string(); let source_hash = function_coverage.source_hash(); diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs index afc2bdbfd52ec..a69d19b10cc1d 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs @@ -244,15 +244,9 @@ fn add_unused_function_coverage( ) { let tcx = cx.tcx; - let mut function_coverage = FunctionCoverage::unused(tcx, instance); - for (index, &code_region) in tcx.covered_code_regions(def_id).iter().enumerate() { - if index == 0 { - // Insert at least one real counter so the LLVM CoverageMappingReader will find expected - // definitions. - function_coverage.add_counter(UNUSED_FUNCTION_COUNTER_ID, code_region.clone()); - } else { - function_coverage.add_unreachable_region(code_region.clone()); - } + let mut function_coverage = FunctionCoverage::unused(instance); + for &code_region in tcx.covered_code_regions(def_id).iter() { + function_coverage.add_unreachable_region(code_region.clone()); } if let Some(coverage_context) = cx.coverage_context() { diff --git a/compiler/rustc_codegen_ssa/src/coverageinfo/map.rs b/compiler/rustc_codegen_ssa/src/coverageinfo/map.rs index 4458fd686788f..737eb95c31996 100644 --- a/compiler/rustc_codegen_ssa/src/coverageinfo/map.rs +++ b/compiler/rustc_codegen_ssa/src/coverageinfo/map.rs @@ -28,6 +28,7 @@ pub struct Expression { /// only whitespace or comments). According to LLVM Code Coverage Mapping documentation, "A count /// for a gap area is only used as the line execution count if there are no other regions on a /// line." +#[derive(Debug)] pub struct FunctionCoverage<'tcx> { instance: Instance<'tcx>, source_hash: u64, @@ -40,30 +41,34 @@ pub struct FunctionCoverage<'tcx> { impl<'tcx> FunctionCoverage<'tcx> { /// Creates a new set of coverage data for a used (called) function. pub fn new(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> Self { - Self::create(tcx, instance, true) - } - - /// Creates a new set of coverage data for an unused (never called) function. - pub fn unused(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> Self { - Self::create(tcx, instance, false) - } - - fn create(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>, is_used: bool) -> Self { let coverageinfo = tcx.coverageinfo(instance.def_id()); debug!( - "FunctionCoverage::new(instance={:?}) has coverageinfo={:?}. is_used={}", - instance, coverageinfo, is_used + "FunctionCoverage::new(instance={:?}) has coverageinfo={:?}", + instance, coverageinfo ); Self { instance, source_hash: 0, // will be set with the first `add_counter()` - is_used, + is_used: true, counters: IndexVec::from_elem_n(None, coverageinfo.num_counters as usize), expressions: IndexVec::from_elem_n(None, coverageinfo.num_expressions as usize), unreachable_regions: Vec::new(), } } + /// Creates a new set of coverage data for an unused (never called) function. + pub fn unused(instance: Instance<'tcx>) -> Self { + debug!("FunctionCoverage::unused(instance={:?})", instance); + Self { + instance, + source_hash: 0, // will be set with the first `add_counter()` + is_used: false, + counters: IndexVec::from_elem_n(None, CounterValueReference::START.as_usize()), + expressions: IndexVec::new(), + unreachable_regions: Vec::new(), + } + } + /// Returns true for a used (called) function, and false for an unused function. pub fn is_used(&self) -> bool { self.is_used @@ -142,13 +147,33 @@ impl<'tcx> FunctionCoverage<'tcx> { /// associated `Regions` (from which the LLVM-specific `CoverageMapGenerator` will create /// `CounterMappingRegion`s. pub fn get_expressions_and_counter_regions<'a>( - &'a self, + &'a mut self, ) -> (Vec, impl Iterator) { - assert!( - self.source_hash != 0 || !self.is_used, - "No counters provided the source_hash for used function: {:?}", - self.instance - ); + if self.source_hash == 0 { + // Either this `FunctionCoverage` is _not_ used (created via + // `unused()`, or the function had all statements removed, including + // all `Counter`s. + // + // Dead blocks (removed during MIR transform, typically because + // const evaluation can determine that the block will never be + // called) are removed before codegen, but any coverage statements + // are replaced by `Coverage::unreachable` statements at the top of + // the MIR CFG. + debug!("No counters provided the source_hash for used function:\n{:?}", self); + assert_eq!( + self.expressions.len(), + 0, + "Expressions (from MIR) without counters: {:?}", + self.instance + ); + // If there are `Unreachable` code regions, but no `Counter`s, we + // need to add at least one Counter, because LLVM seems to require + // it. + if let Some(unreachable_code_region) = self.unreachable_regions.pop() { + // Replace any one of the `Unreachable`s with a counter. + self.counters.push(Some(unreachable_code_region)); + } + } let counter_regions = self.counter_regions(); let (counter_expressions, expression_regions) = self.expressions_with_regions();