about summary refs log tree commit diff
path: root/compiler/rustc_codegen_llvm/src/coverageinfo
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_codegen_llvm/src/coverageinfo')
-rw-r--r--compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs155
1 files changed, 148 insertions, 7 deletions
diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs
index 85aaa7e8893..ced3f21f744 100644
--- a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs
+++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs
@@ -3,11 +3,14 @@ use crate::coverageinfo;
 use crate::llvm;
 
 use llvm::coverageinfo::CounterMappingRegion;
-use rustc_codegen_ssa::coverageinfo::map::{Counter, CounterExpression};
+use rustc_codegen_ssa::coverageinfo::map::{Counter, CounterExpression, FunctionCoverage};
 use rustc_codegen_ssa::traits::ConstMethods;
-use rustc_data_structures::fx::FxIndexSet;
+use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet};
+use rustc_hir::def_id::{DefId, DefIdSet, LOCAL_CRATE};
 use rustc_llvm::RustString;
 use rustc_middle::mir::coverage::CodeRegion;
+use rustc_middle::ty::{Instance, TyCtxt};
+use rustc_span::Symbol;
 
 use std::ffi::CString;
 
@@ -26,14 +29,17 @@ use tracing::debug;
 /// undocumented details in Clang's implementation (that may or may not be important) were also
 /// replicated for Rust's Coverage Map.
 pub fn finalize<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>) {
+    let tcx = cx.tcx;
     // Ensure LLVM supports Coverage Map Version 4 (encoded as a zero-based value: 3).
     // If not, the LLVM Version must be less than 11.
     let version = coverageinfo::mapping_version();
     if version != 3 {
-        cx.tcx.sess.fatal("rustc option `-Z instrument-coverage` requires LLVM 11 or higher.");
+        tcx.sess.fatal("rustc option `-Z instrument-coverage` requires LLVM 11 or higher.");
     }
 
-    let function_coverage_map = match cx.coverage_context() {
+    debug!("Generating coverage map for CodegenUnit: `{}`", cx.codegen_unit.name());
+
+    let mut function_coverage_map = match cx.coverage_context() {
         Some(ctx) => ctx.take_function_coverage_map(),
         None => return,
     };
@@ -42,14 +48,15 @@ pub fn finalize<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>) {
         return;
     }
 
+    add_unreachable_coverage(tcx, &mut function_coverage_map);
+
     let mut mapgen = CoverageMapGenerator::new();
 
     // Encode coverage mappings and generate function records
     let mut function_data = Vec::new();
     for (instance, function_coverage) in function_coverage_map {
-        debug!("Generate coverage map for: {:?}", instance);
-
-        let mangled_function_name = cx.tcx.symbol_name(instance).to_string();
+        debug!("Generate function coverage for {}, {:?}", cx.codegen_unit.name(), instance);
+        let mangled_function_name = tcx.symbol_name(instance).to_string();
         let function_source_hash = function_coverage.source_hash();
         let (expressions, counter_regions) =
             function_coverage.get_expressions_and_counter_regions();
@@ -228,3 +235,137 @@ fn save_function_record(
     let is_used = true;
     coverageinfo::save_func_record_to_mod(cx, func_name_hash, func_record_val, is_used);
 }
+
+/// When finalizing the coverage map, `FunctionCoverage` only has the `CodeRegion`s and counters for
+/// the functions that went through codegen; such as public functions and "used" functions
+/// (functions referenced by other "used" or public items). Any other functions considered unused,
+/// or "Unreachable" were still parsed and processed through the MIR stage.
+///
+/// We can find the unreachable functions by the set different of all MIR `DefId`s (`tcx` query
+/// `mir_keys`) minus the codegenned `DefId`s (`tcx` query `collect_and_partition_mono_items`).
+///
+/// *HOWEVER* the codegenned `DefId`s are partitioned across multiple `CodegenUnit`s (CGUs), and
+/// this function is processing a `function_coverage_map` for the functions (`Instance`/`DefId`)
+/// allocated to only one of those CGUs. We must NOT inject any "Unreachable" functions's
+/// `CodeRegion`s more than once, so we have to pick which CGU's `function_coverage_map` to add
+/// each "Unreachable" function to.
+///
+/// Some constraints:
+///
+/// 1. The file name of an "Unreachable" function must match the file name of the existing
+///    codegenned (covered) function to which the unreachable code regions will be added.
+/// 2. The function to which the unreachable code regions will be added must not be a genaric
+///    function (must not have type parameters) because the coverage tools will get confused
+///    if the codegenned function has more than one instantiation and additional `CodeRegion`s
+///    attached to only one of those instantiations.
+fn add_unreachable_coverage<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    function_coverage_map: &mut FxHashMap<Instance<'tcx>, FunctionCoverage<'tcx>>,
+) {
+    // Note: If the crate *only* defines generic functions, there are no codegenerated non-generic
+    // functions to add any unreachable code to. In this case, the unreachable code regions will
+    // have no coverage, instead of having coverage with zero executions.
+    //
+    // This is probably still an improvement over Clang, which does not generate any coverage
+    // for uninstantiated template functions.
+
+    let has_non_generic_def_ids =
+        function_coverage_map.keys().any(|instance| instance.def.attrs(tcx).len() == 0);
+
+    if !has_non_generic_def_ids {
+        // There are no non-generic functions to add unreachable `CodeRegion`s to
+        return;
+    }
+
+    let all_def_ids: DefIdSet =
+        tcx.mir_keys(LOCAL_CRATE).iter().map(|local_def_id| local_def_id.to_def_id()).collect();
+
+    let (codegenned_def_ids, _) = tcx.collect_and_partition_mono_items(LOCAL_CRATE);
+
+    let mut unreachable_def_ids_by_file: FxHashMap<Symbol, Vec<DefId>> = FxHashMap::default();
+    for &non_codegenned_def_id in all_def_ids.difference(codegenned_def_ids) {
+        // Make sure the non-codegenned (unreachable) function has a file_name
+        if let Some(non_codegenned_file_name) = tcx.covered_file_name(non_codegenned_def_id) {
+            let def_ids = unreachable_def_ids_by_file
+                .entry(*non_codegenned_file_name)
+                .or_insert_with(|| Vec::new());
+            def_ids.push(non_codegenned_def_id);
+        }
+    }
+
+    if unreachable_def_ids_by_file.is_empty() {
+        // There are no unreachable functions with file names to add (in any CGU)
+        return;
+    }
+
+    // Since there may be multiple `CodegenUnit`s, some codegenned_def_ids may be codegenned in a
+    // different CGU, and will be added to the function_coverage_map for each CGU. Determine which
+    // function_coverage_map has the responsibility for publishing unreachable coverage
+    // based on file name:
+    //
+    // For each covered file name, sort ONLY the non-generic codegenned_def_ids, and if
+    // covered_def_ids.contains(the first def_id) for a given file_name, add the unreachable code
+    // region in this function_coverage_map. Otherwise, ignore it and assume another CGU's
+    // function_coverage_map will be adding it (because it will be first for one, and only one,
+    // of them).
+    let mut sorted_codegenned_def_ids: Vec<DefId> =
+        codegenned_def_ids.iter().map(|def_id| *def_id).collect();
+    sorted_codegenned_def_ids.sort_unstable();
+
+    let mut first_covered_def_id_by_file: FxHashMap<Symbol, DefId> = FxHashMap::default();
+    for &def_id in sorted_codegenned_def_ids.iter() {
+        // Only consider non-generic functions, to potentially add unreachable code regions
+        if tcx.generics_of(def_id).count() == 0 {
+            if let Some(covered_file_name) = tcx.covered_file_name(def_id) {
+                // Only add files known to have unreachable functions
+                if unreachable_def_ids_by_file.contains_key(covered_file_name) {
+                    first_covered_def_id_by_file.entry(*covered_file_name).or_insert(def_id);
+                }
+            }
+        }
+    }
+
+    // Get the set of def_ids with coverage regions, known by *this* CoverageContext.
+    let cgu_covered_def_ids: DefIdSet =
+        function_coverage_map.keys().map(|instance| instance.def.def_id()).collect();
+
+    let mut cgu_covered_files: FxHashSet<Symbol> = first_covered_def_id_by_file
+        .iter()
+        .filter_map(
+            |(&file_name, def_id)| {
+                if cgu_covered_def_ids.contains(def_id) { Some(file_name) } else { None }
+            },
+        )
+        .collect();
+
+    // Find the first covered, non-generic function (instance) for each cgu_covered_file. Take the
+    // unreachable code regions for that file, and add them to the function.
+    //
+    // There are three `for` loops here, but (a) the lists have already been reduced to the minimum
+    // required values, the lists are further reduced (by `remove()` calls) when elements are no
+    // longer needed, and there are several opportunities to branch out of loops early.
+    for (instance, function_coverage) in function_coverage_map.iter_mut() {
+        if instance.def.attrs(tcx).len() > 0 {
+            continue;
+        }
+        // The covered function is not generic...
+        let covered_def_id = instance.def.def_id();
+        if let Some(covered_file_name) = tcx.covered_file_name(covered_def_id) {
+            if !cgu_covered_files.remove(&covered_file_name) {
+                continue;
+            }
+            // The covered function's file is one of the files with unreachable code regions, so
+            // all of the unreachable code regions for this file will be added to this function.
+            for def_id in
+                unreachable_def_ids_by_file.remove(&covered_file_name).into_iter().flatten()
+            {
+                for &region in tcx.covered_code_regions(def_id) {
+                    function_coverage.add_unreachable_region(region.clone());
+                }
+            }
+            if cgu_covered_files.is_empty() {
+                break;
+            }
+        }
+    }
+}