@@ -23,9 +23,7 @@ pub(crate) struct BranchInfoBuilder {
23
23
markers : BlockMarkerGen ,
24
24
branch_spans : Vec < BranchSpan > ,
25
25
26
- mcdc_branch_spans : Vec < MCDCBranchSpan > ,
27
- mcdc_decision_spans : Vec < MCDCDecisionSpan > ,
28
- mcdc_state : Option < MCDCState > ,
26
+ mcdc_info : Option < MCDCInfoBuilder > ,
29
27
}
30
28
31
29
#[ derive( Clone , Copy ) ]
@@ -76,9 +74,7 @@ impl BranchInfoBuilder {
76
74
nots : FxHashMap :: default ( ) ,
77
75
markers : BlockMarkerGen :: default ( ) ,
78
76
branch_spans : vec ! [ ] ,
79
- mcdc_branch_spans : vec ! [ ] ,
80
- mcdc_decision_spans : vec ! [ ] ,
81
- mcdc_state : MCDCState :: new_if_enabled ( tcx) ,
77
+ mcdc_info : tcx. sess . instrument_coverage_mcdc ( ) . then ( MCDCInfoBuilder :: new) ,
82
78
} )
83
79
} else {
84
80
None
@@ -125,48 +121,6 @@ impl BranchInfoBuilder {
125
121
}
126
122
}
127
123
128
- fn fetch_mcdc_condition_info (
129
- & mut self ,
130
- tcx : TyCtxt < ' _ > ,
131
- true_marker : BlockMarkerId ,
132
- false_marker : BlockMarkerId ,
133
- ) -> Option < ( u16 , ConditionInfo ) > {
134
- let mcdc_state = self . mcdc_state . as_mut ( ) ?;
135
- let decision_depth = mcdc_state. decision_depth ( ) ;
136
- let ( mut condition_info, decision_result) =
137
- mcdc_state. take_condition ( true_marker, false_marker) ;
138
- // take_condition() returns Some for decision_result when the decision stack
139
- // is empty, i.e. when all the conditions of the decision were instrumented,
140
- // and the decision is "complete".
141
- if let Some ( decision) = decision_result {
142
- match decision. conditions_num {
143
- 0 => {
144
- unreachable ! ( "Decision with no condition is not expected" ) ;
145
- }
146
- 1 ..=MAX_CONDITIONS_NUM_IN_DECISION => {
147
- self . mcdc_decision_spans . push ( decision) ;
148
- }
149
- _ => {
150
- // Do not generate mcdc mappings and statements for decisions with too many conditions.
151
- let rebase_idx = self . mcdc_branch_spans . len ( ) - decision. conditions_num + 1 ;
152
- for branch in & mut self . mcdc_branch_spans [ rebase_idx..] {
153
- branch. condition_info = None ;
154
- }
155
-
156
- // ConditionInfo of this branch shall also be reset.
157
- condition_info = None ;
158
-
159
- tcx. dcx ( ) . emit_warn ( MCDCExceedsConditionNumLimit {
160
- span : decision. span ,
161
- conditions_num : decision. conditions_num ,
162
- max_conditions_num : MAX_CONDITIONS_NUM_IN_DECISION ,
163
- } ) ;
164
- }
165
- }
166
- }
167
- condition_info. map ( |cond_info| ( decision_depth, cond_info) )
168
- }
169
-
170
124
fn add_two_way_branch < ' tcx > (
171
125
& mut self ,
172
126
cfg : & mut CFG < ' tcx > ,
@@ -185,16 +139,17 @@ impl BranchInfoBuilder {
185
139
nots : _,
186
140
markers : BlockMarkerGen { num_block_markers } ,
187
141
branch_spans,
188
- mcdc_branch_spans,
189
- mcdc_decision_spans,
190
- mcdc_state : _,
142
+ mcdc_info,
191
143
} = self ;
192
144
193
145
if num_block_markers == 0 {
194
146
assert ! ( branch_spans. is_empty( ) ) ;
195
147
return None ;
196
148
}
197
149
150
+ let ( mcdc_decision_spans, mcdc_branch_spans) =
151
+ mcdc_info. map ( MCDCInfoBuilder :: into_done) . unwrap_or_default ( ) ;
152
+
198
153
Some ( Box :: new ( mir:: coverage:: BranchInfo {
199
154
num_block_markers,
200
155
branch_spans,
@@ -221,20 +176,23 @@ struct MCDCState {
221
176
}
222
177
223
178
impl MCDCState {
224
- fn new_if_enabled ( tcx : TyCtxt < ' _ > ) -> Option < Self > {
225
- tcx. sess
226
- . instrument_coverage_mcdc ( )
227
- . then ( || Self { decision_ctx_stack : vec ! [ MCDCDecisionCtx :: default ( ) ] } )
179
+ fn new ( ) -> Self {
180
+ Self { decision_ctx_stack : vec ! [ MCDCDecisionCtx :: default ( ) ] }
228
181
}
229
182
230
183
/// Decision depth is given as a u16 to reduce the size of the `CoverageKind`,
231
184
/// as it is very unlikely that the depth ever reaches 2^16.
232
185
#[ inline]
233
186
fn decision_depth ( & self ) -> u16 {
234
- u16:: try_from (
235
- self . decision_ctx_stack . len ( ) . checked_sub ( 1 ) . expect ( "Unexpected empty decision stack" ) ,
236
- )
237
- . expect ( "decision depth did not fit in u16, this is likely to be an instrumentation error" )
187
+ match u16:: try_from ( self . decision_ctx_stack . len ( ) )
188
+ . expect (
189
+ "decision depth did not fit in u16, this is likely to be an instrumentation error" ,
190
+ )
191
+ . checked_sub ( 1 )
192
+ {
193
+ Some ( d) => d,
194
+ None => bug ! ( "Unexpected empty decision stack" ) ,
195
+ }
238
196
}
239
197
240
198
// At first we assign ConditionIds for each sub expression.
@@ -343,8 +301,9 @@ impl MCDCState {
343
301
true_marker : BlockMarkerId ,
344
302
false_marker : BlockMarkerId ,
345
303
) -> ( Option < ConditionInfo > , Option < MCDCDecisionSpan > ) {
346
- let decision_ctx =
347
- self . decision_ctx_stack . last_mut ( ) . expect ( "Unexpected empty decision_ctx_stack" ) ;
304
+ let Some ( decision_ctx) = self . decision_ctx_stack . last_mut ( ) else {
305
+ bug ! ( "Unexpected empty decision_ctx_stack" )
306
+ } ;
348
307
let Some ( condition_info) = decision_ctx. decision_stack . pop_back ( ) else {
349
308
return ( None , None ) ;
350
309
} ;
@@ -366,6 +325,74 @@ impl MCDCState {
366
325
}
367
326
}
368
327
328
+ struct MCDCInfoBuilder {
329
+ branch_spans : Vec < MCDCBranchSpan > ,
330
+ decision_spans : Vec < MCDCDecisionSpan > ,
331
+ state : MCDCState ,
332
+ }
333
+
334
+ impl MCDCInfoBuilder {
335
+ fn new ( ) -> Self {
336
+ Self { branch_spans : vec ! [ ] , decision_spans : vec ! [ ] , state : MCDCState :: new ( ) }
337
+ }
338
+
339
+ fn visit_evaluated_condition (
340
+ & mut self ,
341
+ tcx : TyCtxt < ' _ > ,
342
+ source_info : SourceInfo ,
343
+ true_block : BasicBlock ,
344
+ false_block : BasicBlock ,
345
+ mut inject_block_marker : impl FnMut ( SourceInfo , BasicBlock ) -> BlockMarkerId ,
346
+ ) {
347
+ let true_marker = inject_block_marker ( source_info, true_block) ;
348
+ let false_marker = inject_block_marker ( source_info, false_block) ;
349
+
350
+ let decision_depth = self . state . decision_depth ( ) ;
351
+ let ( mut condition_info, decision_result) =
352
+ self . state . take_condition ( true_marker, false_marker) ;
353
+ // take_condition() returns Some for decision_result when the decision stack
354
+ // is empty, i.e. when all the conditions of the decision were instrumented,
355
+ // and the decision is "complete".
356
+ if let Some ( decision) = decision_result {
357
+ match decision. conditions_num {
358
+ 0 => {
359
+ unreachable ! ( "Decision with no condition is not expected" ) ;
360
+ }
361
+ 1 ..=MAX_CONDITIONS_NUM_IN_DECISION => {
362
+ self . decision_spans . push ( decision) ;
363
+ }
364
+ _ => {
365
+ // Do not generate mcdc mappings and statements for decisions with too many conditions.
366
+ let rebase_idx = self . branch_spans . len ( ) - decision. conditions_num + 1 ;
367
+ for branch in & mut self . branch_spans [ rebase_idx..] {
368
+ branch. condition_info = None ;
369
+ }
370
+
371
+ // ConditionInfo of this branch shall also be reset.
372
+ condition_info = None ;
373
+
374
+ tcx. dcx ( ) . emit_warn ( MCDCExceedsConditionNumLimit {
375
+ span : decision. span ,
376
+ conditions_num : decision. conditions_num ,
377
+ max_conditions_num : MAX_CONDITIONS_NUM_IN_DECISION ,
378
+ } ) ;
379
+ }
380
+ }
381
+ }
382
+ self . branch_spans . push ( MCDCBranchSpan {
383
+ span : source_info. span ,
384
+ condition_info,
385
+ true_marker,
386
+ false_marker,
387
+ decision_depth,
388
+ } ) ;
389
+ }
390
+
391
+ fn into_done ( self ) -> ( Vec < MCDCDecisionSpan > , Vec < MCDCBranchSpan > ) {
392
+ ( self . decision_spans , self . branch_spans )
393
+ }
394
+ }
395
+
369
396
impl Builder < ' _ , ' _ > {
370
397
/// If branch coverage is enabled, inject marker statements into `then_block`
371
398
/// and `else_block`, and record their IDs in the table of branch spans.
@@ -390,50 +417,52 @@ impl Builder<'_, '_> {
390
417
let source_info = SourceInfo { span : self . thir [ expr_id] . span , scope : self . source_scope } ;
391
418
392
419
// Separate path for handling branches when MC/DC is enabled.
393
- if branch_info. mcdc_state . is_some ( ) {
394
- let mut inject_block_marker =
395
- |block| branch_info. markers . inject_block_marker ( & mut self . cfg , source_info, block) ;
396
- let true_marker = inject_block_marker ( then_block) ;
397
- let false_marker = inject_block_marker ( else_block) ;
398
- let ( decision_depth, condition_info) = branch_info
399
- . fetch_mcdc_condition_info ( self . tcx , true_marker, false_marker)
400
- . map_or ( ( 0 , None ) , |( decision_depth, condition_info) | {
401
- ( decision_depth, Some ( condition_info) )
402
- } ) ;
403
- branch_info. mcdc_branch_spans . push ( MCDCBranchSpan {
404
- span : source_info. span ,
405
- condition_info,
406
- true_marker,
407
- false_marker,
408
- decision_depth,
409
- } ) ;
420
+ if let Some ( mcdc_info) = branch_info. mcdc_info . as_mut ( ) {
421
+ let inject_block_marker = |source_info, block| {
422
+ branch_info. markers . inject_block_marker ( & mut self . cfg , source_info, block)
423
+ } ;
424
+ mcdc_info. visit_evaluated_condition (
425
+ self . tcx ,
426
+ source_info,
427
+ then_block,
428
+ else_block,
429
+ inject_block_marker,
430
+ ) ;
410
431
return ;
411
432
}
412
433
413
434
branch_info. add_two_way_branch ( & mut self . cfg , source_info, then_block, else_block) ;
414
435
}
415
436
416
437
pub ( crate ) fn visit_coverage_branch_operation ( & mut self , logical_op : LogicalOp , span : Span ) {
417
- if let Some ( branch_info) = self . coverage_branch_info . as_mut ( )
418
- && let Some ( mcdc_state) = branch_info. mcdc_state . as_mut ( )
438
+ if let Some ( mcdc_info) = self
439
+ . coverage_branch_info
440
+ . as_mut ( )
441
+ . and_then ( |branch_info| branch_info. mcdc_info . as_mut ( ) )
419
442
{
420
- mcdc_state . record_conditions ( logical_op, span) ;
443
+ mcdc_info . state . record_conditions ( logical_op, span) ;
421
444
}
422
445
}
423
446
424
447
pub ( crate ) fn mcdc_increment_depth_if_enabled ( & mut self ) {
425
- if let Some ( branch_info) = self . coverage_branch_info . as_mut ( )
426
- && let Some ( mcdc_state) = branch_info. mcdc_state . as_mut ( )
448
+ if let Some ( mcdc_info) = self
449
+ . coverage_branch_info
450
+ . as_mut ( )
451
+ . and_then ( |branch_info| branch_info. mcdc_info . as_mut ( ) )
427
452
{
428
- mcdc_state . decision_ctx_stack . push ( MCDCDecisionCtx :: default ( ) ) ;
453
+ mcdc_info . state . decision_ctx_stack . push ( MCDCDecisionCtx :: default ( ) ) ;
429
454
} ;
430
455
}
431
456
432
457
pub ( crate ) fn mcdc_decrement_depth_if_enabled ( & mut self ) {
433
- if let Some ( branch_info) = self . coverage_branch_info . as_mut ( )
434
- && let Some ( mcdc_state) = branch_info. mcdc_state . as_mut ( )
458
+ if let Some ( mcdc_info) = self
459
+ . coverage_branch_info
460
+ . as_mut ( )
461
+ . and_then ( |branch_info| branch_info. mcdc_info . as_mut ( ) )
435
462
{
436
- mcdc_state. decision_ctx_stack . pop ( ) . expect ( "Unexpected empty decision stack" ) ;
463
+ if mcdc_info. state . decision_ctx_stack . pop ( ) . is_none ( ) {
464
+ bug ! ( "Unexpected empty decision stack" ) ;
465
+ }
437
466
} ;
438
467
}
439
468
}
0 commit comments