diff --git a/compiler/rustc_codegen_llvm/src/base.rs b/compiler/rustc_codegen_llvm/src/base.rs index 7d01f6a54995a..6828f95bd4aeb 100644 --- a/compiler/rustc_codegen_llvm/src/base.rs +++ b/compiler/rustc_codegen_llvm/src/base.rs @@ -24,22 +24,27 @@ use crate::metadata; use crate::value::Value; use rustc_codegen_ssa::base::maybe_create_entry_wrapper; +use rustc_codegen_ssa::coverageinfo::map::FunctionCoverage; use rustc_codegen_ssa::mono_item::MonoItemExt; use rustc_codegen_ssa::traits::*; use rustc_codegen_ssa::{ModuleCodegen, ModuleKind}; use rustc_data_structures::small_c_str::SmallCStr; +use rustc_hir::def_id::LOCAL_CRATE; use rustc_middle::dep_graph; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs; use rustc_middle::middle::cstore::EncodedMetadata; use rustc_middle::middle::exported_symbols; use rustc_middle::mir::mono::{Linkage, Visibility}; -use rustc_middle::ty::TyCtxt; +use rustc_middle::ty::{Instance, TyCtxt}; +use rustc_middle::ty::subst::InternalSubsts; use rustc_session::config::{DebugInfo, SanitizerSet}; use rustc_span::symbol::Symbol; use std::ffi::CString; use std::time::Instant; +use tracing::debug; + pub fn write_compressed_metadata<'tcx>( tcx: TyCtxt<'tcx>, metadata: &EncodedMetadata, @@ -109,7 +114,7 @@ pub fn compile_codegen_unit( let cost = time_to_codegen.as_nanos() as u64; fn module_codegen(tcx: TyCtxt<'_>, cgu_name: Symbol) -> ModuleCodegen { - let cgu = tcx.codegen_unit(cgu_name); + let (index, cgu) = tcx.indexed_codegen_unit(cgu_name); let _prof_timer = tcx.prof.generic_activity_with_args( "codegen_module", &[cgu_name.to_string(), cgu.size_estimate().to_string()], @@ -145,7 +150,37 @@ pub fn compile_codegen_unit( // Finalize code coverage by injecting the coverage map. Note, the coverage map will // also be added to the `llvm.used` variable, created next. - if cx.sess().opts.debugging_opts.instrument_coverage { + if let Some(coverage_cx) = cx.coverage_context() { + if index == 0 { + // If this is the first CGU for the current `Crate` (because this should only + // be done once per `Crate`), find any MIR not associated with a `MonoItem`, + // and add it's `mir::Body`s code region to the Coverage Map, with a `Zero` + // Counter. Codegen was not done for these items, so nothing was added to the + // Coverage Map otherwise. + let mut coverage_map = coverage_cx.function_coverage_map.borrow_mut(); + for local_def_id in tcx + .mir_keys(LOCAL_CRATE) + .iter() + .filter(|&def_id| !tcx.is_codegened_item(*def_id)) + { + let def_id = local_def_id.to_def_id(); + if let Some((hash, region)) = tcx.uncovered_function_hash_and_region(def_id) { + let substs = InternalSubsts::identity_for_item(tcx, def_id); + let instance = Instance::new(def_id, substs); + debug!( + "adding a coverage map entry for uncovered function {:?}, \ + mangled name={}, function source hash={} at {:?}", + instance, cx.tcx.symbol_name(instance).to_string(), hash, region, + ); + let mut function_coverage = FunctionCoverage::new(tcx, instance); + function_coverage.set_function_source_hash(*hash); + function_coverage.add_unreachable_region(region.clone()); + coverage_map.insert(instance, function_coverage).expect_none( + "uncovered functions should not already be in the coverage map", + ); + } + } + } cx.coverageinfo_finalize(); } diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index 5974b59d39e42..03ac4f410c692 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -11,6 +11,7 @@ #![feature(extern_types)] #![feature(in_band_lifetimes)] #![feature(nll)] +#![feature(option_expect_none)] #![feature(or_patterns)] #![recursion_limit = "256"] diff --git a/compiler/rustc_codegen_ssa/src/coverageinfo/map.rs b/compiler/rustc_codegen_ssa/src/coverageinfo/map.rs index b0d7953f511c7..1a56ff62f00a9 100644 --- a/compiler/rustc_codegen_ssa/src/coverageinfo/map.rs +++ b/compiler/rustc_codegen_ssa/src/coverageinfo/map.rs @@ -28,6 +28,7 @@ pub struct Expression { /// only whitespace or comments). According to LLVM Code Coverage Mapping documentation, "A count /// for a gap area is only used as the line execution count if there are no other regions on a /// line." +#[derive(Debug)] pub struct FunctionCoverage<'tcx> { instance: Instance<'tcx>, source_hash: u64, @@ -55,6 +56,7 @@ impl<'tcx> FunctionCoverage<'tcx> { /// Sets the function source hash value. If called multiple times for the same function, all /// calls should have the same hash value. pub fn set_function_source_hash(&mut self, source_hash: u64) { + debug!("set_function_source_hash({}), for {:?}", source_hash, self.instance); if self.source_hash == 0 { self.source_hash = source_hash; } else { @@ -64,6 +66,7 @@ impl<'tcx> FunctionCoverage<'tcx> { /// Adds a code region to be counted by an injected counter intrinsic. pub fn add_counter(&mut self, id: CounterValueReference, region: CodeRegion) { + debug!("add_counter({:?}) at {:?}, for {:?}", id, region, self.instance); self.counters[id].replace(region).expect_none("add_counter called with duplicate `id`"); } @@ -90,8 +93,8 @@ impl<'tcx> FunctionCoverage<'tcx> { region: Option, ) { debug!( - "add_counter_expression({:?}, lhs={:?}, op={:?}, rhs={:?} at {:?}", - expression_id, lhs, op, rhs, region + "add_counter_expression({:?}, lhs={:?}, op={:?}, rhs={:?}) at {:?}, for {:?}", + expression_id, lhs, op, rhs, region, self.instance ); let expression_index = self.expression_index(u32::from(expression_id)); self.expressions[expression_index] @@ -101,6 +104,7 @@ impl<'tcx> FunctionCoverage<'tcx> { /// Add a region that will be marked as "unreachable", with a constant "zero counter". pub fn add_unreachable_region(&mut self, region: CodeRegion) { + debug!("add_unreachable_region() at {:?}, for {:?}", region, self.instance); self.unreachable_regions.push(region) } diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 634d50368bd88..b1ec923e1e5a8 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -346,6 +346,14 @@ rustc_queries! { cache_on_disk_if { key.is_local() } } + /// Returns the function source hash and code region for the body of a function that is + /// uncovered, because it was unreachable and not codegen'ed. + query uncovered_function_hash_and_region(key: DefId) -> Option<(u64, mir::coverage::CodeRegion)> { + desc { |tcx| "retrieving uncovered function hash and code region from MIR for `{}`", tcx.def_path_str(key) } + storage(ArenaCacheSelector<'tcx>) + cache_on_disk_if { key.is_local() } + } + /// The `DefId` is the `DefId` of the containing MIR body. Promoteds do not have their own /// `DefId`. This function returns all promoteds in the specified body. The body references /// promoteds by the `DefId` and the `mir::Promoted` index. This is necessary, because @@ -1411,6 +1419,9 @@ rustc_queries! { query codegen_unit(_: Symbol) -> &'tcx CodegenUnit<'tcx> { desc { "codegen_unit" } } + query indexed_codegen_unit(_: Symbol) -> (usize, &'tcx CodegenUnit<'tcx>) { + desc { "indexed_codegen_unit" } + } query unused_generic_params(key: DefId) -> FiniteBitSet { cache_on_disk_if { key.is_local() } desc { diff --git a/compiler/rustc_mir/src/monomorphize/partitioning/mod.rs b/compiler/rustc_mir/src/monomorphize/partitioning/mod.rs index db6d3b2d912d6..662aef9be5377 100644 --- a/compiler/rustc_mir/src/monomorphize/partitioning/mod.rs +++ b/compiler/rustc_mir/src/monomorphize/partitioning/mod.rs @@ -430,4 +430,12 @@ pub fn provide(providers: &mut Providers) { .find(|cgu| cgu.name() == name) .unwrap_or_else(|| panic!("failed to find cgu with name {:?}", name)) }; + + providers.indexed_codegen_unit = |tcx, name| { + let (_, all) = tcx.collect_and_partition_mono_items(LOCAL_CRATE); + all.iter() + .enumerate() + .find(|(_, cgu)| cgu.name() == name) + .unwrap_or_else(|| panic!("failed to find cgu with name {:?}", name)) + }; } diff --git a/compiler/rustc_mir/src/transform/coverage/query.rs b/compiler/rustc_mir/src/transform/coverage/query.rs index e86bb96d29c30..1713f6f4df9e2 100644 --- a/compiler/rustc_mir/src/transform/coverage/query.rs +++ b/compiler/rustc_mir/src/transform/coverage/query.rs @@ -1,14 +1,19 @@ +use super::*; + use rustc_middle::mir::coverage::*; use rustc_middle::mir::visit::Visitor; use rustc_middle::mir::{Coverage, CoverageInfo, Location}; use rustc_middle::ty::query::Providers; use rustc_middle::ty::TyCtxt; +use rustc_span::BytePos; use rustc_span::def_id::DefId; /// The `query` provider for `CoverageInfo`, requested by `codegen_coverage()` (to inject each /// counter) and `FunctionCoverage::new()` (to extract the coverage map metadata from the MIR). pub(crate) fn provide(providers: &mut Providers) { providers.coverageinfo = |tcx, def_id| coverageinfo_from_mir(tcx, def_id); + providers.uncovered_function_hash_and_region = + |tcx, def_id| uncovered_function_hash_and_region(tcx, def_id); } /// The `num_counters` argument to `llvm.instrprof.increment` is the max counter_id + 1, or in @@ -123,3 +128,24 @@ fn coverageinfo_from_mir<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> CoverageInfo coverage_visitor.info } + +fn uncovered_function_hash_and_region<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> Option<(u64, CodeRegion)> { + let mir_body = tcx.optimized_mir(def_id); + if ! mir_body.basic_blocks().iter_enumerated().any(|(_, data)| data.statements.iter().any(|statement| match statement.kind { + StatementKind::Coverage(_) => true, + _ => false, + })) { + return None; + + } + let hir_body = hir_body(tcx, def_id); + let body_span = hir_body.value.span; + let source_map = tcx.sess.source_map(); + let source_file = source_map.lookup_source_file(body_span.lo()); + let file_name = Symbol::intern(&source_file.name.to_string()); + let function_span_to_closing_brace = body_span.with_hi(body_span.hi() - BytePos(1)); + Some(( + hash_mir_source(tcx, hir_body), + make_code_region(file_name, &source_file, function_span_to_closing_brace, body_span), + )) +} diff --git a/src/test/run-make-fulldeps/coverage/async.rs b/src/test/run-make-fulldeps/coverage/async.rs new file mode 100644 index 0000000000000..e93e10e0ae21c --- /dev/null +++ b/src/test/run-make-fulldeps/coverage/async.rs @@ -0,0 +1,67 @@ +#![allow(unused_assignments)] + +// require-rust-edition-2018 + +async fn f() -> u8 { + println!("executed body of async fn f()"); + 1 +} + +async fn foo() -> [bool; 10] { [false; 10] } + +pub async fn g(x: u8) { + match x { + y if f().await == y => (), + _ => (), + } +} + +// #78366: check the reference to the binding is recorded even if the binding is not autorefed + +async fn h(x: usize) { + match x { + y if foo().await[y] => (), + _ => (), + } +} + +async fn i(x: u8) { + match x { + y if f().await == y + 1 => (), + _ => (), + } +} + +fn main() { + let _ = g(10); + let _ = h(9); + let mut future = Box::pin(i(8)); + executor::block_on(future.as_mut()); +} + +mod executor { + use core::{ + future::Future, + pin::Pin, + task::{Context, Poll, RawWaker, RawWakerVTable, Waker}, + }; + + pub fn block_on(mut future: F) -> F::Output { + let mut future = unsafe { Pin::new_unchecked(&mut future) }; + + static VTABLE: RawWakerVTable = RawWakerVTable::new( + |_| unimplemented!("clone"), + |_| unimplemented!("wake"), + |_| unimplemented!("wake_by_ref"), + |_| (), + ); + let waker = unsafe { Waker::from_raw(RawWaker::new(core::ptr::null(), &VTABLE)) }; + let mut context = Context::from_waker(&waker); + + loop { + if let Poll::Ready(val) = future.as_mut().poll(&mut context) { + break val; + } + } + } +} diff --git a/src/test/run-make-fulldeps/coverage/if.rs b/src/test/run-make-fulldeps/coverage/if.rs index 8ad5042ff7baf..5d5d024b0754a 100644 --- a/src/test/run-make-fulldeps/coverage/if.rs +++ b/src/test/run-make-fulldeps/coverage/if.rs @@ -1,6 +1,16 @@ -#![allow(unused_assignments, unused_variables)] +// #![allow(unused_assignments, unused_variables)] + +fn notcalled() { + println!("pub never called"); +} fn main() { + // let unused = || { + // println!("closure never called"); + // }; + + + // Initialize test constants in a way that cannot be determined at compile time, to ensure // rustc and LLVM cannot optimize out statements (or coverage counters) downstream from // dependent conditions.