@@ -13,7 +13,6 @@ use self::spans::CoverageSpans;
13
13
14
14
use crate :: MirPass ;
15
15
16
- use rustc_data_structures:: sync:: Lrc ;
17
16
use rustc_middle:: hir;
18
17
use rustc_middle:: middle:: codegen_fn_attrs:: CodegenFnAttrFlags ;
19
18
use rustc_middle:: mir:: coverage:: * ;
@@ -22,9 +21,9 @@ use rustc_middle::mir::{
22
21
TerminatorKind ,
23
22
} ;
24
23
use rustc_middle:: ty:: TyCtxt ;
25
- use rustc_span:: def_id:: DefId ;
24
+ use rustc_span:: def_id:: LocalDefId ;
26
25
use rustc_span:: source_map:: SourceMap ;
27
- use rustc_span:: { ExpnKind , SourceFile , Span , Symbol } ;
26
+ use rustc_span:: { ExpnKind , Span , Symbol } ;
28
27
29
28
/// Inserts `StatementKind::Coverage` statements that either instrument the binary with injected
30
29
/// counters, via intrinsic `llvm.instrprof.increment`, and/or inject metadata used during codegen
@@ -39,31 +38,19 @@ impl<'tcx> MirPass<'tcx> for InstrumentCoverage {
39
38
fn run_pass ( & self , tcx : TyCtxt < ' tcx > , mir_body : & mut mir:: Body < ' tcx > ) {
40
39
let mir_source = mir_body. source ;
41
40
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 ( ) ;
51
46
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)" ) ;
64
49
return ;
65
50
}
66
51
52
+ // An otherwise-eligible function is still skipped if its start block
53
+ // is known to be unreachable.
67
54
match mir_body. basic_blocks [ mir:: START_BLOCK ] . terminator ( ) . kind {
68
55
TerminatorKind :: Unreachable => {
69
56
trace ! ( "InstrumentCoverage skipped for unreachable `START_BLOCK`" ) ;
@@ -72,21 +59,15 @@ impl<'tcx> MirPass<'tcx> for InstrumentCoverage {
72
59
_ => { }
73
60
}
74
61
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:?}" ) ;
81
63
Instrumentor :: new ( tcx, mir_body) . inject_counters ( ) ;
82
- trace ! ( "InstrumentCoverage done for {:?}" , mir_source . def_id ( ) ) ;
64
+ trace ! ( "InstrumentCoverage done for {def_id :?}" ) ;
83
65
}
84
66
}
85
67
86
68
struct Instrumentor < ' a , ' tcx > {
87
69
tcx : TyCtxt < ' tcx > ,
88
70
mir_body : & ' a mut mir:: Body < ' tcx > ,
89
- source_file : Lrc < SourceFile > ,
90
71
fn_sig_span : Span ,
91
72
body_span : Span ,
92
73
function_source_hash : u64 ,
@@ -96,37 +77,17 @@ struct Instrumentor<'a, 'tcx> {
96
77
97
78
impl < ' a , ' tcx > Instrumentor < ' a , ' tcx > {
98
79
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 ( ) ) ;
102
82
103
- let body_span = get_body_span ( tcx , hir_body , mir_body) ;
83
+ debug ! ( ?hir_info , "instrumenting {:?}" , mir_body. source . def_id ( ) ) ;
104
84
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) ;
123
85
let basic_coverage_blocks = CoverageGraph :: from_mir ( mir_body) ;
124
86
let coverage_counters = CoverageCounters :: new ( & basic_coverage_blocks) ;
125
87
126
88
Self {
127
89
tcx,
128
90
mir_body,
129
- source_file,
130
91
fn_sig_span,
131
92
body_span,
132
93
function_source_hash,
@@ -136,15 +97,12 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> {
136
97
}
137
98
138
99
fn inject_counters ( & ' a mut self ) {
139
- let fn_sig_span = self . fn_sig_span ;
140
- let body_span = self . body_span ;
141
-
142
100
////////////////////////////////////////////////////
143
101
// Compute coverage spans from the `CoverageGraph`.
144
102
let coverage_spans = CoverageSpans :: generate_coverage_spans (
145
103
self . mir_body ,
146
- fn_sig_span,
147
- body_span,
104
+ self . fn_sig_span ,
105
+ self . body_span ,
148
106
& self . basic_coverage_blocks ,
149
107
) ;
150
108
@@ -177,9 +135,10 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> {
177
135
let source_map = self . tcx . sess . source_map ( ) ;
178
136
let body_span = self . body_span ;
179
137
138
+ let source_file = source_map. lookup_source_file ( body_span. lo ( ) ) ;
180
139
use rustc_session:: RemapFileNameExt ;
181
140
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 ( ) ) ;
183
142
184
143
let mut mappings = Vec :: new ( ) ;
185
144
@@ -325,27 +284,75 @@ fn make_code_region(
325
284
}
326
285
}
327
286
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 {
332
316
// FIXME(#79625): Consider improving MIR to provide the information needed, to avoid going back
333
317
// 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) ;
335
320
let ( _, fn_body_id) =
336
321
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 }
338
346
}
339
347
340
348
fn get_body_span < ' tcx > (
341
349
tcx : TyCtxt < ' tcx > ,
342
350
hir_body : & rustc_hir:: Body < ' tcx > ,
343
- mir_body : & mut mir :: Body < ' tcx > ,
351
+ def_id : LocalDefId ,
344
352
) -> Span {
345
353
let mut body_span = hir_body. value . span ;
346
- let def_id = mir_body. source . def_id ( ) ;
347
354
348
- if tcx. is_closure ( def_id) {
355
+ if tcx. is_closure ( def_id. to_def_id ( ) ) {
349
356
// If the MIR function is a closure, and if the closure body span
350
357
// starts from a macro, but it's content is not in that macro, try
351
358
// to find a non-macro callsite, and instrument the spans there
0 commit comments