@@ -13,7 +13,6 @@ use self::spans::CoverageSpans;
1313
1414use crate :: MirPass ;
1515
16- use rustc_data_structures:: sync:: Lrc ;
1716use rustc_middle:: hir;
1817use rustc_middle:: middle:: codegen_fn_attrs:: CodegenFnAttrFlags ;
1918use rustc_middle:: mir:: coverage:: * ;
@@ -22,9 +21,9 @@ use rustc_middle::mir::{
2221 TerminatorKind ,
2322} ;
2423use rustc_middle:: ty:: TyCtxt ;
25- use rustc_span:: def_id:: DefId ;
24+ use rustc_span:: def_id:: LocalDefId ;
2625use rustc_span:: source_map:: SourceMap ;
27- use rustc_span:: { ExpnKind , SourceFile , Span , Symbol } ;
26+ use rustc_span:: { ExpnKind , Span , Symbol } ;
2827
2928/// Inserts `StatementKind::Coverage` statements that either instrument the binary with injected
3029/// counters, via intrinsic `llvm.instrprof.increment`, and/or inject metadata used during codegen
@@ -39,31 +38,19 @@ impl<'tcx> MirPass<'tcx> for InstrumentCoverage {
3938 fn run_pass ( & self , tcx : TyCtxt < ' tcx > , mir_body : & mut mir:: Body < ' tcx > ) {
4039 let mir_source = mir_body. source ;
4140
42- // If the InstrumentCoverage pass is called on promoted MIRs, skip them.
43- // See: https://github.com/rust-lang/rust/pull/73011#discussion_r438317601
44- if mir_source. promoted . is_some ( ) {
45- trace ! (
46- "InstrumentCoverage skipped for {:?} (already promoted for Miri evaluation)" ,
47- mir_source. def_id( )
48- ) ;
49- return ;
50- }
41+ // This pass runs after MIR promotion, but before promoted MIR starts to
42+ // be transformed, so it should never see promoted MIR.
43+ assert ! ( mir_source. promoted. is_none( ) ) ;
44+
45+ let def_id = mir_source. def_id ( ) . expect_local ( ) ;
5146
52- let is_fn_like =
53- tcx. hir_node_by_def_id ( mir_source. def_id ( ) . expect_local ( ) ) . fn_kind ( ) . is_some ( ) ;
54-
55- // Only instrument functions, methods, and closures (not constants since they are evaluated
56- // at compile time by Miri).
57- // FIXME(#73156): Handle source code coverage in const eval, but note, if and when const
58- // expressions get coverage spans, we will probably have to "carve out" space for const
59- // expressions from coverage spans in enclosing MIR's, like we do for closures. (That might
60- // be tricky if const expressions have no corresponding statements in the enclosing MIR.
61- // Closures are carved out by their initial `Assign` statement.)
62- if !is_fn_like {
63- trace ! ( "InstrumentCoverage skipped for {:?} (not an fn-like)" , mir_source. def_id( ) ) ;
47+ if !is_eligible_for_coverage ( tcx, def_id) {
48+ trace ! ( "InstrumentCoverage skipped for {def_id:?} (not eligible)" ) ;
6449 return ;
6550 }
6651
52+ // An otherwise-eligible function is still skipped if its start block
53+ // is known to be unreachable.
6754 match mir_body. basic_blocks [ mir:: START_BLOCK ] . terminator ( ) . kind {
6855 TerminatorKind :: Unreachable => {
6956 trace ! ( "InstrumentCoverage skipped for unreachable `START_BLOCK`" ) ;
@@ -72,21 +59,15 @@ impl<'tcx> MirPass<'tcx> for InstrumentCoverage {
7259 _ => { }
7360 }
7461
75- let codegen_fn_attrs = tcx. codegen_fn_attrs ( mir_source. def_id ( ) ) ;
76- if codegen_fn_attrs. flags . contains ( CodegenFnAttrFlags :: NO_COVERAGE ) {
77- return ;
78- }
79-
80- trace ! ( "InstrumentCoverage starting for {:?}" , mir_source. def_id( ) ) ;
62+ trace ! ( "InstrumentCoverage starting for {def_id:?}" ) ;
8163 Instrumentor :: new ( tcx, mir_body) . inject_counters ( ) ;
82- trace ! ( "InstrumentCoverage done for {:?}" , mir_source . def_id ( ) ) ;
64+ trace ! ( "InstrumentCoverage done for {def_id :?}" ) ;
8365 }
8466}
8567
8668struct Instrumentor < ' a , ' tcx > {
8769 tcx : TyCtxt < ' tcx > ,
8870 mir_body : & ' a mut mir:: Body < ' tcx > ,
89- source_file : Lrc < SourceFile > ,
9071 fn_sig_span : Span ,
9172 body_span : Span ,
9273 function_source_hash : u64 ,
@@ -96,37 +77,17 @@ struct Instrumentor<'a, 'tcx> {
9677
9778impl < ' a , ' tcx > Instrumentor < ' a , ' tcx > {
9879 fn new ( tcx : TyCtxt < ' tcx > , mir_body : & ' a mut mir:: Body < ' tcx > ) -> Self {
99- let source_map = tcx. sess . source_map ( ) ;
100- let def_id = mir_body. source . def_id ( ) ;
101- let ( some_fn_sig, hir_body) = fn_sig_and_body ( tcx, def_id) ;
80+ let hir_info @ ExtractedHirInfo { function_source_hash, fn_sig_span, body_span } =
81+ extract_hir_info ( tcx, mir_body. source . def_id ( ) . expect_local ( ) ) ;
10282
103- let body_span = get_body_span ( tcx , hir_body , mir_body) ;
83+ debug ! ( ?hir_info , "instrumenting {:?}" , mir_body. source . def_id ( ) ) ;
10484
105- let source_file = source_map. lookup_source_file ( body_span. lo ( ) ) ;
106- let fn_sig_span = match some_fn_sig. filter ( |fn_sig| {
107- fn_sig. span . eq_ctxt ( body_span)
108- && Lrc :: ptr_eq ( & source_file, & source_map. lookup_source_file ( fn_sig. span . lo ( ) ) )
109- } ) {
110- Some ( fn_sig) => fn_sig. span . with_hi ( body_span. lo ( ) ) ,
111- None => body_span. shrink_to_lo ( ) ,
112- } ;
113-
114- debug ! (
115- "instrumenting {}: {:?}, fn sig span: {:?}, body span: {:?}" ,
116- if tcx. is_closure( def_id) { "closure" } else { "function" } ,
117- def_id,
118- fn_sig_span,
119- body_span
120- ) ;
121-
122- let function_source_hash = hash_mir_source ( tcx, hir_body) ;
12385 let basic_coverage_blocks = CoverageGraph :: from_mir ( mir_body) ;
12486 let coverage_counters = CoverageCounters :: new ( & basic_coverage_blocks) ;
12587
12688 Self {
12789 tcx,
12890 mir_body,
129- source_file,
13091 fn_sig_span,
13192 body_span,
13293 function_source_hash,
@@ -136,15 +97,12 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> {
13697 }
13798
13899 fn inject_counters ( & ' a mut self ) {
139- let fn_sig_span = self . fn_sig_span ;
140- let body_span = self . body_span ;
141-
142100 ////////////////////////////////////////////////////
143101 // Compute coverage spans from the `CoverageGraph`.
144102 let coverage_spans = CoverageSpans :: generate_coverage_spans (
145103 self . mir_body ,
146- fn_sig_span,
147- body_span,
104+ self . fn_sig_span ,
105+ self . body_span ,
148106 & self . basic_coverage_blocks ,
149107 ) ;
150108
@@ -177,9 +135,10 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> {
177135 let source_map = self . tcx . sess . source_map ( ) ;
178136 let body_span = self . body_span ;
179137
138+ let source_file = source_map. lookup_source_file ( body_span. lo ( ) ) ;
180139 use rustc_session:: RemapFileNameExt ;
181140 let file_name =
182- Symbol :: intern ( & self . source_file . name . for_codegen ( self . tcx . sess ) . to_string_lossy ( ) ) ;
141+ Symbol :: intern ( & source_file. name . for_codegen ( self . tcx . sess ) . to_string_lossy ( ) ) ;
183142
184143 let mut mappings = Vec :: new ( ) ;
185144
@@ -325,27 +284,75 @@ fn make_code_region(
325284 }
326285}
327286
328- fn fn_sig_and_body (
329- tcx : TyCtxt < ' _ > ,
330- def_id : DefId ,
331- ) -> ( Option < & rustc_hir:: FnSig < ' _ > > , & rustc_hir:: Body < ' _ > ) {
287+ fn is_eligible_for_coverage ( tcx : TyCtxt < ' _ > , def_id : LocalDefId ) -> bool {
288+ // Only instrument functions, methods, and closures (not constants since they are evaluated
289+ // at compile time by Miri).
290+ // FIXME(#73156): Handle source code coverage in const eval, but note, if and when const
291+ // expressions get coverage spans, we will probably have to "carve out" space for const
292+ // expressions from coverage spans in enclosing MIR's, like we do for closures. (That might
293+ // be tricky if const expressions have no corresponding statements in the enclosing MIR.
294+ // Closures are carved out by their initial `Assign` statement.)
295+ if !tcx. def_kind ( def_id) . is_fn_like ( ) {
296+ trace ! ( "InstrumentCoverage skipped for {def_id:?} (not an fn-like)" ) ;
297+ return false ;
298+ }
299+
300+ if tcx. codegen_fn_attrs ( def_id) . flags . contains ( CodegenFnAttrFlags :: NO_COVERAGE ) {
301+ return false ;
302+ }
303+
304+ true
305+ }
306+
307+ /// Function information extracted from HIR by the coverage instrumentor.
308+ #[ derive( Debug ) ]
309+ struct ExtractedHirInfo {
310+ function_source_hash : u64 ,
311+ fn_sig_span : Span ,
312+ body_span : Span ,
313+ }
314+
315+ fn extract_hir_info < ' tcx > ( tcx : TyCtxt < ' tcx > , def_id : LocalDefId ) -> ExtractedHirInfo {
332316 // FIXME(#79625): Consider improving MIR to provide the information needed, to avoid going back
333317 // to HIR for it.
334- let hir_node = tcx. hir ( ) . get_if_local ( def_id) . expect ( "expected DefId is local" ) ;
318+
319+ let hir_node = tcx. hir_node_by_def_id ( def_id) ;
335320 let ( _, fn_body_id) =
336321 hir:: map:: associated_body ( hir_node) . expect ( "HIR node is a function with body" ) ;
337- ( hir_node. fn_sig ( ) , tcx. hir ( ) . body ( fn_body_id) )
322+ let hir_body = tcx. hir ( ) . body ( fn_body_id) ;
323+
324+ let body_span = get_body_span ( tcx, hir_body, def_id) ;
325+
326+ // The actual signature span is only used if it has the same context and
327+ // filename as the body, and precedes the body.
328+ let maybe_fn_sig_span = hir_node. fn_sig ( ) . map ( |fn_sig| fn_sig. span ) ;
329+ let fn_sig_span = maybe_fn_sig_span
330+ . filter ( |& fn_sig_span| {
331+ let source_map = tcx. sess . source_map ( ) ;
332+ let file_idx = |span : Span | source_map. lookup_source_file_idx ( span. lo ( ) ) ;
333+
334+ fn_sig_span. eq_ctxt ( body_span)
335+ && fn_sig_span. hi ( ) <= body_span. lo ( )
336+ && file_idx ( fn_sig_span) == file_idx ( body_span)
337+ } )
338+ // If so, extend it to the start of the body span.
339+ . map ( |fn_sig_span| fn_sig_span. with_hi ( body_span. lo ( ) ) )
340+ // Otherwise, create a dummy signature span at the start of the body.
341+ . unwrap_or_else ( || body_span. shrink_to_lo ( ) ) ;
342+
343+ let function_source_hash = hash_mir_source ( tcx, hir_body) ;
344+
345+ ExtractedHirInfo { function_source_hash, fn_sig_span, body_span }
338346}
339347
340348fn get_body_span < ' tcx > (
341349 tcx : TyCtxt < ' tcx > ,
342350 hir_body : & rustc_hir:: Body < ' tcx > ,
343- mir_body : & mut mir :: Body < ' tcx > ,
351+ def_id : LocalDefId ,
344352) -> Span {
345353 let mut body_span = hir_body. value . span ;
346- let def_id = mir_body. source . def_id ( ) ;
347354
348- if tcx. is_closure ( def_id) {
355+ if tcx. is_closure ( def_id. to_def_id ( ) ) {
349356 // If the MIR function is a closure, and if the closure body span
350357 // starts from a macro, but it's content is not in that macro, try
351358 // to find a non-macro callsite, and instrument the spans there
0 commit comments