about summary refs log tree commit diff
diff options
context:
space:
mode:
authorZalathar <Zalathar@users.noreply.github.com>2024-03-22 13:23:04 +1100
committerZalathar <Zalathar@users.noreply.github.com>2024-03-26 11:46:04 +1100
commit54116c8cae58f1d46f231fbeac5630a81b994a4c (patch)
tree6998d9daea1582c741df1f45958e3347a78043c6
parente3f66b24933356e5bda6a7161ab92706cd015dea (diff)
downloadrust-54116c8cae58f1d46f231fbeac5630a81b994a4c.tar.gz
rust-54116c8cae58f1d46f231fbeac5630a81b994a4c.zip
coverage: Detect functions that have lost all their coverage statements
If a function was instrumented for coverage, but all of its coverage statements
have been removed by later MIR transforms, it should be treated as "unused"
even if the compiler generates an unreachable stub for it.
-rw-r--r--compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs22
1 files changed, 18 insertions, 4 deletions
diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs
index 95cd4cca4d2..0fbc624389b 100644
--- a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs
+++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs
@@ -353,10 +353,11 @@ fn add_unused_functions(cx: &CodegenCx<'_, '_>) {
 
         // To be eligible for "unused function" mappings, a definition must:
         // - Be function-like
-        // - Not participate directly in codegen
+        // - Not participate directly in codegen (or have lost all its coverage statements)
         // - Not have any coverage statements inlined into codegenned functions
         tcx.def_kind(def_id).is_fn_like()
-            && !usage.all_mono_items.contains(&def_id)
+            && (!usage.all_mono_items.contains(&def_id)
+                || usage.missing_own_coverage.contains(&def_id))
             && !usage.used_via_inlining.contains(&def_id)
     };
 
@@ -379,6 +380,7 @@ fn add_unused_functions(cx: &CodegenCx<'_, '_>) {
 struct UsageSets<'tcx> {
     all_mono_items: &'tcx DefIdSet,
     used_via_inlining: FxHashSet<DefId>,
+    missing_own_coverage: FxHashSet<DefId>,
 }
 
 /// Prepare sets of definitions that are relevant to deciding whether something
@@ -406,8 +408,13 @@ fn prepare_usage_sets<'tcx>(tcx: TyCtxt<'tcx>) -> UsageSets<'tcx> {
 
     // Functions whose coverage statments were found inlined into other functions.
     let mut used_via_inlining = FxHashSet::default();
+    // Functions that were instrumented, but had all of their coverage statements
+    // removed by later MIR transforms (e.g. UnreachablePropagation).
+    let mut missing_own_coverage = FxHashSet::default();
+
+    for (def_id, body) in def_and_mir_for_all_mono_fns {
+        let mut saw_own_coverage = false;
 
-    for (_def_id, body) in def_and_mir_for_all_mono_fns {
         // Inspect every coverage statement in the function's MIR.
         for stmt in body
             .basic_blocks
@@ -418,11 +425,18 @@ fn prepare_usage_sets<'tcx>(tcx: TyCtxt<'tcx>) -> UsageSets<'tcx> {
             if let Some(inlined) = stmt.source_info.scope.inlined_instance(&body.source_scopes) {
                 // This coverage statement was inlined from another function.
                 used_via_inlining.insert(inlined.def_id());
+            } else {
+                // Non-inlined coverage statements belong to the enclosing function.
+                saw_own_coverage = true;
             }
         }
+
+        if !saw_own_coverage && body.function_coverage_info.is_some() {
+            missing_own_coverage.insert(def_id);
+        }
     }
 
-    UsageSets { all_mono_items, used_via_inlining }
+    UsageSets { all_mono_items, used_via_inlining, missing_own_coverage }
 }
 
 fn add_unused_function_coverage<'tcx>(