rustc_codegen_llvm/coverageinfo/
mod.rs

1use std::cell::{OnceCell, RefCell};
2use std::ffi::{CStr, CString};
3
4use rustc_abi::Size;
5use rustc_codegen_ssa::traits::{
6    BuilderMethods, ConstCodegenMethods, CoverageInfoBuilderMethods, MiscCodegenMethods,
7};
8use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
9use rustc_middle::mir::coverage::CoverageKind;
10use rustc_middle::ty::Instance;
11use tracing::{debug, instrument};
12
13use crate::builder::Builder;
14use crate::common::CodegenCx;
15use crate::llvm;
16
17pub(crate) mod ffi;
18mod llvm_cov;
19mod mapgen;
20
21/// Extra per-CGU context/state needed for coverage instrumentation.
22pub(crate) struct CguCoverageContext<'ll, 'tcx> {
23    /// Associates function instances with an LLVM global that holds the
24    /// function's symbol name, as needed by LLVM coverage intrinsics.
25    ///
26    /// Instances in this map are also considered "used" for the purposes of
27    /// emitting covfun records. Every covfun record holds a hash of its
28    /// symbol name, and `llvm-cov` will exit fatally if it can't resolve that
29    /// hash back to an entry in the binary's `__llvm_prf_names` linker section.
30    pub(crate) pgo_func_name_var_map: RefCell<FxIndexMap<Instance<'tcx>, &'ll llvm::Value>>,
31    pub(crate) mcdc_condition_bitmap_map: RefCell<FxHashMap<Instance<'tcx>, Vec<&'ll llvm::Value>>>,
32
33    covfun_section_name: OnceCell<CString>,
34}
35
36impl<'ll, 'tcx> CguCoverageContext<'ll, 'tcx> {
37    pub(crate) fn new() -> Self {
38        Self {
39            pgo_func_name_var_map: Default::default(),
40            mcdc_condition_bitmap_map: Default::default(),
41            covfun_section_name: Default::default(),
42        }
43    }
44
45    /// LLVM use a temp value to record evaluated mcdc test vector of each decision, which is
46    /// called condition bitmap. In order to handle nested decisions, several condition bitmaps can
47    /// be allocated for a function body. These values are named `mcdc.addr.{i}` and are a 32-bit
48    /// integers. They respectively hold the condition bitmaps for decisions with a depth of `i`.
49    fn try_get_mcdc_condition_bitmap(
50        &self,
51        instance: &Instance<'tcx>,
52        decision_depth: u16,
53    ) -> Option<&'ll llvm::Value> {
54        self.mcdc_condition_bitmap_map
55            .borrow()
56            .get(instance)
57            .and_then(|bitmap_map| bitmap_map.get(decision_depth as usize))
58            .copied() // Dereference Option<&&Value> to Option<&Value>
59    }
60
61    /// Returns the list of instances considered "used" in this CGU, as
62    /// inferred from the keys of `pgo_func_name_var_map`.
63    pub(crate) fn instances_used(&self) -> Vec<Instance<'tcx>> {
64        // Collecting into a Vec is way easier than trying to juggle RefCell
65        // projections, and this should only run once per CGU anyway.
66        self.pgo_func_name_var_map.borrow().keys().copied().collect::<Vec<_>>()
67    }
68}
69
70impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> {
71    pub(crate) fn coverageinfo_finalize(&mut self) {
72        mapgen::finalize(self)
73    }
74
75    /// Returns the section name to use when embedding per-function coverage information
76    /// in the object file, according to the target's object file format. LLVM's coverage
77    /// tools use information from this section when producing coverage reports.
78    ///
79    /// Typical values are:
80    /// - `__llvm_covfun` on Linux
81    /// - `__LLVM_COV,__llvm_covfun` on macOS (includes `__LLVM_COV,` segment prefix)
82    /// - `.lcovfun$M` on Windows (includes `$M` sorting suffix)
83    fn covfun_section_name(&self) -> &CStr {
84        self.coverage_cx()
85            .covfun_section_name
86            .get_or_init(|| llvm_cov::covfun_section_name(self.llmod))
87    }
88
89    /// For LLVM codegen, returns a function-specific `Value` for a global
90    /// string, to hold the function name passed to LLVM intrinsic
91    /// `instrprof.increment()`. The `Value` is only created once per instance.
92    /// Multiple invocations with the same instance return the same `Value`.
93    ///
94    /// This has the side-effect of causing coverage codegen to consider this
95    /// function "used", making it eligible to emit an associated covfun record.
96    fn ensure_pgo_func_name_var(&self, instance: Instance<'tcx>) -> &'ll llvm::Value {
97        debug!("getting pgo_func_name_var for instance={:?}", instance);
98        let mut pgo_func_name_var_map = self.coverage_cx().pgo_func_name_var_map.borrow_mut();
99        pgo_func_name_var_map.entry(instance).or_insert_with(|| {
100            let llfn = self.get_fn(instance);
101            let mangled_fn_name: &str = self.tcx.symbol_name(instance).name;
102            llvm_cov::create_pgo_func_name_var(llfn, mangled_fn_name)
103        })
104    }
105}
106
107impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> {
108    fn init_coverage(&mut self, instance: Instance<'tcx>) {
109        let Some(function_coverage_info) =
110            self.tcx.instance_mir(instance.def).function_coverage_info.as_deref()
111        else {
112            return;
113        };
114
115        // If there are no MC/DC bitmaps to set up, return immediately.
116        if function_coverage_info.mcdc_bitmap_bits == 0 {
117            return;
118        }
119
120        let fn_name = self.ensure_pgo_func_name_var(instance);
121        let hash = self.const_u64(function_coverage_info.function_source_hash);
122        let bitmap_bits = self.const_u32(function_coverage_info.mcdc_bitmap_bits as u32);
123        self.mcdc_parameters(fn_name, hash, bitmap_bits);
124
125        // Create pointers named `mcdc.addr.{i}` to stack-allocated condition bitmaps.
126        let mut cond_bitmaps = vec![];
127        for i in 0..function_coverage_info.mcdc_num_condition_bitmaps {
128            // MC/DC intrinsics will perform loads/stores that use the ABI default
129            // alignment for i32, so our variable declaration should match.
130            let align = self.tcx.data_layout.i32_align.abi;
131            let cond_bitmap = self.alloca(Size::from_bytes(4), align);
132            llvm::set_value_name(cond_bitmap, format!("mcdc.addr.{i}").as_bytes());
133            self.store(self.const_i32(0), cond_bitmap, align);
134            cond_bitmaps.push(cond_bitmap);
135        }
136
137        self.coverage_cx().mcdc_condition_bitmap_map.borrow_mut().insert(instance, cond_bitmaps);
138    }
139
140    #[instrument(level = "debug", skip(self))]
141    fn add_coverage(&mut self, instance: Instance<'tcx>, kind: &CoverageKind) {
142        // Our caller should have already taken care of inlining subtleties,
143        // so we can assume that counter/expression IDs in this coverage
144        // statement are meaningful for the given instance.
145        //
146        // (Either the statement was not inlined and directly belongs to this
147        // instance, or it was inlined *from* this instance.)
148
149        let bx = self;
150
151        // Due to LocalCopy instantiation or MIR inlining, coverage statements
152        // can end up in a crate that isn't doing coverage instrumentation.
153        // When that happens, we currently just discard those statements, so
154        // the corresponding code will be undercounted.
155        // FIXME(Zalathar): Find a better solution for mixed-coverage builds.
156        let Some(coverage_cx) = &bx.cx.coverage_cx else { return };
157
158        let Some(function_coverage_info) =
159            bx.tcx.instance_mir(instance.def).function_coverage_info.as_deref()
160        else {
161            debug!("function has a coverage statement but no coverage info");
162            return;
163        };
164        let Some(ids_info) = bx.tcx.coverage_ids_info(instance.def) else {
165            debug!("function has a coverage statement but no IDs info");
166            return;
167        };
168
169        match *kind {
170            CoverageKind::SpanMarker | CoverageKind::BlockMarker { .. } => unreachable!(
171                "marker statement {kind:?} should have been removed by CleanupPostBorrowck"
172            ),
173            CoverageKind::VirtualCounter { bcb }
174                if let Some(&id) = ids_info.phys_counter_for_node.get(&bcb) =>
175            {
176                let fn_name = bx.ensure_pgo_func_name_var(instance);
177                let hash = bx.const_u64(function_coverage_info.function_source_hash);
178                let num_counters = bx.const_u32(ids_info.num_counters);
179                let index = bx.const_u32(id.as_u32());
180                debug!(
181                    "codegen intrinsic instrprof.increment(fn_name={:?}, hash={:?}, num_counters={:?}, index={:?})",
182                    fn_name, hash, num_counters, index,
183                );
184                bx.instrprof_increment(fn_name, hash, num_counters, index);
185            }
186            // If a BCB doesn't have an associated physical counter, there's nothing to codegen.
187            CoverageKind::VirtualCounter { .. } => {}
188            CoverageKind::CondBitmapUpdate { index, decision_depth } => {
189                let cond_bitmap = coverage_cx
190                    .try_get_mcdc_condition_bitmap(&instance, decision_depth)
191                    .expect("mcdc cond bitmap should have been allocated for updating");
192                let cond_index = bx.const_i32(index as i32);
193                bx.mcdc_condbitmap_update(cond_index, cond_bitmap);
194            }
195            CoverageKind::TestVectorBitmapUpdate { bitmap_idx, decision_depth } => {
196                let cond_bitmap =
197                    coverage_cx.try_get_mcdc_condition_bitmap(&instance, decision_depth).expect(
198                        "mcdc cond bitmap should have been allocated for merging \
199                        into the global bitmap",
200                    );
201                assert!(
202                    bitmap_idx as usize <= function_coverage_info.mcdc_bitmap_bits,
203                    "bitmap index of the decision out of range"
204                );
205
206                let fn_name = bx.ensure_pgo_func_name_var(instance);
207                let hash = bx.const_u64(function_coverage_info.function_source_hash);
208                let bitmap_index = bx.const_u32(bitmap_idx);
209                bx.mcdc_tvbitmap_update(fn_name, hash, bitmap_index, cond_bitmap);
210                bx.mcdc_condbitmap_reset(cond_bitmap);
211            }
212        }
213    }
214}