|
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); |
| 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(|| self.create_pgo_func_name_var(instance)) |
| 71 | + } else { |
| 72 | + bug!("Could not get the `coverage_context`"); |
| 73 | + } |
| 74 | + } |
| 75 | + |
| 76 | + /// Calls llvm::createPGOFuncNameVar() with the given function instance's |
| 77 | + /// mangled function name. The LLVM API returns an llvm::GlobalVariable |
| 78 | + /// containing the function name, with the specific variable name and |
| 79 | + /// linkage required by LLVM InstrProf source-based coverage |
| 80 | + /// instrumentation. Use `bx.get_pgo_func_name_var()` to ensure the variable |
| 81 | + /// is only created once per `Instance`. |
| 82 | + fn create_pgo_func_name_var(&self, instance: Instance<'tcx>) -> &'ll llvm::Value { |
56 | 83 | let mangled_fn_name = CString::new(self.tcx.symbol_name(instance).name)
|
57 | 84 | .expect("error converting function name to C string");
|
| 85 | + let llfn = self.get_fn(instance); |
58 | 86 | unsafe { llvm::LLVMRustCoverageCreatePGOFuncNameVar(llfn, mangled_fn_name.as_ptr()) }
|
59 | 87 | }
|
60 | 88 |
|
| 89 | + fn define_unused_fn(&self, def_id: DefId) { |
| 90 | + let instance = declare_unused_fn(self, &def_id); |
| 91 | + codegen_unused_fn_and_counter(self, instance); |
| 92 | + add_function_coverage(self, instance, def_id); |
| 93 | + } |
| 94 | +} |
| 95 | + |
| 96 | +impl CoverageInfoBuilderMethods<'tcx> for Builder<'a, 'll, 'tcx> { |
61 | 97 | fn set_function_source_hash(
|
62 | 98 | &mut self,
|
63 | 99 | instance: Instance<'tcx>,
|
@@ -145,6 +181,86 @@ impl CoverageInfoBuilderMethods<'tcx> for Builder<'a, 'll, 'tcx> {
|
145 | 181 | }
|
146 | 182 | }
|
147 | 183 |
|
| 184 | +fn declare_unused_fn(cx: &CodegenCx<'ll, 'tcx>, def_id: &DefId) -> Instance<'tcx> { |
| 185 | + let tcx = cx.tcx; |
| 186 | + |
| 187 | + let instance = Instance::new( |
| 188 | + *def_id, |
| 189 | + InternalSubsts::for_item(tcx, *def_id, |param, _| { |
| 190 | + if let ty::GenericParamDefKind::Lifetime = param.kind { |
| 191 | + tcx.lifetimes.re_erased.into() |
| 192 | + } else { |
| 193 | + tcx.mk_param_from_def(param) |
| 194 | + } |
| 195 | + }), |
| 196 | + ); |
| 197 | + |
| 198 | + let llfn = cx.declare_fn( |
| 199 | + &tcx.symbol_name(instance).name, |
| 200 | + &FnAbi::of_fn_ptr( |
| 201 | + cx, |
| 202 | + ty::Binder::dummy(tcx.mk_fn_sig( |
| 203 | + iter::once(tcx.mk_unit()), |
| 204 | + tcx.mk_unit(), |
| 205 | + false, |
| 206 | + hir::Unsafety::Unsafe, |
| 207 | + Abi::Rust, |
| 208 | + )), |
| 209 | + &[], |
| 210 | + ), |
| 211 | + ); |
| 212 | + |
| 213 | + unsafe { |
| 214 | + llvm::LLVMRustSetLinkage(llfn, llvm::Linkage::ExternalLinkage); |
| 215 | + llvm::LLVMRustSetVisibility(llfn, llvm::Visibility::Hidden); |
| 216 | + } |
| 217 | + |
| 218 | + cx.instances.borrow_mut().insert(instance, llfn); |
| 219 | + |
| 220 | + instance |
| 221 | +} |
| 222 | + |
| 223 | +fn codegen_unused_fn_and_counter(cx: &CodegenCx<'ll, 'tcx>, instance: Instance<'tcx>) { |
| 224 | + let llfn = cx.get_fn(instance); |
| 225 | + let mut bx = Builder::new_block(cx, llfn, "unused_function"); |
| 226 | + let fn_name = bx.get_pgo_func_name_var(instance); |
| 227 | + let hash = bx.const_u64(0); |
| 228 | + let num_counters = bx.const_u32(1); |
| 229 | + let index = bx.const_u32(u32::from(UNUSED_FUNCTION_COUNTER_ID)); |
| 230 | + debug!( |
| 231 | + "codegen intrinsic instrprof.increment(fn_name={:?}, hash={:?}, num_counters={:?}, |
| 232 | + index={:?}) for unused function: {:?}", |
| 233 | + fn_name, hash, num_counters, index, instance |
| 234 | + ); |
| 235 | + bx.instrprof_increment(fn_name, hash, num_counters, index); |
| 236 | + bx.ret_void(); |
| 237 | +} |
| 238 | + |
| 239 | +fn add_function_coverage(cx: &CodegenCx<'ll, 'tcx>, instance: Instance<'tcx>, def_id: DefId) { |
| 240 | + let tcx = cx.tcx; |
| 241 | + |
| 242 | + let mut function_coverage = FunctionCoverage::unused(tcx, instance); |
| 243 | + for (index, &code_region) in tcx.covered_code_regions(def_id).iter().enumerate() { |
| 244 | + if index == 0 { |
| 245 | + // Insert at least one real counter so the LLVM CoverageMappingReader will find expected |
| 246 | + // definitions. |
| 247 | + function_coverage.add_counter(UNUSED_FUNCTION_COUNTER_ID, code_region.clone()); |
| 248 | + } |
| 249 | + // Add a Zero Counter for every code region. |
| 250 | + // |
| 251 | + // Even though the first coverage region already has an actual Counter, `llvm-cov` will not |
| 252 | + // always report it. Re-adding an unreachable region (zero counter) for the same region |
| 253 | + // seems to help produce the expected coverage. |
| 254 | + function_coverage.add_unreachable_region(code_region.clone()); |
| 255 | + } |
| 256 | + |
| 257 | + if let Some(coverage_context) = cx.coverage_context() { |
| 258 | + coverage_context.function_coverage_map.borrow_mut().insert(instance, function_coverage); |
| 259 | + } else { |
| 260 | + bug!("Could not get the `coverage_context`"); |
| 261 | + } |
| 262 | +} |
| 263 | + |
148 | 264 | pub(crate) fn write_filenames_section_to_buffer<'a>(
|
149 | 265 | filenames: impl IntoIterator<Item = &'a CString>,
|
150 | 266 | buffer: &RustString,
|
@@ -177,6 +293,7 @@ pub(crate) fn write_mapping_to_buffer(
|
177 | 293 | );
|
178 | 294 | }
|
179 | 295 | }
|
| 296 | + |
180 | 297 | pub(crate) fn hash_str(strval: &str) -> u64 {
|
181 | 298 | let strval = CString::new(strval).expect("null error converting hashable str to C string");
|
182 | 299 | unsafe { llvm::LLVMRustCoverageHashCString(strval.as_ptr()) }
|
|
0 commit comments