@@ -4,7 +4,7 @@ use std::collections::hash_map::Entry;
44use rustc_data_structures:: fx:: FxHashMap ;
55use rustc_index:: IndexVec ;
66use rustc_middle:: mir:: coverage:: {
7- BlockMarkerId , BranchSpan , CoverageKind , DecisionId , DecisionSpan ,
7+ BlockMarkerId , BranchSpan , ConditionId , CoverageKind , DecisionId , DecisionSpan ,
88} ;
99use rustc_middle:: mir:: { self , BasicBlock , UnOp } ;
1010use rustc_middle:: thir:: { ExprId , ExprKind , Thir } ;
@@ -15,14 +15,7 @@ use rustc_span::Span;
1515use crate :: build:: Builder ;
1616use crate :: errors:: MCDCNestedDecision ;
1717
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 {
2619 /// ID of the current decision.
2720 /// Do not use directly. Use the function instead, as it will hide
2821 /// the decision in the scope of nested decisions.
@@ -31,7 +24,54 @@ pub(crate) struct BranchInfoBuilder {
3124 /// nested decisions.
3225 nested_decision_level : u32 ,
3326 /// 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 > ,
3575}
3676
3777#[ derive( Clone , Copy ) ]
@@ -51,13 +91,21 @@ impl BranchInfoBuilder {
5191 if ( tcx. sess . instrument_coverage_branch ( ) || tcx. sess . instrument_coverage_mcdc ( ) )
5292 && tcx. is_eligible_for_coverage ( def_id)
5393 {
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+ } ;
54104 Some ( Self {
55105 nots : FxHashMap :: default ( ) ,
56106 num_block_markers : 0 ,
57107 branch_spans : vec ! [ ] ,
58- current_decision_id : None ,
59- nested_decision_level : 0 ,
60- decisions : IndexVec :: new ( ) ,
108+ mcdc_info,
61109 } )
62110 } else {
63111 None
@@ -111,15 +159,18 @@ impl BranchInfoBuilder {
111159 }
112160
113161 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 ;
115163
116164 if num_block_markers == 0 {
117165 assert ! ( branch_spans. is_empty( ) ) ;
118166 return None ;
119167 }
120168
121169 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 } ) ,
123174 ) ;
124175
125176 // Count the number of conditions linked to each decision.
@@ -135,32 +186,6 @@ impl BranchInfoBuilder {
135186 decision_spans,
136187 } ) )
137188 }
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- }
164189}
165190
166191impl Builder < ' _ , ' _ > {
@@ -206,7 +231,11 @@ impl Builder<'_, '_> {
206231 branch_info. branch_spans . push ( BranchSpan {
207232 span : source_info. span ,
208233 // 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 ) ) ,
210239 true_marker,
211240 false_marker,
212241 } ) ;
@@ -215,25 +244,24 @@ impl Builder<'_, '_> {
215244 /// If MCDC coverage is enabled, inject a decision entry marker in the given decision.
216245 /// return true
217246 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 {
223251 return ;
224252 } ;
225253
226254 let span = self . thir [ expr_id] . span ;
227255
228256 // 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
231259 debug ! ( "MCDC: Unsupported nested decision" ) ;
232260 self . tcx . dcx ( ) . emit_warn ( MCDCNestedDecision { span } ) ;
233261 return ;
234262 }
235263
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." ) ;
237265
238266 // Inject a decision marker
239267 let source_info = self . source_info ( span) ;
@@ -248,16 +276,15 @@ impl Builder<'_, '_> {
248276
249277 /// If MCDC is enabled, and function is instrumented,
250278 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 {
256283 return ;
257284 } ;
258285
259286 // Exit decision now so we can drop &mut to branch info
260- branch_info . exit_decision ( ) ;
287+ mcdc_info . exit_decision ( ) ;
261288 }
262289
263290 /// If MCDC is enabled and the current decision is being instrumented,
@@ -268,16 +295,19 @@ impl Builder<'_, '_> {
268295 bb : BasicBlock ,
269296 outcome : bool ,
270297 ) -> 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 {
273302 return bb;
274303 } ;
275- let Some ( decision_id) = branch_info. current_decision_id ( ) else {
304+
305+ let Some ( decision_id) = mcdc_info. current_decision_id ( ) else {
276306 // Decision is not instrumented
277307 return bb;
278308 } ;
279309
280- let span = branch_info . decisions [ decision_id] ;
310+ let span = mcdc_info . decision_spans [ decision_id] ;
281311 let source_info = self . source_info ( span) ;
282312 let marker_statement = mir:: Statement {
283313 source_info,
@@ -298,4 +328,49 @@ impl Builder<'_, '_> {
298328
299329 new_bb
300330 }
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+ }
301376}
0 commit comments