@@ -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.
@@ -279,8 +237,9 @@ impl MCDCState {
279
237
// - If the op is OR, the "true_next" of LHS and RHS should be the parent's "true_next". While "false_next" of the LHS is the RHS, the "false next" of RHS is the parent's "false_next".
280
238
fn record_conditions ( & mut self , op : LogicalOp , span : Span ) {
281
239
let decision_depth = self . decision_depth ( ) ;
282
- let decision_ctx =
283
- self . decision_ctx_stack . last_mut ( ) . expect ( "Unexpected empty decision_ctx_stack" ) ;
240
+ let Some ( decision_ctx) = self . decision_ctx_stack . last_mut ( ) else {
241
+ bug ! ( "Unexpected empty decision_ctx_stack" )
242
+ } ;
284
243
let decision = match decision_ctx. processing_decision . as_mut ( ) {
285
244
Some ( decision) => {
286
245
decision. span = decision. span . to ( span) ;
@@ -343,8 +302,9 @@ impl MCDCState {
343
302
true_marker : BlockMarkerId ,
344
303
false_marker : BlockMarkerId ,
345
304
) -> ( Option < ConditionInfo > , Option < MCDCDecisionSpan > ) {
346
- let decision_ctx =
347
- self . decision_ctx_stack . last_mut ( ) . expect ( "Unexpected empty decision_ctx_stack" ) ;
305
+ let Some ( decision_ctx) = self . decision_ctx_stack . last_mut ( ) else {
306
+ bug ! ( "Unexpected empty decision_ctx_stack" )
307
+ } ;
348
308
let Some ( condition_info) = decision_ctx. decision_stack . pop_back ( ) else {
349
309
return ( None , None ) ;
350
310
} ;
@@ -366,6 +326,74 @@ impl MCDCState {
366
326
}
367
327
}
368
328
329
+ struct MCDCInfoBuilder {
330
+ branch_spans : Vec < MCDCBranchSpan > ,
331
+ decision_spans : Vec < MCDCDecisionSpan > ,
332
+ state : MCDCState ,
333
+ }
334
+
335
+ impl MCDCInfoBuilder {
336
+ fn new ( ) -> Self {
337
+ Self { branch_spans : vec ! [ ] , decision_spans : vec ! [ ] , state : MCDCState :: new ( ) }
338
+ }
339
+
340
+ fn visit_evaluated_condition (
341
+ & mut self ,
342
+ tcx : TyCtxt < ' _ > ,
343
+ source_info : SourceInfo ,
344
+ true_block : BasicBlock ,
345
+ false_block : BasicBlock ,
346
+ mut inject_block_marker : impl FnMut ( SourceInfo , BasicBlock ) -> BlockMarkerId ,
347
+ ) {
348
+ let true_marker = inject_block_marker ( source_info, true_block) ;
349
+ let false_marker = inject_block_marker ( source_info, false_block) ;
350
+
351
+ let decision_depth = self . state . decision_depth ( ) ;
352
+ let ( mut condition_info, decision_result) =
353
+ self . state . take_condition ( true_marker, false_marker) ;
354
+ // take_condition() returns Some for decision_result when the decision stack
355
+ // is empty, i.e. when all the conditions of the decision were instrumented,
356
+ // and the decision is "complete".
357
+ if let Some ( decision) = decision_result {
358
+ match decision. conditions_num {
359
+ 0 => {
360
+ unreachable ! ( "Decision with no condition is not expected" ) ;
361
+ }
362
+ 1 ..=MAX_CONDITIONS_NUM_IN_DECISION => {
363
+ self . decision_spans . push ( decision) ;
364
+ }
365
+ _ => {
366
+ // Do not generate mcdc mappings and statements for decisions with too many conditions.
367
+ let rebase_idx = self . branch_spans . len ( ) - decision. conditions_num + 1 ;
368
+ for branch in & mut self . branch_spans [ rebase_idx..] {
369
+ branch. condition_info = None ;
370
+ }
371
+
372
+ // ConditionInfo of this branch shall also be reset.
373
+ condition_info = None ;
374
+
375
+ tcx. dcx ( ) . emit_warn ( MCDCExceedsConditionNumLimit {
376
+ span : decision. span ,
377
+ conditions_num : decision. conditions_num ,
378
+ max_conditions_num : MAX_CONDITIONS_NUM_IN_DECISION ,
379
+ } ) ;
380
+ }
381
+ }
382
+ }
383
+ self . branch_spans . push ( MCDCBranchSpan {
384
+ span : source_info. span ,
385
+ condition_info,
386
+ true_marker,
387
+ false_marker,
388
+ decision_depth,
389
+ } ) ;
390
+ }
391
+
392
+ fn into_done ( self ) -> ( Vec < MCDCDecisionSpan > , Vec < MCDCBranchSpan > ) {
393
+ ( self . decision_spans , self . branch_spans )
394
+ }
395
+ }
396
+
369
397
impl Builder < ' _ , ' _ > {
370
398
/// If branch coverage is enabled, inject marker statements into `then_block`
371
399
/// and `else_block`, and record their IDs in the table of branch spans.
@@ -390,23 +418,17 @@ impl Builder<'_, '_> {
390
418
let source_info = SourceInfo { span : self . thir [ expr_id] . span , scope : self . source_scope } ;
391
419
392
420
// 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
- } ) ;
421
+ if let Some ( mcdc_info) = branch_info. mcdc_info . as_mut ( ) {
422
+ let inject_block_marker = |source_info, block| {
423
+ branch_info. markers . inject_block_marker ( & mut self . cfg , source_info, block)
424
+ } ;
425
+ mcdc_info. visit_evaluated_condition (
426
+ self . tcx ,
427
+ source_info,
428
+ then_block,
429
+ else_block,
430
+ inject_block_marker,
431
+ ) ;
410
432
return ;
411
433
}
412
434
@@ -415,25 +437,27 @@ impl Builder<'_, '_> {
415
437
416
438
pub ( crate ) fn visit_coverage_branch_operation ( & mut self , logical_op : LogicalOp , span : Span ) {
417
439
if let Some ( branch_info) = self . coverage_branch_info . as_mut ( )
418
- && let Some ( mcdc_state ) = branch_info. mcdc_state . as_mut ( )
440
+ && let Some ( mcdc_info ) = branch_info. mcdc_info . as_mut ( )
419
441
{
420
- mcdc_state . record_conditions ( logical_op, span) ;
442
+ mcdc_info . state . record_conditions ( logical_op, span) ;
421
443
}
422
444
}
423
445
424
446
pub ( crate ) fn mcdc_increment_depth_if_enabled ( & mut self ) {
425
447
if let Some ( branch_info) = self . coverage_branch_info . as_mut ( )
426
- && let Some ( mcdc_state ) = branch_info. mcdc_state . as_mut ( )
448
+ && let Some ( mcdc_info ) = branch_info. mcdc_info . as_mut ( )
427
449
{
428
- mcdc_state . decision_ctx_stack . push ( MCDCDecisionCtx :: default ( ) ) ;
450
+ mcdc_info . state . decision_ctx_stack . push ( MCDCDecisionCtx :: default ( ) ) ;
429
451
} ;
430
452
}
431
453
432
454
pub ( crate ) fn mcdc_decrement_depth_if_enabled ( & mut self ) {
433
455
if let Some ( branch_info) = self . coverage_branch_info . as_mut ( )
434
- && let Some ( mcdc_state ) = branch_info. mcdc_state . as_mut ( )
456
+ && let Some ( mcdc_info ) = branch_info. mcdc_info . as_mut ( )
435
457
{
436
- mcdc_state. decision_ctx_stack . pop ( ) . expect ( "Unexpected empty decision stack" ) ;
458
+ if mcdc_info. state . decision_ctx_stack . pop ( ) . is_none ( ) {
459
+ bug ! ( "Unexpected empty decision stack" ) ;
460
+ }
437
461
} ;
438
462
}
439
463
}
0 commit comments