@@ -101,10 +101,14 @@ impl BranchInfoBuilder {
101
101
tcx : TyCtxt < ' _ > ,
102
102
true_marker : BlockMarkerId ,
103
103
false_marker : BlockMarkerId ,
104
- ) -> Option < ConditionInfo > {
104
+ ) -> Option < ( u16 , ConditionInfo ) > {
105
105
let mcdc_state = self . mcdc_state . as_mut ( ) ?;
106
+ let decision_depth = mcdc_state. decision_depth ( ) ;
106
107
let ( mut condition_info, decision_result) =
107
108
mcdc_state. take_condition ( true_marker, false_marker) ;
109
+ // take_condition() returns Some for decision_result when the decision stack
110
+ // is empty, i.e. when all the conditions of the decision were instrumented,
111
+ // and the decision is "complete".
108
112
if let Some ( decision) = decision_result {
109
113
match decision. conditions_num {
110
114
0 => {
@@ -131,7 +135,7 @@ impl BranchInfoBuilder {
131
135
}
132
136
}
133
137
}
134
- condition_info
138
+ condition_info. map ( |cond_info| ( decision_depth , cond_info ) )
135
139
}
136
140
137
141
fn add_two_way_branch < ' tcx > (
@@ -199,17 +203,32 @@ impl BranchInfoBuilder {
199
203
/// This limit may be relaxed if the [upstream change](https://github.com/llvm/llvm-project/pull/82448) is merged.
200
204
const MAX_CONDITIONS_NUM_IN_DECISION : usize = 6 ;
201
205
202
- struct MCDCState {
206
+ #[ derive( Default ) ]
207
+ struct MCDCDecisionCtx {
203
208
/// To construct condition evaluation tree.
204
209
decision_stack : VecDeque < ConditionInfo > ,
205
210
processing_decision : Option < MCDCDecisionSpan > ,
206
211
}
207
212
213
+ struct MCDCState {
214
+ decision_ctx_stack : Vec < MCDCDecisionCtx > ,
215
+ }
216
+
208
217
impl MCDCState {
209
218
fn new_if_enabled ( tcx : TyCtxt < ' _ > ) -> Option < Self > {
210
219
tcx. sess
211
220
. instrument_coverage_mcdc ( )
212
- . then ( || Self { decision_stack : VecDeque :: new ( ) , processing_decision : None } )
221
+ . then ( || Self { decision_ctx_stack : vec ! [ MCDCDecisionCtx :: default ( ) ] } )
222
+ }
223
+
224
+ /// Decision depth is given as a u16 to reduce the size of the `CoverageKind`,
225
+ /// as it is very unlikely that the depth ever reaches 2^16.
226
+ #[ inline]
227
+ fn decision_depth ( & self ) -> u16 {
228
+ u16:: try_from (
229
+ self . decision_ctx_stack . len ( ) . checked_sub ( 1 ) . expect ( "Unexpected empty decision stack" ) ,
230
+ )
231
+ . expect ( "decision depth did not fit in u16, this is likely to be an instrumentation error" )
213
232
}
214
233
215
234
// At first we assign ConditionIds for each sub expression.
@@ -253,20 +272,23 @@ impl MCDCState {
253
272
// - If the op is AND, the "false_next" of LHS and RHS should be the parent's "false_next". While "true_next" of the LHS is the RHS, the "true next" of RHS is the parent's "true_next".
254
273
// - 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".
255
274
fn record_conditions ( & mut self , op : LogicalOp , span : Span ) {
256
- let decision = match self . processing_decision . as_mut ( ) {
275
+ let decision_depth = self . decision_depth ( ) ;
276
+ let decision_ctx =
277
+ self . decision_ctx_stack . last_mut ( ) . expect ( "Unexpected empty decision_ctx_stack" ) ;
278
+ let decision = match decision_ctx. processing_decision . as_mut ( ) {
257
279
Some ( decision) => {
258
280
decision. span = decision. span . to ( span) ;
259
281
decision
260
282
}
261
- None => self . processing_decision . insert ( MCDCDecisionSpan {
283
+ None => decision_ctx . processing_decision . insert ( MCDCDecisionSpan {
262
284
span,
263
285
conditions_num : 0 ,
264
286
end_markers : vec ! [ ] ,
265
- decision_depth : 0 ,
287
+ decision_depth,
266
288
} ) ,
267
289
} ;
268
290
269
- let parent_condition = self . decision_stack . pop_back ( ) . unwrap_or_default ( ) ;
291
+ let parent_condition = decision_ctx . decision_stack . pop_back ( ) . unwrap_or_default ( ) ;
270
292
let lhs_id = if parent_condition. condition_id == ConditionId :: NONE {
271
293
decision. conditions_num += 1 ;
272
294
ConditionId :: from ( decision. conditions_num )
@@ -306,19 +328,21 @@ impl MCDCState {
306
328
}
307
329
} ;
308
330
// We visit expressions tree in pre-order, so place the left-hand side on the top.
309
- self . decision_stack . push_back ( rhs) ;
310
- self . decision_stack . push_back ( lhs) ;
331
+ decision_ctx . decision_stack . push_back ( rhs) ;
332
+ decision_ctx . decision_stack . push_back ( lhs) ;
311
333
}
312
334
313
335
fn take_condition (
314
336
& mut self ,
315
337
true_marker : BlockMarkerId ,
316
338
false_marker : BlockMarkerId ,
317
339
) -> ( Option < ConditionInfo > , Option < MCDCDecisionSpan > ) {
318
- let Some ( condition_info) = self . decision_stack . pop_back ( ) else {
340
+ let decision_ctx =
341
+ self . decision_ctx_stack . last_mut ( ) . expect ( "Unexpected empty decision_ctx_stack" ) ;
342
+ let Some ( condition_info) = decision_ctx. decision_stack . pop_back ( ) else {
319
343
return ( None , None ) ;
320
344
} ;
321
- let Some ( decision) = self . processing_decision . as_mut ( ) else {
345
+ let Some ( decision) = decision_ctx . processing_decision . as_mut ( ) else {
322
346
bug ! ( "Processing decision should have been created before any conditions are taken" ) ;
323
347
} ;
324
348
if condition_info. true_next_id == ConditionId :: NONE {
@@ -328,8 +352,8 @@ impl MCDCState {
328
352
decision. end_markers . push ( false_marker) ;
329
353
}
330
354
331
- if self . decision_stack . is_empty ( ) {
332
- ( Some ( condition_info) , self . processing_decision . take ( ) )
355
+ if decision_ctx . decision_stack . is_empty ( ) {
356
+ ( Some ( condition_info) , decision_ctx . processing_decision . take ( ) )
333
357
} else {
334
358
( Some ( condition_info) , None )
335
359
}
@@ -365,14 +389,17 @@ impl Builder<'_, '_> {
365
389
|block| branch_info. inject_block_marker ( & mut self . cfg , source_info, block) ;
366
390
let true_marker = inject_block_marker ( then_block) ;
367
391
let false_marker = inject_block_marker ( else_block) ;
368
- let condition_info =
369
- branch_info. fetch_mcdc_condition_info ( self . tcx , true_marker, false_marker) ;
392
+ let ( decision_depth, condition_info) = branch_info
393
+ . fetch_mcdc_condition_info ( self . tcx , true_marker, false_marker)
394
+ . map_or ( ( 0 , None ) , |( decision_depth, condition_info) | {
395
+ ( decision_depth, Some ( condition_info) )
396
+ } ) ;
370
397
branch_info. mcdc_branch_spans . push ( MCDCBranchSpan {
371
398
span : source_info. span ,
372
399
condition_info,
373
400
true_marker,
374
401
false_marker,
375
- decision_depth : 0 ,
402
+ decision_depth,
376
403
} ) ;
377
404
return ;
378
405
}
@@ -387,4 +414,20 @@ impl Builder<'_, '_> {
387
414
mcdc_state. record_conditions ( logical_op, span) ;
388
415
}
389
416
}
417
+
418
+ pub ( crate ) fn mcdc_increment_depth_if_enabled ( & mut self ) {
419
+ if let Some ( branch_info) = self . coverage_branch_info . as_mut ( )
420
+ && let Some ( mcdc_state) = branch_info. mcdc_state . as_mut ( )
421
+ {
422
+ mcdc_state. decision_ctx_stack . push ( MCDCDecisionCtx :: default ( ) ) ;
423
+ } ;
424
+ }
425
+
426
+ pub ( crate ) fn mcdc_decrement_depth_if_enabled ( & mut self ) {
427
+ if let Some ( branch_info) = self . coverage_branch_info . as_mut ( )
428
+ && let Some ( mcdc_state) = branch_info. mcdc_state . as_mut ( )
429
+ {
430
+ mcdc_state. decision_ctx_stack . pop ( ) . expect ( "Unexpected empty decision stack" ) ;
431
+ } ;
432
+ }
390
433
}
0 commit comments