Skip to content

Commit 35a8746

Browse files
committed
coverage: Instrument the RHS value of lazy logical operators
When a lazy logical operator (`&&` or `||`) occurs outside of an `if` condition, it normally doesn't have any associated control-flow branch, so we don't have an existing way to track whether it was true or false. This patch adds special code to handle this case, by inserting extra MIR blocks in a diamond shape after evaluating the RHS. This gives us a place to insert the appropriate marker statements, which can then be given their own counters.
1 parent 20174e6 commit 35a8746

File tree

4 files changed

+148
-47
lines changed

4 files changed

+148
-47
lines changed

compiler/rustc_mir_build/src/build/coverageinfo.rs

+57
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,63 @@ impl BranchInfoBuilder {
157157
}
158158

159159
impl<'tcx> Builder<'_, 'tcx> {
160+
/// If condition coverage is enabled, inject extra blocks and marker statements
161+
/// that will let us track the value of the condition in `place`.
162+
pub(crate) fn visit_coverage_standalone_condition(
163+
&mut self,
164+
mut expr_id: ExprId, // Expression giving the span of the condition
165+
place: mir::Place<'tcx>, // Already holds the boolean condition value
166+
block: &mut BasicBlock,
167+
) {
168+
// Bail out if condition coverage is not enabled for this function.
169+
let Some(branch_info) = self.coverage_branch_info.as_mut() else { return };
170+
if !self.tcx.sess.instrument_coverage_condition() {
171+
return;
172+
};
173+
174+
// Remove any wrappers, so that we can inspect the real underlying expression.
175+
while let ExprKind::Use { source: inner } | ExprKind::Scope { value: inner, .. } =
176+
self.thir[expr_id].kind
177+
{
178+
expr_id = inner;
179+
}
180+
// If the expression is a lazy logical op, it will naturally get branch
181+
// coverage as part of its normal lowering, so we can disregard it here.
182+
if let ExprKind::LogicalOp { .. } = self.thir[expr_id].kind {
183+
return;
184+
}
185+
186+
let source_info = SourceInfo { span: self.thir[expr_id].span, scope: self.source_scope };
187+
188+
// Using the boolean value that has already been stored in `place`, set up
189+
// control flow in the shape of a diamond, so that we can place separate
190+
// marker statements in the true and false blocks. The coverage MIR pass
191+
// will use those markers to inject coverage counters as appropriate.
192+
//
193+
// block
194+
// / \
195+
// true_block false_block
196+
// (marker) (marker)
197+
// \ /
198+
// join_block
199+
200+
let true_block = self.cfg.start_new_block();
201+
let false_block = self.cfg.start_new_block();
202+
self.cfg.terminate(
203+
*block,
204+
source_info,
205+
mir::TerminatorKind::if_(mir::Operand::Copy(place), true_block, false_block),
206+
);
207+
208+
branch_info.add_two_way_branch(&mut self.cfg, source_info, true_block, false_block);
209+
210+
let join_block = self.cfg.start_new_block();
211+
self.cfg.goto(true_block, source_info, join_block);
212+
self.cfg.goto(false_block, source_info, join_block);
213+
// Any subsequent codegen in the caller should use the new join block.
214+
*block = join_block;
215+
}
216+
160217
/// If branch coverage is enabled, inject marker statements into `then_block`
161218
/// and `else_block`, and record their IDs in the table of branch spans.
162219
pub(crate) fn visit_coverage_branch_condition(

compiler/rustc_mir_build/src/build/expr/into.rs

+6-2
Original file line numberDiff line numberDiff line change
@@ -183,9 +183,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
183183
const_: Const::from_bool(this.tcx, constant),
184184
},
185185
);
186-
let rhs = unpack!(this.expr_into_dest(destination, continuation, rhs));
186+
let mut rhs_block = unpack!(this.expr_into_dest(destination, continuation, rhs));
187+
// Instrument the lowered RHS's value for condition coverage.
188+
// (Does nothing if condition coverage is not enabled.)
189+
this.visit_coverage_standalone_condition(rhs, destination, &mut rhs_block);
190+
187191
let target = this.cfg.start_new_block();
188-
this.cfg.goto(rhs, source_info, target);
192+
this.cfg.goto(rhs_block, source_info, target);
189193
this.cfg.goto(short_circuit, source_info, target);
190194
target.unit()
191195
}

tests/coverage/condition/conditions.cov-map

+80-45
Original file line numberDiff line numberDiff line change
@@ -1,89 +1,118 @@
11
Function name: conditions::assign_3_and_or
2-
Raw bytes (60): 0x[01, 01, 06, 0d, 13, 09, 16, 01, 05, 01, 05, 09, 16, 01, 05, 08, 01, 1c, 01, 00, 2f, 03, 01, 09, 00, 0a, 01, 00, 0d, 00, 0e, 20, 05, 16, 00, 0d, 00, 0e, 05, 00, 12, 00, 13, 20, 0d, 09, 00, 12, 00, 13, 13, 00, 17, 00, 18, 03, 01, 05, 01, 02]
2+
Raw bytes (69): 0x[01, 01, 07, 07, 11, 09, 0d, 01, 05, 05, 09, 16, 1a, 05, 09, 01, 05, 09, 01, 1c, 01, 00, 2f, 03, 01, 09, 00, 0a, 01, 00, 0d, 00, 0e, 20, 05, 1a, 00, 0d, 00, 0e, 05, 00, 12, 00, 13, 20, 09, 16, 00, 12, 00, 13, 13, 00, 17, 00, 18, 20, 0d, 11, 00, 17, 00, 18, 03, 01, 05, 01, 02]
33
Number of files: 1
44
- file 0 => global file 1
5-
Number of expressions: 6
6-
- expression 0 operands: lhs = Counter(3), rhs = Expression(4, Add)
7-
- expression 1 operands: lhs = Counter(2), rhs = Expression(5, Sub)
5+
Number of expressions: 7
6+
- expression 0 operands: lhs = Expression(1, Add), rhs = Counter(4)
7+
- expression 1 operands: lhs = Counter(2), rhs = Counter(3)
88
- expression 2 operands: lhs = Counter(0), rhs = Counter(1)
9-
- expression 3 operands: lhs = Counter(0), rhs = Counter(1)
10-
- expression 4 operands: lhs = Counter(2), rhs = Expression(5, Sub)
11-
- expression 5 operands: lhs = Counter(0), rhs = Counter(1)
12-
Number of file 0 mappings: 8
9+
- expression 3 operands: lhs = Counter(1), rhs = Counter(2)
10+
- expression 4 operands: lhs = Expression(5, Sub), rhs = Expression(6, Sub)
11+
- expression 5 operands: lhs = Counter(1), rhs = Counter(2)
12+
- expression 6 operands: lhs = Counter(0), rhs = Counter(1)
13+
Number of file 0 mappings: 9
1314
- Code(Counter(0)) at (prev + 28, 1) to (start + 0, 47)
1415
- Code(Expression(0, Add)) at (prev + 1, 9) to (start + 0, 10)
15-
= (c3 + (c2 + (c0 - c1)))
16+
= ((c2 + c3) + c4)
1617
- Code(Counter(0)) at (prev + 0, 13) to (start + 0, 14)
17-
- Branch { true: Counter(1), false: Expression(5, Sub) } at (prev + 0, 13) to (start + 0, 14)
18+
- Branch { true: Counter(1), false: Expression(6, Sub) } at (prev + 0, 13) to (start + 0, 14)
1819
true = c1
1920
false = (c0 - c1)
2021
- Code(Counter(1)) at (prev + 0, 18) to (start + 0, 19)
21-
- Branch { true: Counter(3), false: Counter(2) } at (prev + 0, 18) to (start + 0, 19)
22-
true = c3
23-
false = c2
22+
- Branch { true: Counter(2), false: Expression(5, Sub) } at (prev + 0, 18) to (start + 0, 19)
23+
true = c2
24+
false = (c1 - c2)
2425
- Code(Expression(4, Add)) at (prev + 0, 23) to (start + 0, 24)
25-
= (c2 + (c0 - c1))
26+
= ((c1 - c2) + (c0 - c1))
27+
- Branch { true: Counter(3), false: Counter(4) } at (prev + 0, 23) to (start + 0, 24)
28+
true = c3
29+
false = c4
2630
- Code(Expression(0, Add)) at (prev + 1, 5) to (start + 1, 2)
27-
= (c3 + (c2 + (c0 - c1)))
31+
= ((c2 + c3) + c4)
2832

2933
Function name: conditions::assign_3_or_and
30-
Raw bytes (56): 0x[01, 01, 04, 05, 07, 09, 0d, 01, 05, 01, 05, 08, 01, 17, 01, 00, 2f, 03, 01, 09, 00, 0a, 01, 00, 0d, 00, 0e, 20, 05, 0e, 00, 0d, 00, 0e, 0e, 00, 12, 00, 13, 20, 09, 0d, 00, 12, 00, 13, 09, 00, 17, 00, 18, 03, 01, 05, 01, 02]
34+
Raw bytes (73): 0x[01, 01, 09, 05, 07, 0b, 11, 09, 0d, 01, 05, 01, 05, 22, 11, 01, 05, 22, 11, 01, 05, 09, 01, 17, 01, 00, 2f, 03, 01, 09, 00, 0a, 01, 00, 0d, 00, 0e, 20, 05, 22, 00, 0d, 00, 0e, 22, 00, 12, 00, 13, 20, 1e, 11, 00, 12, 00, 13, 1e, 00, 17, 00, 18, 20, 09, 0d, 00, 17, 00, 18, 03, 01, 05, 01, 02]
3135
Number of files: 1
3236
- file 0 => global file 1
33-
Number of expressions: 4
37+
Number of expressions: 9
3438
- expression 0 operands: lhs = Counter(1), rhs = Expression(1, Add)
35-
- expression 1 operands: lhs = Counter(2), rhs = Counter(3)
36-
- expression 2 operands: lhs = Counter(0), rhs = Counter(1)
39+
- expression 1 operands: lhs = Expression(2, Add), rhs = Counter(4)
40+
- expression 2 operands: lhs = Counter(2), rhs = Counter(3)
3741
- expression 3 operands: lhs = Counter(0), rhs = Counter(1)
38-
Number of file 0 mappings: 8
42+
- expression 4 operands: lhs = Counter(0), rhs = Counter(1)
43+
- expression 5 operands: lhs = Expression(8, Sub), rhs = Counter(4)
44+
- expression 6 operands: lhs = Counter(0), rhs = Counter(1)
45+
- expression 7 operands: lhs = Expression(8, Sub), rhs = Counter(4)
46+
- expression 8 operands: lhs = Counter(0), rhs = Counter(1)
47+
Number of file 0 mappings: 9
3948
- Code(Counter(0)) at (prev + 23, 1) to (start + 0, 47)
4049
- Code(Expression(0, Add)) at (prev + 1, 9) to (start + 0, 10)
41-
= (c1 + (c2 + c3))
50+
= (c1 + ((c2 + c3) + c4))
4251
- Code(Counter(0)) at (prev + 0, 13) to (start + 0, 14)
43-
- Branch { true: Counter(1), false: Expression(3, Sub) } at (prev + 0, 13) to (start + 0, 14)
52+
- Branch { true: Counter(1), false: Expression(8, Sub) } at (prev + 0, 13) to (start + 0, 14)
4453
true = c1
4554
false = (c0 - c1)
46-
- Code(Expression(3, Sub)) at (prev + 0, 18) to (start + 0, 19)
55+
- Code(Expression(8, Sub)) at (prev + 0, 18) to (start + 0, 19)
4756
= (c0 - c1)
48-
- Branch { true: Counter(2), false: Counter(3) } at (prev + 0, 18) to (start + 0, 19)
57+
- Branch { true: Expression(7, Sub), false: Counter(4) } at (prev + 0, 18) to (start + 0, 19)
58+
true = ((c0 - c1) - c4)
59+
false = c4
60+
- Code(Expression(7, Sub)) at (prev + 0, 23) to (start + 0, 24)
61+
= ((c0 - c1) - c4)
62+
- Branch { true: Counter(2), false: Counter(3) } at (prev + 0, 23) to (start + 0, 24)
4963
true = c2
5064
false = c3
51-
- Code(Counter(2)) at (prev + 0, 23) to (start + 0, 24)
5265
- Code(Expression(0, Add)) at (prev + 1, 5) to (start + 1, 2)
53-
= (c1 + (c2 + c3))
66+
= (c1 + ((c2 + c3) + c4))
5467

5568
Function name: conditions::assign_and
56-
Raw bytes (38): 0x[01, 01, 01, 01, 05, 06, 01, 0d, 01, 00, 21, 01, 01, 09, 00, 0a, 01, 00, 0d, 00, 0e, 20, 05, 02, 00, 0d, 00, 0e, 05, 00, 12, 00, 13, 01, 01, 05, 01, 02]
69+
Raw bytes (51): 0x[01, 01, 04, 07, 0e, 09, 0d, 01, 05, 01, 05, 07, 01, 0d, 01, 00, 21, 03, 01, 09, 00, 0a, 01, 00, 0d, 00, 0e, 20, 05, 0e, 00, 0d, 00, 0e, 05, 00, 12, 00, 13, 20, 09, 0d, 00, 12, 00, 13, 03, 01, 05, 01, 02]
5770
Number of files: 1
5871
- file 0 => global file 1
59-
Number of expressions: 1
60-
- expression 0 operands: lhs = Counter(0), rhs = Counter(1)
61-
Number of file 0 mappings: 6
72+
Number of expressions: 4
73+
- expression 0 operands: lhs = Expression(1, Add), rhs = Expression(3, Sub)
74+
- expression 1 operands: lhs = Counter(2), rhs = Counter(3)
75+
- expression 2 operands: lhs = Counter(0), rhs = Counter(1)
76+
- expression 3 operands: lhs = Counter(0), rhs = Counter(1)
77+
Number of file 0 mappings: 7
6278
- Code(Counter(0)) at (prev + 13, 1) to (start + 0, 33)
63-
- Code(Counter(0)) at (prev + 1, 9) to (start + 0, 10)
79+
- Code(Expression(0, Add)) at (prev + 1, 9) to (start + 0, 10)
80+
= ((c2 + c3) + (c0 - c1))
6481
- Code(Counter(0)) at (prev + 0, 13) to (start + 0, 14)
65-
- Branch { true: Counter(1), false: Expression(0, Sub) } at (prev + 0, 13) to (start + 0, 14)
82+
- Branch { true: Counter(1), false: Expression(3, Sub) } at (prev + 0, 13) to (start + 0, 14)
6683
true = c1
6784
false = (c0 - c1)
6885
- Code(Counter(1)) at (prev + 0, 18) to (start + 0, 19)
69-
- Code(Counter(0)) at (prev + 1, 5) to (start + 1, 2)
86+
- Branch { true: Counter(2), false: Counter(3) } at (prev + 0, 18) to (start + 0, 19)
87+
true = c2
88+
false = c3
89+
- Code(Expression(0, Add)) at (prev + 1, 5) to (start + 1, 2)
90+
= ((c2 + c3) + (c0 - c1))
7091

7192
Function name: conditions::assign_or
72-
Raw bytes (38): 0x[01, 01, 01, 01, 05, 06, 01, 12, 01, 00, 20, 01, 01, 09, 00, 0a, 01, 00, 0d, 00, 0e, 20, 05, 02, 00, 0d, 00, 0e, 02, 00, 12, 00, 13, 01, 01, 05, 01, 02]
93+
Raw bytes (51): 0x[01, 01, 04, 07, 0d, 05, 09, 01, 05, 01, 05, 07, 01, 12, 01, 00, 20, 03, 01, 09, 00, 0a, 01, 00, 0d, 00, 0e, 20, 05, 0e, 00, 0d, 00, 0e, 0e, 00, 12, 00, 13, 20, 09, 0d, 00, 12, 00, 13, 03, 01, 05, 01, 02]
7394
Number of files: 1
7495
- file 0 => global file 1
75-
Number of expressions: 1
76-
- expression 0 operands: lhs = Counter(0), rhs = Counter(1)
77-
Number of file 0 mappings: 6
96+
Number of expressions: 4
97+
- expression 0 operands: lhs = Expression(1, Add), rhs = Counter(3)
98+
- expression 1 operands: lhs = Counter(1), rhs = Counter(2)
99+
- expression 2 operands: lhs = Counter(0), rhs = Counter(1)
100+
- expression 3 operands: lhs = Counter(0), rhs = Counter(1)
101+
Number of file 0 mappings: 7
78102
- Code(Counter(0)) at (prev + 18, 1) to (start + 0, 32)
79-
- Code(Counter(0)) at (prev + 1, 9) to (start + 0, 10)
103+
- Code(Expression(0, Add)) at (prev + 1, 9) to (start + 0, 10)
104+
= ((c1 + c2) + c3)
80105
- Code(Counter(0)) at (prev + 0, 13) to (start + 0, 14)
81-
- Branch { true: Counter(1), false: Expression(0, Sub) } at (prev + 0, 13) to (start + 0, 14)
106+
- Branch { true: Counter(1), false: Expression(3, Sub) } at (prev + 0, 13) to (start + 0, 14)
82107
true = c1
83108
false = (c0 - c1)
84-
- Code(Expression(0, Sub)) at (prev + 0, 18) to (start + 0, 19)
109+
- Code(Expression(3, Sub)) at (prev + 0, 18) to (start + 0, 19)
85110
= (c0 - c1)
86-
- Code(Counter(0)) at (prev + 1, 5) to (start + 1, 2)
111+
- Branch { true: Counter(2), false: Counter(3) } at (prev + 0, 18) to (start + 0, 19)
112+
true = c2
113+
false = c3
114+
- Code(Expression(0, Add)) at (prev + 1, 5) to (start + 1, 2)
115+
= ((c1 + c2) + c3)
87116

88117
Function name: conditions::foo
89118
Raw bytes (9): 0x[01, 01, 00, 01, 01, 21, 01, 02, 02]
@@ -94,18 +123,24 @@ Number of file 0 mappings: 1
94123
- Code(Counter(0)) at (prev + 33, 1) to (start + 2, 2)
95124

96125
Function name: conditions::func_call
97-
Raw bytes (28): 0x[01, 01, 01, 01, 05, 04, 01, 25, 01, 01, 0a, 20, 05, 02, 01, 09, 00, 0a, 05, 00, 0e, 00, 0f, 01, 01, 01, 00, 02]
126+
Raw bytes (39): 0x[01, 01, 03, 01, 05, 0b, 02, 09, 0d, 05, 01, 25, 01, 01, 0a, 20, 05, 02, 01, 09, 00, 0a, 05, 00, 0e, 00, 0f, 20, 09, 0d, 00, 0e, 00, 0f, 07, 01, 01, 00, 02]
98127
Number of files: 1
99128
- file 0 => global file 1
100-
Number of expressions: 1
129+
Number of expressions: 3
101130
- expression 0 operands: lhs = Counter(0), rhs = Counter(1)
102-
Number of file 0 mappings: 4
131+
- expression 1 operands: lhs = Expression(2, Add), rhs = Expression(0, Sub)
132+
- expression 2 operands: lhs = Counter(2), rhs = Counter(3)
133+
Number of file 0 mappings: 5
103134
- Code(Counter(0)) at (prev + 37, 1) to (start + 1, 10)
104135
- Branch { true: Counter(1), false: Expression(0, Sub) } at (prev + 1, 9) to (start + 0, 10)
105136
true = c1
106137
false = (c0 - c1)
107138
- Code(Counter(1)) at (prev + 0, 14) to (start + 0, 15)
108-
- Code(Counter(0)) at (prev + 1, 1) to (start + 0, 2)
139+
- Branch { true: Counter(2), false: Counter(3) } at (prev + 0, 14) to (start + 0, 15)
140+
true = c2
141+
false = c3
142+
- Code(Expression(1, Add)) at (prev + 1, 1) to (start + 0, 2)
143+
= ((c2 + c3) + (c0 - c1))
109144

110145
Function name: conditions::simple_assign
111146
Raw bytes (9): 0x[01, 01, 00, 01, 01, 08, 01, 03, 02]

tests/coverage/condition/conditions.coverage

+5
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
^2
1616
------------------
1717
| Branch (LL:13): [True: 2, False: 1]
18+
| Branch (LL:18): [True: 1, False: 1]
1819
------------------
1920
LL| 3| black_box(x);
2021
LL| 3|}
@@ -24,6 +25,7 @@
2425
^1
2526
------------------
2627
| Branch (LL:13): [True: 2, False: 1]
28+
| Branch (LL:18): [True: 0, False: 1]
2729
------------------
2830
LL| 3| black_box(x);
2931
LL| 3|}
@@ -34,6 +36,7 @@
3436
------------------
3537
| Branch (LL:13): [True: 2, False: 2]
3638
| Branch (LL:18): [True: 1, False: 1]
39+
| Branch (LL:23): [True: 1, False: 0]
3740
------------------
3841
LL| 4| black_box(x);
3942
LL| 4|}
@@ -44,6 +47,7 @@
4447
------------------
4548
| Branch (LL:13): [True: 2, False: 2]
4649
| Branch (LL:18): [True: 1, False: 1]
50+
| Branch (LL:23): [True: 2, False: 1]
4751
------------------
4852
LL| 4| black_box(x);
4953
LL| 4|}
@@ -57,6 +61,7 @@
5761
^2
5862
------------------
5963
| Branch (LL:9): [True: 2, False: 1]
64+
| Branch (LL:14): [True: 1, False: 1]
6065
------------------
6166
LL| 3|}
6267
LL| |

0 commit comments

Comments
 (0)