@@ -4,7 +4,7 @@ use std::collections::hash_map::Entry;
4
4
use rustc_data_structures:: fx:: FxHashMap ;
5
5
use rustc_index:: IndexVec ;
6
6
use rustc_middle:: mir:: coverage:: {
7
- BlockMarkerId , BranchSpan , CoverageKind , DecisionId , DecisionSpan ,
7
+ BlockMarkerId , BranchSpan , ConditionId , CoverageKind , DecisionId , DecisionSpan ,
8
8
} ;
9
9
use rustc_middle:: mir:: { self , BasicBlock , UnOp } ;
10
10
use rustc_middle:: thir:: { ExprId , ExprKind , Thir } ;
@@ -15,14 +15,7 @@ use rustc_span::Span;
15
15
use crate :: build:: Builder ;
16
16
use crate :: errors:: MCDCNestedDecision ;
17
17
18
- pub ( crate ) struct BranchInfoBuilder {
19
- /// Maps condition expressions to their enclosing `!`, for better instrumentation.
20
- nots : FxHashMap < ExprId , NotInfo > ,
21
-
22
- num_block_markers : usize ,
23
- branch_spans : Vec < BranchSpan > ,
24
-
25
- // MCDC decision stuff
18
+ struct MCDCInfoBuilder {
26
19
/// ID of the current decision.
27
20
/// Do not use directly. Use the function instead, as it will hide
28
21
/// the decision in the scope of nested decisions.
@@ -31,7 +24,54 @@ pub(crate) struct BranchInfoBuilder {
31
24
/// nested decisions.
32
25
nested_decision_level : u32 ,
33
26
/// Vector for storing all the decisions with their span
34
- decisions : IndexVec < DecisionId , Span > ,
27
+ decision_spans : IndexVec < DecisionId , Span > ,
28
+
29
+ next_condition_id : u32 ,
30
+ }
31
+
32
+ impl MCDCInfoBuilder {
33
+ /// Increase the nested decision level and return true if the
34
+ /// decision can be instrumented (not in a nested condition).
35
+ pub fn enter_decision ( & mut self , span : Span ) -> bool {
36
+ self . nested_decision_level += 1 ;
37
+ let can_mcdc = !self . in_nested_condition ( ) ;
38
+
39
+ if can_mcdc {
40
+ self . current_decision_id = Some ( self . decision_spans . push ( span) ) ;
41
+ }
42
+
43
+ can_mcdc
44
+ }
45
+
46
+ pub fn exit_decision ( & mut self ) {
47
+ self . nested_decision_level -= 1 ;
48
+ }
49
+
50
+ /// Return true if the current decision is located inside another decision.
51
+ pub fn in_nested_condition ( & self ) -> bool {
52
+ self . nested_decision_level > 1
53
+ }
54
+
55
+ pub fn current_decision_id ( & self ) -> Option < DecisionId > {
56
+ if self . in_nested_condition ( ) { None } else { self . current_decision_id }
57
+ }
58
+
59
+ pub fn next_condition_id ( & mut self ) -> u32 {
60
+ let res = self . next_condition_id ;
61
+ self . next_condition_id += 1 ;
62
+ res
63
+ }
64
+ }
65
+
66
+ pub ( crate ) struct BranchInfoBuilder {
67
+ /// Maps condition expressions to their enclosing `!`, for better instrumentation.
68
+ nots : FxHashMap < ExprId , NotInfo > ,
69
+
70
+ num_block_markers : usize ,
71
+ branch_spans : Vec < BranchSpan > ,
72
+
73
+ // MCDC decision stuff
74
+ mcdc_info : Option < MCDCInfoBuilder > ,
35
75
}
36
76
37
77
#[ derive( Clone , Copy ) ]
@@ -51,13 +91,21 @@ impl BranchInfoBuilder {
51
91
if ( tcx. sess . instrument_coverage_branch ( ) || tcx. sess . instrument_coverage_mcdc ( ) )
52
92
&& tcx. is_eligible_for_coverage ( def_id)
53
93
{
94
+ let mcdc_info = if tcx. sess . instrument_coverage_mcdc ( ) {
95
+ Some ( MCDCInfoBuilder {
96
+ current_decision_id : None ,
97
+ nested_decision_level : 0 ,
98
+ decision_spans : IndexVec :: new ( ) ,
99
+ next_condition_id : 0 ,
100
+ } )
101
+ } else {
102
+ None
103
+ } ;
54
104
Some ( Self {
55
105
nots : FxHashMap :: default ( ) ,
56
106
num_block_markers : 0 ,
57
107
branch_spans : vec ! [ ] ,
58
- current_decision_id : None ,
59
- nested_decision_level : 0 ,
60
- decisions : IndexVec :: new ( ) ,
108
+ mcdc_info,
61
109
} )
62
110
} else {
63
111
None
@@ -111,15 +159,18 @@ impl BranchInfoBuilder {
111
159
}
112
160
113
161
pub ( crate ) fn into_done ( self ) -> Option < Box < mir:: coverage:: BranchInfo > > {
114
- let Self { nots : _, num_block_markers, branch_spans, decisions , .. } = self ;
162
+ let Self { nots : _, num_block_markers, branch_spans, mcdc_info , .. } = self ;
115
163
116
164
if num_block_markers == 0 {
117
165
assert ! ( branch_spans. is_empty( ) ) ;
118
166
return None ;
119
167
}
120
168
121
169
let mut decision_spans = IndexVec :: from_iter (
122
- decisions. into_iter ( ) . map ( |span| DecisionSpan { span, num_conditions : 0 } ) ,
170
+ mcdc_info
171
+ . map_or ( IndexVec :: new ( ) , |mcdc_info| mcdc_info. decision_spans )
172
+ . into_iter ( )
173
+ . map ( |span| DecisionSpan { span, num_conditions : 0 } ) ,
123
174
) ;
124
175
125
176
// Count the number of conditions linked to each decision.
@@ -135,32 +186,6 @@ impl BranchInfoBuilder {
135
186
decision_spans,
136
187
} ) )
137
188
}
138
-
139
- /// Increase the nested decision level and return true if the
140
- /// decision can be instrumented (not in a nested condition).
141
- pub fn enter_decision ( & mut self , span : Span ) -> bool {
142
- self . nested_decision_level += 1 ;
143
- let can_mcdc = !self . in_nested_condition ( ) ;
144
-
145
- if can_mcdc {
146
- self . current_decision_id = Some ( self . decisions . push ( span) ) ;
147
- }
148
-
149
- can_mcdc
150
- }
151
-
152
- pub fn exit_decision ( & mut self ) {
153
- self . nested_decision_level -= 1 ;
154
- }
155
-
156
- /// Return true if the current decision is located inside another decision.
157
- pub fn in_nested_condition ( & self ) -> bool {
158
- self . nested_decision_level > 1
159
- }
160
-
161
- pub fn current_decision_id ( & self ) -> Option < DecisionId > {
162
- if self . in_nested_condition ( ) { None } else { self . current_decision_id }
163
- }
164
189
}
165
190
166
191
impl Builder < ' _ , ' _ > {
@@ -206,7 +231,11 @@ impl Builder<'_, '_> {
206
231
branch_info. branch_spans . push ( BranchSpan {
207
232
span : source_info. span ,
208
233
// FIXME(dprn): Handle case when MCDC is disabled better than just putting 0.
209
- decision_id : branch_info. current_decision_id . unwrap_or ( DecisionId :: from_u32 ( 0 ) ) ,
234
+ decision_id : branch_info
235
+ . mcdc_info
236
+ . as_ref ( )
237
+ . and_then ( |mcdc_info| mcdc_info. current_decision_id ( ) )
238
+ . unwrap_or ( DecisionId :: from_u32 ( 0 ) ) ,
210
239
true_marker,
211
240
false_marker,
212
241
} ) ;
@@ -215,25 +244,24 @@ impl Builder<'_, '_> {
215
244
/// If MCDC coverage is enabled, inject a decision entry marker in the given decision.
216
245
/// return true
217
246
pub ( crate ) fn begin_mcdc_decision_coverage ( & mut self , expr_id : ExprId , block : BasicBlock ) {
218
- // Early return if MCDC coverage is not enabled.
219
- if !self . tcx . sess . instrument_coverage_mcdc ( ) {
220
- return ;
221
- }
222
- let Some ( branch_info) = self . coverage_branch_info . as_mut ( ) else {
247
+ // Get the MCDCInfoBuilder object, which existence implies that MCDC is enabled.
248
+ let Some ( BranchInfoBuilder { mcdc_info : Some ( mcdc_info) , .. } ) =
249
+ self . coverage_branch_info . as_mut ( )
250
+ else {
223
251
return ;
224
252
} ;
225
253
226
254
let span = self . thir [ expr_id] . span ;
227
255
228
256
// enter_decision returns false if it detects nested decisions.
229
- if !branch_info . enter_decision ( span) {
230
- // FIXME(dprn): do WARNING for nested decision.
257
+ if !mcdc_info . enter_decision ( span) {
258
+ // FIXME(dprn): WARNING for nested decision does not seem to work properly
231
259
debug ! ( "MCDC: Unsupported nested decision" ) ;
232
260
self . tcx . dcx ( ) . emit_warn ( MCDCNestedDecision { span } ) ;
233
261
return ;
234
262
}
235
263
236
- let decision_id = branch_info . current_decision_id ( ) . expect ( "Should have returned." ) ;
264
+ let decision_id = mcdc_info . current_decision_id ( ) . expect ( "Should have returned." ) ;
237
265
238
266
// Inject a decision marker
239
267
let source_info = self . source_info ( span) ;
@@ -248,16 +276,15 @@ impl Builder<'_, '_> {
248
276
249
277
/// If MCDC is enabled, and function is instrumented,
250
278
pub ( crate ) fn end_mcdc_decision_coverage ( & mut self ) {
251
- // Early return if MCDC coverage is not enabled.
252
- if !self . tcx . sess . instrument_coverage_mcdc ( ) {
253
- return ;
254
- }
255
- let Some ( branch_info) = self . coverage_branch_info . as_mut ( ) else {
279
+ // Get the MCDCInfoBuilder object, which existence implies that MCDC is enabled.
280
+ let Some ( BranchInfoBuilder { mcdc_info : Some ( mcdc_info) , .. } ) =
281
+ self . coverage_branch_info . as_mut ( )
282
+ else {
256
283
return ;
257
284
} ;
258
285
259
286
// Exit decision now so we can drop &mut to branch info
260
- branch_info . exit_decision ( ) ;
287
+ mcdc_info . exit_decision ( ) ;
261
288
}
262
289
263
290
/// If MCDC is enabled and the current decision is being instrumented,
@@ -268,16 +295,19 @@ impl Builder<'_, '_> {
268
295
bb : BasicBlock ,
269
296
outcome : bool ,
270
297
) -> BasicBlock {
271
- let Some ( branch_info) = self . coverage_branch_info . as_mut ( ) else {
272
- // Coverage instrumentation is not enabled.
298
+ // Get the MCDCInfoBuilder object, which existence implies that MCDC is enabled.
299
+ let Some ( BranchInfoBuilder { mcdc_info : Some ( mcdc_info) , .. } ) =
300
+ self . coverage_branch_info . as_mut ( )
301
+ else {
273
302
return bb;
274
303
} ;
275
- let Some ( decision_id) = branch_info. current_decision_id ( ) else {
304
+
305
+ let Some ( decision_id) = mcdc_info. current_decision_id ( ) else {
276
306
// Decision is not instrumented
277
307
return bb;
278
308
} ;
279
309
280
- let span = branch_info . decisions [ decision_id] ;
310
+ let span = mcdc_info . decision_spans [ decision_id] ;
281
311
let source_info = self . source_info ( span) ;
282
312
let marker_statement = mir:: Statement {
283
313
source_info,
@@ -298,4 +328,49 @@ impl Builder<'_, '_> {
298
328
299
329
new_bb
300
330
}
331
+
332
+ /// Add markers on the condition's basic blocks to ease the later MCDC instrumentation.
333
+ pub ( crate ) fn visit_mcdc_condition (
334
+ & mut self ,
335
+ condition_expr : ExprId ,
336
+ condition_block : BasicBlock ,
337
+ then_block : BasicBlock ,
338
+ else_block : BasicBlock ,
339
+ ) {
340
+ // Get the MCDCInfoBuilder object, which existence implies that MCDC is enabled.
341
+ let Some ( BranchInfoBuilder { mcdc_info : Some ( mcdc_info) , .. } ) =
342
+ self . coverage_branch_info . as_mut ( )
343
+ else {
344
+ return ;
345
+ } ;
346
+
347
+ let Some ( decision_id) = mcdc_info. current_decision_id ( ) else {
348
+ // If current_decision_id() is None, the decision is not instrumented.
349
+ return ;
350
+ } ;
351
+
352
+
353
+ let id = ConditionId :: from_u32 ( mcdc_info. next_condition_id ( ) ) ;
354
+ let span = self . thir [ condition_expr] . span ;
355
+ let source_info = self . source_info ( span) ;
356
+
357
+ let mut inject_statement = |bb, cov_kind| {
358
+ let statement =
359
+ mir:: Statement { source_info, kind : mir:: StatementKind :: Coverage ( cov_kind) } ;
360
+ self . cfg . basic_blocks [ bb] . statements . insert ( 0 , statement) ;
361
+ } ;
362
+
363
+ inject_statement (
364
+ condition_block,
365
+ CoverageKind :: MCDCConditionEntryMarker { decision_id, id } ,
366
+ ) ;
367
+ inject_statement (
368
+ then_block,
369
+ CoverageKind :: MCDCConditionOutputMarker { decision_id, id, outcome : true } ,
370
+ ) ;
371
+ inject_statement (
372
+ else_block,
373
+ CoverageKind :: MCDCConditionOutputMarker { decision_id, id, outcome : false } ,
374
+ ) ;
375
+ }
301
376
}
0 commit comments