1
1
use std:: assert_matches:: assert_matches;
2
2
use std:: collections:: hash_map:: Entry ;
3
- use std:: collections:: VecDeque ;
3
+ use std:: collections:: { BTreeMap , VecDeque } ;
4
4
5
5
use rustc_data_structures:: fx:: FxHashMap ;
6
6
use rustc_middle:: mir:: coverage:: {
7
- BlockMarkerId , BranchSpan , ConditionId , ConditionInfo , CoverageKind , MCDCBranchSpan ,
8
- MCDCDecisionSpan ,
7
+ BlockMarkerId , BranchSpan , ConditionId , ConditionInfo , CoverageKind , MCDCBranchInfo ,
8
+ MCDCBranchSpan , MCDCDecisionSpan , MCDCMatchArmSpan ,
9
9
} ;
10
10
use rustc_middle:: mir:: { self , BasicBlock , SourceInfo , UnOp } ;
11
11
use rustc_middle:: thir:: { ExprId , ExprKind , LogicalOp , Thir } ;
@@ -23,9 +23,7 @@ pub(crate) struct BranchInfoBuilder {
23
23
num_block_markers : usize ,
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_builder : Option < MCDCBuilder > ,
29
27
}
30
28
31
29
#[ derive( Clone , Copy ) ]
@@ -47,9 +45,7 @@ impl BranchInfoBuilder {
47
45
nots : FxHashMap :: default ( ) ,
48
46
num_block_markers : 0 ,
49
47
branch_spans : vec ! [ ] ,
50
- mcdc_branch_spans : vec ! [ ] ,
51
- mcdc_decision_spans : vec ! [ ] ,
52
- mcdc_state : MCDCState :: new_if_enabled ( tcx) ,
48
+ mcdc_builder : MCDCBuilder :: new_if_enabled ( tcx) ,
53
49
} )
54
50
} else {
55
51
None
@@ -102,21 +98,21 @@ impl BranchInfoBuilder {
102
98
true_marker : BlockMarkerId ,
103
99
false_marker : BlockMarkerId ,
104
100
) -> Option < ConditionInfo > {
105
- let mcdc_state = self . mcdc_state . as_mut ( ) ?;
101
+ let mcdc_builder = self . mcdc_builder . as_mut ( ) ?;
106
102
let ( mut condition_info, decision_result) =
107
- mcdc_state . take_condition ( true_marker, false_marker) ;
103
+ mcdc_builder . take_condition ( true_marker, false_marker) ;
108
104
if let Some ( decision) = decision_result {
109
105
match decision. conditions_num {
110
106
0 => {
111
107
unreachable ! ( "Decision with no condition is not expected" ) ;
112
108
}
113
109
1 ..=MAX_CONDITIONS_NUM_IN_DECISION => {
114
- self . mcdc_decision_spans . push ( decision) ;
110
+ mcdc_builder . decision_spans . push ( decision) ;
115
111
}
116
112
_ => {
117
113
// Do not generate mcdc mappings and statements for decisions with too many conditions.
118
- let rebase_idx = self . mcdc_branch_spans . len ( ) - decision. conditions_num + 1 ;
119
- for branch in & mut self . mcdc_branch_spans [ rebase_idx..] {
114
+ let rebase_idx = mcdc_builder . branch_spans . len ( ) - decision. conditions_num + 1 ;
115
+ for branch in & mut mcdc_builder . branch_spans [ rebase_idx..] {
120
116
branch. condition_info = None ;
121
117
}
122
118
@@ -171,14 +167,7 @@ impl BranchInfoBuilder {
171
167
}
172
168
173
169
pub ( crate ) fn into_done ( self ) -> Option < Box < mir:: coverage:: BranchInfo > > {
174
- let Self {
175
- nots : _,
176
- num_block_markers,
177
- branch_spans,
178
- mcdc_branch_spans,
179
- mcdc_decision_spans,
180
- mcdc_state : _,
181
- } = self ;
170
+ let Self { nots : _, num_block_markers, branch_spans, mcdc_builder } = self ;
182
171
183
172
if num_block_markers == 0 {
184
173
assert ! ( branch_spans. is_empty( ) ) ;
@@ -188,8 +177,7 @@ impl BranchInfoBuilder {
188
177
Some ( Box :: new ( mir:: coverage:: BranchInfo {
189
178
num_block_markers,
190
179
branch_spans,
191
- mcdc_branch_spans,
192
- mcdc_decision_spans,
180
+ mcdc_info : mcdc_builder. map ( MCDCBuilder :: into_done) ,
193
181
} ) )
194
182
}
195
183
}
@@ -199,17 +187,25 @@ impl BranchInfoBuilder {
199
187
/// This limit may be relaxed if the [upstream change](https://github.com/llvm/llvm-project/pull/82448) is merged.
200
188
const MAX_CONDITIONS_NUM_IN_DECISION : usize = 6 ;
201
189
202
- struct MCDCState {
190
+ struct MCDCBuilder {
203
191
/// To construct condition evaluation tree.
204
192
decision_stack : VecDeque < ConditionInfo > ,
205
193
processing_decision : Option < MCDCDecisionSpan > ,
194
+
195
+ match_arm_records : BTreeMap < Span , MCDCMatchArmSpan > ,
196
+ branch_spans : Vec < MCDCBranchSpan > ,
197
+ decision_spans : Vec < MCDCDecisionSpan > ,
206
198
}
207
199
208
- impl MCDCState {
200
+ impl MCDCBuilder {
209
201
fn new_if_enabled ( tcx : TyCtxt < ' _ > ) -> Option < Self > {
210
- tcx. sess
211
- . instrument_coverage_mcdc ( )
212
- . then ( || Self { decision_stack : VecDeque :: new ( ) , processing_decision : None } )
202
+ tcx. sess . instrument_coverage_mcdc ( ) . then ( || Self {
203
+ decision_stack : VecDeque :: new ( ) ,
204
+ processing_decision : None ,
205
+ match_arm_records : BTreeMap :: new ( ) ,
206
+ branch_spans : vec ! [ ] ,
207
+ decision_spans : vec ! [ ] ,
208
+ } )
213
209
}
214
210
215
211
// At first we assign ConditionIds for each sub expression.
@@ -333,6 +329,23 @@ impl MCDCState {
333
329
( Some ( condition_info) , None )
334
330
}
335
331
}
332
+
333
+ fn into_done ( self ) -> MCDCBranchInfo {
334
+ let Self { match_arm_records, branch_spans, decision_spans, .. } = self ;
335
+
336
+ let mut match_arm_spans = match_arm_records. into_values ( ) . collect :: < Vec < _ > > ( ) ;
337
+ match_arm_spans. sort_by ( |a, b| {
338
+ if a. span . contains ( b. span ) { std:: cmp:: Ordering :: Less } else { a. span . cmp ( & b. span ) }
339
+ } ) ;
340
+ for idx in 0 ..match_arm_spans. len ( ) . saturating_sub ( 1 ) {
341
+ if match_arm_spans[ idx] . span . contains ( match_arm_spans[ idx + 1 ] . span ) {
342
+ let refined_span = match_arm_spans[ idx] . span . until ( match_arm_spans[ idx + 1 ] . span ) ;
343
+ match_arm_spans[ idx] . span = refined_span;
344
+ }
345
+ }
346
+
347
+ MCDCBranchInfo { branch_spans, match_arm_spans, decision_spans }
348
+ }
336
349
}
337
350
338
351
impl Builder < ' _ , ' _ > {
@@ -359,19 +372,21 @@ impl Builder<'_, '_> {
359
372
let source_info = SourceInfo { span : self . thir [ expr_id] . span , scope : self . source_scope } ;
360
373
361
374
// Separate path for handling branches when MC/DC is enabled.
362
- if branch_info. mcdc_state . is_some ( ) {
375
+ if branch_info. mcdc_builder . is_some ( ) {
363
376
let mut inject_block_marker =
364
377
|block| branch_info. inject_block_marker ( & mut self . cfg , source_info, block) ;
365
378
let true_marker = inject_block_marker ( then_block) ;
366
379
let false_marker = inject_block_marker ( else_block) ;
367
380
let condition_info =
368
381
branch_info. fetch_mcdc_condition_info ( self . tcx , true_marker, false_marker) ;
369
- branch_info. mcdc_branch_spans . push ( MCDCBranchSpan {
370
- span : source_info. span ,
371
- condition_info,
372
- true_marker,
373
- false_marker,
374
- } ) ;
382
+ branch_info. mcdc_builder . as_mut ( ) . expect ( "checked at condition" ) . branch_spans . push (
383
+ MCDCBranchSpan {
384
+ span : source_info. span ,
385
+ condition_info,
386
+ true_marker,
387
+ false_marker,
388
+ } ,
389
+ ) ;
375
390
return ;
376
391
}
377
392
@@ -380,9 +395,54 @@ impl Builder<'_, '_> {
380
395
381
396
pub ( crate ) fn visit_coverage_branch_operation ( & mut self , logical_op : LogicalOp , span : Span ) {
382
397
if let Some ( branch_info) = self . coverage_branch_info . as_mut ( )
383
- && let Some ( mcdc_state) = branch_info. mcdc_state . as_mut ( )
398
+ && let Some ( mcdc_state) = branch_info. mcdc_builder . as_mut ( )
384
399
{
385
400
mcdc_state. record_conditions ( logical_op, span) ;
386
401
}
387
402
}
403
+
404
+ pub ( crate ) fn visit_coverage_mcdc_match_arms (
405
+ & mut self ,
406
+ ( test_span, test_block) : ( Span , BasicBlock ) ,
407
+ targets : impl Iterator < Item = ( Span , BasicBlock ) > ,
408
+ ) {
409
+ let Some ( branch_info) = self . coverage_branch_info . as_mut ( ) else { return } ;
410
+ // Only enabled in mcdc
411
+ if branch_info. mcdc_builder . is_none ( ) {
412
+ return ;
413
+ }
414
+
415
+ // May inject redundant block markers, it's ok because we will transform them into basic coverage block in coverage pass.
416
+ let mut gen_marker_id = |span, block| {
417
+ branch_info. inject_block_marker (
418
+ & mut self . cfg ,
419
+ SourceInfo { span, scope : self . source_scope } ,
420
+ block,
421
+ )
422
+ } ;
423
+
424
+ let test_block_id = gen_marker_id ( test_span, test_block) ;
425
+
426
+ let mut targets =
427
+ targets. map ( |( span, block) | ( span, gen_marker_id ( span, block) ) ) . collect :: < Vec < _ > > ( ) ;
428
+
429
+ let branch_records =
430
+ & mut branch_info. mcdc_builder . as_mut ( ) . expect ( "checked at entry" ) . match_arm_records ;
431
+
432
+ while let Some ( ( span, matched_block) ) = targets. pop ( ) {
433
+ // By here we do not know the span of the target yet, so just record the
434
+ // basic blocks and insert BlockMarkerId later.
435
+ let record = branch_records. entry ( span) . or_insert_with ( || MCDCMatchArmSpan {
436
+ span,
437
+ condition_info : None ,
438
+ test_markers : vec ! [ ] ,
439
+ true_markers : vec ! [ ] ,
440
+ prior_binding_markers : vec ! [ ] ,
441
+ } ) ;
442
+ record. test_markers . push ( test_block_id) ;
443
+ record. true_markers . push ( matched_block) ;
444
+
445
+ record. prior_binding_markers . extend ( targets. iter ( ) . map ( |( _, blk) | * blk) ) ;
446
+ }
447
+ }
388
448
}
0 commit comments