about summary refs log tree commit diff
path: root/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs')
-rw-r--r--compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs157
1 files changed, 47 insertions, 110 deletions
diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs
index 484a4d00c13..c6b2a623ea6 100644
--- a/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs
+++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs
@@ -1,24 +1,20 @@
-use std::cell::RefCell;
+use std::cell::{OnceCell, RefCell};
 use std::ffi::{CStr, CString};
 
 use libc::c_uint;
 use rustc_codegen_ssa::traits::{
-    BaseTypeCodegenMethods, BuilderMethods, ConstCodegenMethods, CoverageInfoBuilderMethods,
-    MiscCodegenMethods, StaticCodegenMethods,
+    BuilderMethods, ConstCodegenMethods, CoverageInfoBuilderMethods, MiscCodegenMethods,
 };
 use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
 use rustc_llvm::RustString;
-use rustc_middle::bug;
 use rustc_middle::mir::coverage::CoverageKind;
 use rustc_middle::ty::Instance;
 use rustc_middle::ty::layout::HasTyCtxt;
-use rustc_target::abi::{Align, Size};
-use rustc_target::spec::HasTargetSpec;
+use rustc_target::abi::Size;
 use tracing::{debug, instrument};
 
 use crate::builder::Builder;
 use crate::common::CodegenCx;
-use crate::coverageinfo::ffi::{CounterExpression, CounterMappingRegion};
 use crate::coverageinfo::map_data::FunctionCoverageCollector;
 use crate::llvm;
 
@@ -33,6 +29,8 @@ pub(crate) struct CrateCoverageContext<'ll, 'tcx> {
         RefCell<FxIndexMap<Instance<'tcx>, FunctionCoverageCollector<'tcx>>>,
     pub(crate) pgo_func_name_var_map: RefCell<FxHashMap<Instance<'tcx>, &'ll llvm::Value>>,
     pub(crate) mcdc_condition_bitmap_map: RefCell<FxHashMap<Instance<'tcx>, Vec<&'ll llvm::Value>>>,
+
+    covfun_section_name: OnceCell<CString>,
 }
 
 impl<'ll, 'tcx> CrateCoverageContext<'ll, 'tcx> {
@@ -41,6 +39,7 @@ impl<'ll, 'tcx> CrateCoverageContext<'ll, 'tcx> {
             function_coverage_map: Default::default(),
             pgo_func_name_var_map: Default::default(),
             mcdc_condition_bitmap_map: Default::default(),
+            covfun_section_name: Default::default(),
         }
     }
 
@@ -67,27 +66,38 @@ impl<'ll, 'tcx> CrateCoverageContext<'ll, 'tcx> {
     }
 }
 
-// These methods used to be part of trait `CoverageInfoMethods`, which no longer
-// exists after most coverage code was moved out of SSA.
 impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> {
     pub(crate) fn coverageinfo_finalize(&self) {
         mapgen::finalize(self)
     }
 
+    /// Returns the section name to use when embedding per-function coverage information
+    /// in the object file, according to the target's object file format. LLVM's coverage
+    /// tools use information from this section when producing coverage reports.
+    ///
+    /// Typical values are:
+    /// - `__llvm_covfun` on Linux
+    /// - `__LLVM_COV,__llvm_covfun` on macOS (includes `__LLVM_COV,` segment prefix)
+    /// - `.lcovfun$M` on Windows (includes `$M` sorting suffix)
+    fn covfun_section_name(&self) -> &CStr {
+        self.coverage_cx().covfun_section_name.get_or_init(|| {
+            CString::new(llvm::build_byte_buffer(|s| unsafe {
+                llvm::LLVMRustCoverageWriteFuncSectionNameToString(self.llmod, s);
+            }))
+            .expect("covfun section name should not contain NUL")
+        })
+    }
+
     /// For LLVM codegen, returns a function-specific `Value` for a global
     /// string, to hold the function name passed to LLVM intrinsic
     /// `instrprof.increment()`. The `Value` is only created once per instance.
     /// Multiple invocations with the same instance return the same `Value`.
     fn get_pgo_func_name_var(&self, instance: Instance<'tcx>) -> &'ll llvm::Value {
-        if let Some(coverage_context) = self.coverage_context() {
-            debug!("getting pgo_func_name_var for instance={:?}", instance);
-            let mut pgo_func_name_var_map = coverage_context.pgo_func_name_var_map.borrow_mut();
-            pgo_func_name_var_map
-                .entry(instance)
-                .or_insert_with(|| create_pgo_func_name_var(self, instance))
-        } else {
-            bug!("Could not get the `coverage_context`");
-        }
+        debug!("getting pgo_func_name_var for instance={:?}", instance);
+        let mut pgo_func_name_var_map = self.coverage_cx().pgo_func_name_var_map.borrow_mut();
+        pgo_func_name_var_map
+            .entry(instance)
+            .or_insert_with(|| create_pgo_func_name_var(self, instance))
     }
 }
 
@@ -121,11 +131,7 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> {
             cond_bitmaps.push(cond_bitmap);
         }
 
-        self.coverage_context()
-            .expect("always present when coverage is enabled")
-            .mcdc_condition_bitmap_map
-            .borrow_mut()
-            .insert(instance, cond_bitmaps);
+        self.coverage_cx().mcdc_condition_bitmap_map.borrow_mut().insert(instance, cond_bitmaps);
     }
 
     #[instrument(level = "debug", skip(self))]
@@ -146,8 +152,7 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> {
             return;
         };
 
-        let Some(coverage_context) = bx.coverage_context() else { return };
-        let mut coverage_map = coverage_context.function_coverage_map.borrow_mut();
+        let mut coverage_map = bx.coverage_cx().function_coverage_map.borrow_mut();
         let func_coverage = coverage_map
             .entry(instance)
             .or_insert_with(|| FunctionCoverageCollector::new(instance, function_coverage_info));
@@ -189,7 +194,8 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> {
             }
             CoverageKind::CondBitmapUpdate { index, decision_depth } => {
                 drop(coverage_map);
-                let cond_bitmap = coverage_context
+                let cond_bitmap = bx
+                    .coverage_cx()
                     .try_get_mcdc_condition_bitmap(&instance, decision_depth)
                     .expect("mcdc cond bitmap should have been allocated for updating");
                 let cond_index = bx.const_i32(index as i32);
@@ -197,7 +203,7 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> {
             }
             CoverageKind::TestVectorBitmapUpdate { bitmap_idx, decision_depth } => {
                 drop(coverage_map);
-                let cond_bitmap = coverage_context
+                let cond_bitmap = bx.coverage_cx()
                                     .try_get_mcdc_condition_bitmap(&instance, decision_depth)
                                     .expect("mcdc cond bitmap should have been allocated for merging into the global bitmap");
                 assert!(
@@ -209,6 +215,7 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> {
                 let hash = bx.const_u64(function_coverage_info.function_source_hash);
                 let bitmap_index = bx.const_u32(bitmap_idx);
                 bx.mcdc_tvbitmap_update(fn_name, hash, bitmap_index, cond_bitmap);
+                bx.mcdc_condbitmap_reset(cond_bitmap);
             }
         }
     }
@@ -257,8 +264,11 @@ pub(crate) fn write_filenames_section_to_buffer<'a>(
 
 pub(crate) fn write_mapping_to_buffer(
     virtual_file_mapping: Vec<u32>,
-    expressions: Vec<CounterExpression>,
-    mapping_regions: Vec<CounterMappingRegion>,
+    expressions: Vec<ffi::CounterExpression>,
+    code_regions: &[ffi::CodeRegion],
+    branch_regions: &[ffi::BranchRegion],
+    mcdc_branch_regions: &[ffi::MCDCBranchRegion],
+    mcdc_decision_regions: &[ffi::MCDCDecisionRegion],
     buffer: &RustString,
 ) {
     unsafe {
@@ -267,8 +277,14 @@ pub(crate) fn write_mapping_to_buffer(
             virtual_file_mapping.len() as c_uint,
             expressions.as_ptr(),
             expressions.len() as c_uint,
-            mapping_regions.as_ptr(),
-            mapping_regions.len() as c_uint,
+            code_regions.as_ptr(),
+            code_regions.len() as c_uint,
+            branch_regions.as_ptr(),
+            branch_regions.len() as c_uint,
+            mcdc_branch_regions.as_ptr(),
+            mcdc_branch_regions.len() as c_uint,
+            mcdc_decision_regions.as_ptr(),
+            mcdc_decision_regions.len() as c_uint,
             buffer,
         );
     }
@@ -281,82 +297,3 @@ pub(crate) fn hash_bytes(bytes: &[u8]) -> u64 {
 pub(crate) fn mapping_version() -> u32 {
     unsafe { llvm::LLVMRustCoverageMappingVersion() }
 }
-
-pub(crate) fn save_cov_data_to_mod<'ll, 'tcx>(
-    cx: &CodegenCx<'ll, 'tcx>,
-    cov_data_val: &'ll llvm::Value,
-) {
-    let covmap_var_name = CString::new(llvm::build_byte_buffer(|s| unsafe {
-        llvm::LLVMRustCoverageWriteMappingVarNameToString(s);
-    }))
-    .unwrap();
-    debug!("covmap var name: {:?}", covmap_var_name);
-
-    let covmap_section_name = CString::new(llvm::build_byte_buffer(|s| unsafe {
-        llvm::LLVMRustCoverageWriteMapSectionNameToString(cx.llmod, s);
-    }))
-    .expect("covmap section name should not contain NUL");
-    debug!("covmap section name: {:?}", covmap_section_name);
-
-    let llglobal = llvm::add_global(cx.llmod, cx.val_ty(cov_data_val), &covmap_var_name);
-    llvm::set_initializer(llglobal, cov_data_val);
-    llvm::set_global_constant(llglobal, true);
-    llvm::set_linkage(llglobal, llvm::Linkage::PrivateLinkage);
-    llvm::set_section(llglobal, &covmap_section_name);
-    // LLVM's coverage mapping format specifies 8-byte alignment for items in this section.
-    llvm::set_alignment(llglobal, Align::EIGHT);
-    cx.add_used_global(llglobal);
-}
-
-pub(crate) fn save_func_record_to_mod<'ll, 'tcx>(
-    cx: &CodegenCx<'ll, 'tcx>,
-    covfun_section_name: &CStr,
-    func_name_hash: u64,
-    func_record_val: &'ll llvm::Value,
-    is_used: bool,
-) {
-    // Assign a name to the function record. This is used to merge duplicates.
-    //
-    // In LLVM, a "translation unit" (effectively, a `Crate` in Rust) can describe functions that
-    // are included-but-not-used. If (or when) Rust generates functions that are
-    // included-but-not-used, note that a dummy description for a function included-but-not-used
-    // in a Crate can be replaced by full description provided by a different Crate. The two kinds
-    // of descriptions play distinct roles in LLVM IR; therefore, assign them different names (by
-    // appending "u" to the end of the function record var name, to prevent `linkonce_odr` merging.
-    let func_record_var_name =
-        CString::new(format!("__covrec_{:X}{}", func_name_hash, if is_used { "u" } else { "" }))
-            .unwrap();
-    debug!("function record var name: {:?}", func_record_var_name);
-    debug!("function record section name: {:?}", covfun_section_name);
-
-    let llglobal = llvm::add_global(cx.llmod, cx.val_ty(func_record_val), &func_record_var_name);
-    llvm::set_initializer(llglobal, func_record_val);
-    llvm::set_global_constant(llglobal, true);
-    llvm::set_linkage(llglobal, llvm::Linkage::LinkOnceODRLinkage);
-    llvm::set_visibility(llglobal, llvm::Visibility::Hidden);
-    llvm::set_section(llglobal, covfun_section_name);
-    // LLVM's coverage mapping format specifies 8-byte alignment for items in this section.
-    llvm::set_alignment(llglobal, Align::EIGHT);
-    if cx.target_spec().supports_comdat() {
-        llvm::set_comdat(cx.llmod, llglobal, &func_record_var_name);
-    }
-    cx.add_used_global(llglobal);
-}
-
-/// Returns the section name string to pass through to the linker when embedding
-/// per-function coverage information in the object file, according to the target
-/// platform's object file format.
-///
-/// LLVM's coverage tools read coverage mapping details from this section when
-/// producing coverage reports.
-///
-/// Typical values are:
-/// - `__llvm_covfun` on Linux
-/// - `__LLVM_COV,__llvm_covfun` on macOS (includes `__LLVM_COV,` segment prefix)
-/// - `.lcovfun$M` on Windows (includes `$M` sorting suffix)
-pub(crate) fn covfun_section_name(cx: &CodegenCx<'_, '_>) -> CString {
-    CString::new(llvm::build_byte_buffer(|s| unsafe {
-        llvm::LLVMRustCoverageWriteFuncSectionNameToString(cx.llmod, s);
-    }))
-    .expect("covfun section name should not contain NUL")
-}