Skip to content

Commit beb8a4a

Browse files
committed
coverage: Record branch information during MIR building
1 parent 8e35f81 commit beb8a4a

File tree

2 files changed

+117
-9
lines changed

2 files changed

+117
-9
lines changed

compiler/rustc_mir_build/src/build/coverageinfo.rs

Lines changed: 80 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
1-
use rustc_middle::mir;
2-
use rustc_middle::mir::coverage::BranchSpan;
1+
use rustc_middle::mir::coverage::{BlockMarkerId, BranchSpan, CoverageKind};
2+
use rustc_middle::mir::{self, BasicBlock, Statement, StatementKind, UnOp};
3+
use rustc_middle::thir::{ExprId, ExprKind};
34
use rustc_middle::ty::TyCtxt;
45
use rustc_span::def_id::LocalDefId;
56

7+
use crate::build::Builder;
8+
69
pub(crate) struct HirBranchInfoBuilder {
710
num_block_markers: usize,
811
branch_spans: Vec<BranchSpan>,
@@ -17,6 +20,12 @@ impl HirBranchInfoBuilder {
1720
}
1821
}
1922

23+
fn next_block_marker_id(&mut self) -> BlockMarkerId {
24+
let id = BlockMarkerId::from_usize(self.num_block_markers);
25+
self.num_block_markers += 1;
26+
id
27+
}
28+
2029
pub(crate) fn into_done(self) -> Option<Box<mir::coverage::HirBranchInfo>> {
2130
let Self { num_block_markers, branch_spans } = self;
2231

@@ -28,3 +37,72 @@ impl HirBranchInfoBuilder {
2837
Some(Box::new(mir::coverage::HirBranchInfo { num_block_markers, branch_spans }))
2938
}
3039
}
40+
41+
impl<'a, 'tcx> Builder<'a, 'tcx> {
42+
/// If branch coverage is enabled, inject marker statements into `then_block`
43+
/// and `else_block`, and record their IDs in the table of branch spans.
44+
pub(crate) fn coverage_record_branch(
45+
&mut self,
46+
cond_expr_id: ExprId,
47+
enclosing_not_expr_id: Option<ExprId>,
48+
then_block: BasicBlock,
49+
else_block: BasicBlock,
50+
) {
51+
// If branch coverage instrumentation isn't enabled, do nothing.
52+
if self.coverage_branch_info.is_none() {
53+
return;
54+
}
55+
56+
// If the condition is nested in an odd number of `!` expressions, we
57+
// need to reverse the meaning of the then/else block markers, so that
58+
// they correspond to the outermost `!` expression being true/false.
59+
let swap_arm_markers = {
60+
let mut curr = enclosing_not_expr_id.unwrap_or(cond_expr_id);
61+
let mut swap_arm_markers = false;
62+
63+
while curr != cond_expr_id {
64+
match self.thir[curr].kind {
65+
ExprKind::Scope { value, .. } => curr = value,
66+
ExprKind::Unary { op: UnOp::Not, arg } => {
67+
swap_arm_markers = !swap_arm_markers;
68+
curr = arg;
69+
}
70+
_ => unreachable!("ensured by `Builder::then_else_break_inner`"),
71+
}
72+
}
73+
swap_arm_markers
74+
};
75+
76+
let cond_source_info =
77+
self.source_info(self.thir[enclosing_not_expr_id.unwrap_or(cond_expr_id)].span);
78+
79+
let branch_info_builder =
80+
self.coverage_branch_info.as_mut().expect("confirmed present above");
81+
82+
let mut inject_branch_marker = |block: BasicBlock| {
83+
let id = branch_info_builder.next_block_marker_id();
84+
85+
let marker_statement = Statement {
86+
source_info: cond_source_info,
87+
kind: StatementKind::Coverage(Box::new(mir::Coverage {
88+
kind: CoverageKind::BlockMarker { id },
89+
})),
90+
};
91+
self.cfg.push(block, marker_statement);
92+
93+
id
94+
};
95+
96+
let mut true_marker = inject_branch_marker(then_block);
97+
let mut false_marker = inject_branch_marker(else_block);
98+
if swap_arm_markers {
99+
std::mem::swap(&mut true_marker, &mut false_marker);
100+
}
101+
102+
branch_info_builder.branch_spans.push(BranchSpan {
103+
span: cond_source_info.span,
104+
true_marker,
105+
false_marker,
106+
});
107+
}
108+
}

compiler/rustc_mir_build/src/build/matches/mod.rs

Lines changed: 37 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,30 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
3838
/// expressions will not be declared. This is for if let guards on arms with
3939
/// an or pattern, where the guard is lowered multiple times.
4040
pub(crate) fn then_else_break(
41+
&mut self,
42+
block: BasicBlock,
43+
expr_id: ExprId,
44+
temp_scope_override: Option<region::Scope>,
45+
break_scope: region::Scope,
46+
variable_source_info: SourceInfo,
47+
declare_bindings: bool,
48+
) -> BlockAnd<()> {
49+
self.then_else_break_inner(
50+
block,
51+
expr_id,
52+
None,
53+
temp_scope_override,
54+
break_scope,
55+
variable_source_info,
56+
declare_bindings,
57+
)
58+
}
59+
60+
fn then_else_break_inner(
4161
&mut self,
4262
mut block: BasicBlock,
4363
expr_id: ExprId,
64+
enclosing_not_expr_id: Option<ExprId>,
4465
temp_scope_override: Option<region::Scope>,
4566
break_scope: region::Scope,
4667
variable_source_info: SourceInfo,
@@ -52,18 +73,20 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
5273

5374
match expr.kind {
5475
ExprKind::LogicalOp { op: LogicalOp::And, lhs, rhs } => {
55-
let lhs_then_block = unpack!(this.then_else_break(
76+
let lhs_then_block = unpack!(this.then_else_break_inner(
5677
block,
5778
lhs,
79+
None,
5880
temp_scope_override,
5981
break_scope,
6082
variable_source_info,
6183
declare_bindings,
6284
));
6385

64-
let rhs_then_block = unpack!(this.then_else_break(
86+
let rhs_then_block = unpack!(this.then_else_break_inner(
6587
lhs_then_block,
6688
rhs,
89+
None,
6790
temp_scope_override,
6891
break_scope,
6992
variable_source_info,
@@ -76,18 +99,20 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
7699
let local_scope = this.local_scope();
77100
let (lhs_success_block, failure_block) =
78101
this.in_if_then_scope(local_scope, expr_span, |this| {
79-
this.then_else_break(
102+
this.then_else_break_inner(
80103
block,
81104
lhs,
105+
None,
82106
temp_scope_override,
83107
local_scope,
84108
variable_source_info,
85109
true,
86110
)
87111
});
88-
let rhs_success_block = unpack!(this.then_else_break(
112+
let rhs_success_block = unpack!(this.then_else_break_inner(
89113
failure_block,
90114
rhs,
115+
None,
91116
temp_scope_override,
92117
break_scope,
93118
variable_source_info,
@@ -108,9 +133,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
108133
if this.tcx.sess.instrument_coverage() {
109134
this.cfg.push_coverage_span_marker(block, this.source_info(expr_span));
110135
}
111-
this.then_else_break(
136+
this.then_else_break_inner(
112137
block,
113138
arg,
139+
enclosing_not_expr_id.or(Some(expr_id)),
114140
temp_scope_override,
115141
local_scope,
116142
variable_source_info,
@@ -123,19 +149,21 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
123149
ExprKind::Scope { region_scope, lint_level, value } => {
124150
let region_scope = (region_scope, this.source_info(expr_span));
125151
this.in_scope(region_scope, lint_level, |this| {
126-
this.then_else_break(
152+
this.then_else_break_inner(
127153
block,
128154
value,
155+
enclosing_not_expr_id,
129156
temp_scope_override,
130157
break_scope,
131158
variable_source_info,
132159
declare_bindings,
133160
)
134161
})
135162
}
136-
ExprKind::Use { source } => this.then_else_break(
163+
ExprKind::Use { source } => this.then_else_break_inner(
137164
block,
138165
source,
166+
None,
139167
temp_scope_override,
140168
break_scope,
141169
variable_source_info,
@@ -161,6 +189,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
161189
let else_block = this.cfg.start_new_block();
162190
let term = TerminatorKind::if_(operand, then_block, else_block);
163191

192+
this.coverage_record_branch(expr_id, enclosing_not_expr_id, then_block, else_block);
193+
164194
let source_info = this.source_info(expr_span);
165195
this.cfg.terminate(block, source_info, term);
166196
this.break_for_else(else_block, break_scope, source_info);

0 commit comments

Comments
 (0)