|
1 | 1 | use crate::llvm;
|
2 | 2 |
|
| 3 | +use crate::abi::{Abi, FnAbi}; |
3 | 4 | use crate::builder::Builder;
|
4 | 5 | use crate::common::CodegenCx;
|
5 | 6 |
|
6 | 7 | use libc::c_uint;
|
7 | 8 | use llvm::coverageinfo::CounterMappingRegion;
|
8 | 9 | use rustc_codegen_ssa::coverageinfo::map::{CounterExpression, FunctionCoverage};
|
9 | 10 | use rustc_codegen_ssa::traits::{
|
10 |
| - BaseTypeMethods, CoverageInfoBuilderMethods, CoverageInfoMethods, MiscMethods, StaticMethods, |
| 11 | + BaseTypeMethods, BuilderMethods, ConstMethods, CoverageInfoBuilderMethods, CoverageInfoMethods, |
| 12 | + MiscMethods, StaticMethods, |
11 | 13 | };
|
12 | 14 | use rustc_data_structures::fx::FxHashMap;
|
| 15 | +use rustc_hir as hir; |
| 16 | +use rustc_hir::def_id::DefId; |
13 | 17 | use rustc_llvm::RustString;
|
| 18 | +use rustc_middle::bug; |
14 | 19 | use rustc_middle::mir::coverage::{
|
15 | 20 | CodeRegion, CounterValueReference, ExpressionOperandId, InjectedExpressionId, Op,
|
16 | 21 | };
|
| 22 | +use rustc_middle::ty; |
| 23 | +use rustc_middle::ty::layout::FnAbiExt; |
| 24 | +use rustc_middle::ty::subst::InternalSubsts; |
17 | 25 | use rustc_middle::ty::Instance;
|
18 | 26 |
|
19 | 27 | use std::cell::RefCell;
|
20 | 28 | use std::ffi::CString;
|
21 | 29 |
|
| 30 | +use std::iter; |
22 | 31 | use tracing::debug;
|
23 | 32 |
|
24 | 33 | pub mod mapgen;
|
25 | 34 |
|
| 35 | +const UNUSED_FUNCTION_COUNTER_ID: CounterValueReference = CounterValueReference::START; |
| 36 | + |
26 | 37 | const VAR_ALIGN_BYTES: usize = 8;
|
27 | 38 |
|
28 | 39 | /// A context object for maintaining all state needed by the coverageinfo module.
|
29 |
| -pub struct CrateCoverageContext<'tcx> { |
| 40 | +pub struct CrateCoverageContext<'ll, 'tcx> { |
30 | 41 | // Coverage data for each instrumented function identified by DefId.
|
31 | 42 | pub(crate) function_coverage_map: RefCell<FxHashMap<Instance<'tcx>, FunctionCoverage<'tcx>>>,
|
| 43 | + pub(crate) pgo_func_name_var_map: RefCell<FxHashMap<Instance<'tcx>, &'ll llvm::Value>>, |
32 | 44 | }
|
33 | 45 |
|
34 |
| -impl<'tcx> CrateCoverageContext<'tcx> { |
| 46 | +impl<'ll, 'tcx> CrateCoverageContext<'ll, 'tcx> { |
35 | 47 | pub fn new() -> Self {
|
36 |
| - Self { function_coverage_map: Default::default() } |
| 48 | + Self { |
| 49 | + function_coverage_map: Default::default(), |
| 50 | + pgo_func_name_var_map: Default::default(), |
| 51 | + } |
37 | 52 | }
|
38 | 53 |
|
39 | 54 | pub fn take_function_coverage_map(&self) -> FxHashMap<Instance<'tcx>, FunctionCoverage<'tcx>> {
|
40 | 55 | self.function_coverage_map.replace(FxHashMap::default())
|
41 | 56 | }
|
42 | 57 | }
|
43 | 58 |
|
44 |
| -impl CoverageInfoMethods for CodegenCx<'ll, 'tcx> { |
| 59 | +impl CoverageInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> { |
45 | 60 | fn coverageinfo_finalize(&self) {
|
46 | 61 | mapgen::finalize(self)
|
47 | 62 | }
|
48 |
| -} |
49 | 63 |
|
50 |
| -impl CoverageInfoBuilderMethods<'tcx> for Builder<'a, 'll, 'tcx> { |
51 |
| - /// Calls llvm::createPGOFuncNameVar() with the given function instance's mangled function name. |
52 |
| - /// The LLVM API returns an llvm::GlobalVariable containing the function name, with the specific |
53 |
| - /// variable name and linkage required by LLVM InstrProf source-based coverage instrumentation. |
54 |
| - fn create_pgo_func_name_var(&self, instance: Instance<'tcx>) -> Self::Value { |
55 |
| - let llfn = self.cx.get_fn(instance); |
56 |
| - let mangled_fn_name = CString::new(self.tcx.symbol_name(instance).name) |
57 |
| - .expect("error converting function name to C string"); |
58 |
| - unsafe { llvm::LLVMRustCoverageCreatePGOFuncNameVar(llfn, mangled_fn_name.as_ptr()) } |
| 64 | + fn get_pgo_func_name_var(&self, instance: Instance<'tcx>) -> &'ll llvm::Value { |
| 65 | + if let Some(coverage_context) = self.coverage_context() { |
| 66 | + debug!("getting pgo_func_name_var for instance={:?}", instance); |
| 67 | + let mut pgo_func_name_var_map = coverage_context.pgo_func_name_var_map.borrow_mut(); |
| 68 | + pgo_func_name_var_map |
| 69 | + .entry(instance) |
| 70 | + .or_insert_with(|| create_pgo_func_name_var(self, instance)) |
| 71 | + } else { |
| 72 | + bug!("Could not get the `coverage_context`"); |
| 73 | + } |
59 | 74 | }
|
60 | 75 |
|
| 76 | + /// Functions with MIR-based coverage are normally codegenned _only_ if |
| 77 | + /// called. LLVM coverage tools typically expect every function to be |
| 78 | + /// defined (even if unused), with at least one call to LLVM intrinsic |
| 79 | + /// `instrprof.increment`. |
| 80 | + /// |
| 81 | + /// Codegen a small function that will never be called, with one counter |
| 82 | + /// that will never be incremented. |
| 83 | + /// |
| 84 | + /// For used/called functions, the coverageinfo was already added to the |
| 85 | + /// `function_coverage_map` (keyed by function `Instance`) during codegen. |
| 86 | + /// But in this case, since the unused function was _not_ previously |
| 87 | + /// codegenned, collect the coverage `CodeRegion`s from the MIR and add |
| 88 | + /// them. The first `CodeRegion` is used to add a single counter, with the |
| 89 | + /// same counter ID used in the injected `instrprof.increment` intrinsic |
| 90 | + /// call. Since the function is never called, all other `CodeRegion`s can be |
| 91 | + /// added as `unreachable_region`s. |
| 92 | + fn define_unused_fn(&self, def_id: DefId) { |
| 93 | + let instance = declare_unused_fn(self, &def_id); |
| 94 | + codegen_unused_fn_and_counter(self, instance); |
| 95 | + add_unused_function_coverage(self, instance, def_id); |
| 96 | + } |
| 97 | +} |
| 98 | + |
| 99 | +impl CoverageInfoBuilderMethods<'tcx> for Builder<'a, 'll, 'tcx> { |
61 | 100 | fn set_function_source_hash(
|
62 | 101 | &mut self,
|
63 | 102 | instance: Instance<'tcx>,
|
@@ -145,6 +184,104 @@ impl CoverageInfoBuilderMethods<'tcx> for Builder<'a, 'll, 'tcx> {
|
145 | 184 | }
|
146 | 185 | }
|
147 | 186 |
|
| 187 | +fn declare_unused_fn(cx: &CodegenCx<'ll, 'tcx>, def_id: &DefId) -> Instance<'tcx> { |
| 188 | + let tcx = cx.tcx; |
| 189 | + |
| 190 | + let instance = Instance::new( |
| 191 | + *def_id, |
| 192 | + InternalSubsts::for_item(tcx, *def_id, |param, _| { |
| 193 | + if let ty::GenericParamDefKind::Lifetime = param.kind { |
| 194 | + tcx.lifetimes.re_erased.into() |
| 195 | + } else { |
| 196 | + tcx.mk_param_from_def(param) |
| 197 | + } |
| 198 | + }), |
| 199 | + ); |
| 200 | + |
| 201 | + let llfn = cx.declare_fn( |
| 202 | + &tcx.symbol_name(instance).name, |
| 203 | + &FnAbi::of_fn_ptr( |
| 204 | + cx, |
| 205 | + ty::Binder::dummy(tcx.mk_fn_sig( |
| 206 | + iter::once(tcx.mk_unit()), |
| 207 | + tcx.mk_unit(), |
| 208 | + false, |
| 209 | + hir::Unsafety::Unsafe, |
| 210 | + Abi::Rust, |
| 211 | + )), |
| 212 | + &[], |
| 213 | + ), |
| 214 | + ); |
| 215 | + |
| 216 | + llvm::set_linkage(llfn, llvm::Linkage::WeakAnyLinkage); |
| 217 | + llvm::set_visibility(llfn, llvm::Visibility::Hidden); |
| 218 | + |
| 219 | + assert!(cx.instances.borrow_mut().insert(instance, llfn).is_none()); |
| 220 | + |
| 221 | + instance |
| 222 | +} |
| 223 | + |
| 224 | +fn codegen_unused_fn_and_counter(cx: &CodegenCx<'ll, 'tcx>, instance: Instance<'tcx>) { |
| 225 | + let llfn = cx.get_fn(instance); |
| 226 | + let mut bx = Builder::new_block(cx, llfn, "unused_function"); |
| 227 | + let fn_name = bx.get_pgo_func_name_var(instance); |
| 228 | + let hash = bx.const_u64(0); |
| 229 | + let num_counters = bx.const_u32(1); |
| 230 | + let index = bx.const_u32(u32::from(UNUSED_FUNCTION_COUNTER_ID)); |
| 231 | + debug!( |
| 232 | + "codegen intrinsic instrprof.increment(fn_name={:?}, hash={:?}, num_counters={:?}, |
| 233 | + index={:?}) for unused function: {:?}", |
| 234 | + fn_name, hash, num_counters, index, instance |
| 235 | + ); |
| 236 | + bx.instrprof_increment(fn_name, hash, num_counters, index); |
| 237 | + bx.ret_void(); |
| 238 | +} |
| 239 | + |
| 240 | +fn add_unused_function_coverage( |
| 241 | + cx: &CodegenCx<'ll, 'tcx>, |
| 242 | + instance: Instance<'tcx>, |
| 243 | + def_id: DefId, |
| 244 | +) { |
| 245 | + let tcx = cx.tcx; |
| 246 | + |
| 247 | + let mut function_coverage = FunctionCoverage::unused(tcx, instance); |
| 248 | + for (index, &code_region) in tcx.covered_code_regions(def_id).iter().enumerate() { |
| 249 | + if index == 0 { |
| 250 | + // Insert at least one real counter so the LLVM CoverageMappingReader will find expected |
| 251 | + // definitions. |
| 252 | + function_coverage.add_counter(UNUSED_FUNCTION_COUNTER_ID, code_region.clone()); |
| 253 | + } |
| 254 | + // Add a Zero Counter for every code region. |
| 255 | + // |
| 256 | + // Even though the first coverage region already has an actual Counter, `llvm-cov` will not |
| 257 | + // always report it. Re-adding an unreachable region (zero counter) for the same region |
| 258 | + // seems to help produce the expected coverage. |
| 259 | + function_coverage.add_unreachable_region(code_region.clone()); |
| 260 | + } |
| 261 | + |
| 262 | + if let Some(coverage_context) = cx.coverage_context() { |
| 263 | + coverage_context.function_coverage_map.borrow_mut().insert(instance, function_coverage); |
| 264 | + } else { |
| 265 | + bug!("Could not get the `coverage_context`"); |
| 266 | + } |
| 267 | +} |
| 268 | + |
| 269 | +/// Calls llvm::createPGOFuncNameVar() with the given function instance's |
| 270 | +/// mangled function name. The LLVM API returns an llvm::GlobalVariable |
| 271 | +/// containing the function name, with the specific variable name and linkage |
| 272 | +/// required by LLVM InstrProf source-based coverage instrumentation. Use |
| 273 | +/// `bx.get_pgo_func_name_var()` to ensure the variable is only created once per |
| 274 | +/// `Instance`. |
| 275 | +fn create_pgo_func_name_var( |
| 276 | + cx: &CodegenCx<'ll, 'tcx>, |
| 277 | + instance: Instance<'tcx>, |
| 278 | +) -> &'ll llvm::Value { |
| 279 | + let mangled_fn_name = CString::new(cx.tcx.symbol_name(instance).name) |
| 280 | + .expect("error converting function name to C string"); |
| 281 | + let llfn = cx.get_fn(instance); |
| 282 | + unsafe { llvm::LLVMRustCoverageCreatePGOFuncNameVar(llfn, mangled_fn_name.as_ptr()) } |
| 283 | +} |
| 284 | + |
148 | 285 | pub(crate) fn write_filenames_section_to_buffer<'a>(
|
149 | 286 | filenames: impl IntoIterator<Item = &'a CString>,
|
150 | 287 | buffer: &RustString,
|
@@ -177,6 +314,7 @@ pub(crate) fn write_mapping_to_buffer(
|
177 | 314 | );
|
178 | 315 | }
|
179 | 316 | }
|
| 317 | + |
180 | 318 | pub(crate) fn hash_str(strval: &str) -> u64 {
|
181 | 319 | let strval = CString::new(strval).expect("null error converting hashable str to C string");
|
182 | 320 | unsafe { llvm::LLVMRustCoverageHashCString(strval.as_ptr()) }
|
|
0 commit comments