Skip to content

Commit afaaf37

Browse files
author
zhuyunxing
committed
coverage. Generate mcdc coverage mappings
1 parent 23fb83c commit afaaf37

File tree

11 files changed

+453
-40
lines changed

11 files changed

+453
-40
lines changed

compiler/rustc_codegen_llvm/src/coverageinfo/ffi.rs

Lines changed: 110 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
use rustc_middle::mir::coverage::{CodeRegion, CounterId, CovTerm, ExpressionId, MappingKind};
1+
use rustc_middle::mir::coverage::{
2+
CodeRegion, ConditionInfo, CounterId, CovTerm, DecisionInfo, ExpressionId, MappingKind,
3+
};
24

35
/// Must match the layout of `LLVMRustCounterKind`.
46
#[derive(Copy, Clone, Debug)]
@@ -99,6 +101,63 @@ pub enum RegionKind {
99101
/// associated with two counters, each representing the number of times the
100102
/// expression evaluates to true or false.
101103
BranchRegion = 4,
104+
105+
/// A DecisionRegion represents a top-level boolean expression and is
106+
/// associated with a variable length bitmap index and condition number.
107+
MCDCDecisionRegion = 5,
108+
109+
/// A Branch Region can be extended to include IDs to facilitate MC/DC.
110+
MCDCBranchRegion = 6,
111+
}
112+
113+
pub mod mcdc {
114+
use rustc_middle::mir::coverage::{ConditionInfo, DecisionInfo};
115+
116+
/// Must match the layout of `LLVMRustMCDCDecisionParameters`.
117+
#[repr(C)]
118+
#[derive(Clone, Copy, Debug)]
119+
pub struct DecisionParameters {
120+
bitmap_idx: u32,
121+
conditions_num: u16,
122+
}
123+
124+
// ConditionId in llvm is `unsigned int` at 18 while `int16_t` at [19](https://github.com/llvm/llvm-project/pull/81257)
125+
type LLVMConditionId = i16;
126+
127+
/// Must match the layout of `LLVMRustMCDCBranchParameters`.
128+
#[repr(C)]
129+
#[derive(Clone, Copy, Debug)]
130+
pub struct BranchParameters {
131+
condition_id: LLVMConditionId,
132+
condition_ids: [LLVMConditionId; 2],
133+
}
134+
135+
/// Same layout with `LLVMRustMCDCParameters`
136+
#[repr(C, u8)]
137+
#[derive(Clone, Copy, Debug)]
138+
pub enum Parameters {
139+
None,
140+
Decision(DecisionParameters),
141+
Branch(BranchParameters),
142+
}
143+
144+
impl From<ConditionInfo> for BranchParameters {
145+
fn from(value: ConditionInfo) -> Self {
146+
Self {
147+
condition_id: value.condition_id.as_u32() as LLVMConditionId,
148+
condition_ids: [
149+
value.false_next_id.as_u32() as LLVMConditionId,
150+
value.true_next_id.as_u32() as LLVMConditionId,
151+
],
152+
}
153+
}
154+
}
155+
156+
impl From<DecisionInfo> for DecisionParameters {
157+
fn from(value: DecisionInfo) -> Self {
158+
Self { bitmap_idx: value.bitmap_idx, conditions_num: value.conditions_num }
159+
}
160+
}
102161
}
103162

104163
/// This struct provides LLVM's representation of a "CoverageMappingRegion", encoded into the
@@ -122,6 +181,7 @@ pub struct CounterMappingRegion {
122181
/// for the false branch of the region.
123182
false_counter: Counter,
124183

184+
mcdc_params: mcdc::Parameters,
125185
/// An indirect reference to the source filename. In the LLVM Coverage Mapping Format, the
126186
/// file_id is an index into a function-specific `virtual_file_mapping` array of indexes
127187
/// that, in turn, are used to look up the filename for this region.
@@ -164,9 +224,20 @@ impl CounterMappingRegion {
164224
end_line,
165225
end_col,
166226
),
167-
MappingKind::Branch { true_term, false_term } => Self::branch_region(
168-
Counter::from_term(true_term),
169-
Counter::from_term(false_term),
227+
MappingKind::Branch { true_term, false_term, mcdc_params: condition_info } => {
228+
Self::branch_region(
229+
Counter::from_term(true_term),
230+
Counter::from_term(false_term),
231+
condition_info,
232+
local_file_id,
233+
start_line,
234+
start_col,
235+
end_line,
236+
end_col,
237+
)
238+
}
239+
MappingKind::Decision(decision_info) => Self::decision_region(
240+
decision_info,
170241
local_file_id,
171242
start_line,
172243
start_col,
@@ -187,6 +258,7 @@ impl CounterMappingRegion {
187258
Self {
188259
counter,
189260
false_counter: Counter::ZERO,
261+
mcdc_params: mcdc::Parameters::None,
190262
file_id,
191263
expanded_file_id: 0,
192264
start_line,
@@ -200,22 +272,52 @@ impl CounterMappingRegion {
200272
pub(crate) fn branch_region(
201273
counter: Counter,
202274
false_counter: Counter,
275+
condition_info: ConditionInfo,
203276
file_id: u32,
204277
start_line: u32,
205278
start_col: u32,
206279
end_line: u32,
207280
end_col: u32,
208281
) -> Self {
282+
let mcdc_params = mcdc::Parameters::Branch(condition_info.into());
283+
let kind = match mcdc_params {
284+
mcdc::Parameters::None => RegionKind::BranchRegion,
285+
mcdc::Parameters::Branch(_) => RegionKind::MCDCBranchRegion,
286+
_ => unreachable!("invalid mcdc params for branch"),
287+
};
209288
Self {
210289
counter,
211290
false_counter,
291+
mcdc_params,
292+
file_id,
293+
expanded_file_id: 0,
294+
start_line,
295+
start_col,
296+
end_line,
297+
end_col,
298+
kind,
299+
}
300+
}
301+
302+
pub(crate) fn decision_region(
303+
decision_info: DecisionInfo,
304+
file_id: u32,
305+
start_line: u32,
306+
start_col: u32,
307+
end_line: u32,
308+
end_col: u32,
309+
) -> Self {
310+
Self {
311+
counter: Counter::ZERO,
312+
false_counter: Counter::ZERO,
313+
mcdc_params: mcdc::Parameters::Decision(decision_info.into()),
212314
file_id,
213315
expanded_file_id: 0,
214316
start_line,
215317
start_col,
216318
end_line,
217319
end_col,
218-
kind: RegionKind::BranchRegion,
320+
kind: RegionKind::MCDCDecisionRegion,
219321
}
220322
}
221323

@@ -233,6 +335,7 @@ impl CounterMappingRegion {
233335
Self {
234336
counter: Counter::ZERO,
235337
false_counter: Counter::ZERO,
338+
mcdc_params: mcdc::Parameters::None,
236339
file_id,
237340
expanded_file_id,
238341
start_line,
@@ -256,6 +359,7 @@ impl CounterMappingRegion {
256359
Self {
257360
counter: Counter::ZERO,
258361
false_counter: Counter::ZERO,
362+
mcdc_params: mcdc::Parameters::None,
259363
file_id,
260364
expanded_file_id: 0,
261365
start_line,
@@ -280,6 +384,7 @@ impl CounterMappingRegion {
280384
Self {
281385
counter,
282386
false_counter: Counter::ZERO,
387+
mcdc_params: mcdc::Parameters::None,
283388
file_id,
284389
expanded_file_id: 0,
285390
start_line,

compiler/rustc_llvm/llvm-wrapper/CoverageMappingWrapper.cpp

Lines changed: 57 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@
44
#include "llvm/ProfileData/Coverage/CoverageMappingWriter.h"
55
#include "llvm/ProfileData/InstrProf.h"
66

7-
#include <iostream>
8-
97
using namespace llvm;
108

119
// FFI equivalent of enum `llvm::coverage::Counter::CounterKind`
@@ -43,6 +41,8 @@ enum class LLVMRustCounterMappingRegionKind {
4341
SkippedRegion = 2,
4442
GapRegion = 3,
4543
BranchRegion = 4,
44+
MCDCDecisionRegion = 5,
45+
MCDCBranchRegion = 6
4646
};
4747

4848
static coverage::CounterMappingRegion::RegionKind
@@ -58,15 +58,69 @@ fromRust(LLVMRustCounterMappingRegionKind Kind) {
5858
return coverage::CounterMappingRegion::GapRegion;
5959
case LLVMRustCounterMappingRegionKind::BranchRegion:
6060
return coverage::CounterMappingRegion::BranchRegion;
61+
case LLVMRustCounterMappingRegionKind::MCDCDecisionRegion:
62+
return coverage::CounterMappingRegion::MCDCDecisionRegion;
63+
case LLVMRustCounterMappingRegionKind::MCDCBranchRegion:
64+
return coverage::CounterMappingRegion::MCDCBranchRegion;
6165
}
6266
report_fatal_error("Bad LLVMRustCounterMappingRegionKind!");
6367
}
6468

69+
enum class LLVMRustMCDCParametersTag : uint8_t {
70+
None = 0,
71+
Decision = 1,
72+
Branch = 2,
73+
};
74+
75+
struct LLVMRustMCDCDecisionParameters {
76+
uint32_t BitmapIdx;
77+
uint16_t NumConditions;
78+
};
79+
80+
struct LLVMRustMCDCBranchParameters {
81+
int16_t ConditionID;
82+
int16_t ConditionIDs[2];
83+
};
84+
85+
union LLVMRustMCDCParametersPayload {
86+
LLVMRustMCDCDecisionParameters DecisionParameters;
87+
LLVMRustMCDCBranchParameters BranchParameters;
88+
};
89+
90+
struct LLVMRustMCDCParameters {
91+
LLVMRustMCDCParametersTag Tag;
92+
LLVMRustMCDCParametersPayload Payload;
93+
};
94+
95+
static coverage::CounterMappingRegion::MCDCParameters
96+
fromRust(LLVMRustMCDCParameters Params) {
97+
switch (Params.Tag) {
98+
case LLVMRustMCDCParametersTag::None:
99+
return coverage::CounterMappingRegion::MCDCParameters{};
100+
case LLVMRustMCDCParametersTag::Decision:
101+
return coverage::CounterMappingRegion::MCDCParameters{
102+
.BitmapIdx =
103+
static_cast<unsigned>(Params.Payload.DecisionParameters.BitmapIdx),
104+
.NumConditions = static_cast<unsigned>(
105+
Params.Payload.DecisionParameters.NumConditions)};
106+
case LLVMRustMCDCParametersTag::Branch:
107+
return coverage::CounterMappingRegion::MCDCParameters{
108+
.ID = static_cast<coverage::CounterMappingRegion::MCDCConditionID>(
109+
Params.Payload.BranchParameters.ConditionID),
110+
.FalseID = static_cast<coverage::CounterMappingRegion::MCDCConditionID>(
111+
Params.Payload.BranchParameters.ConditionIDs[0]),
112+
.TrueID = static_cast<coverage::CounterMappingRegion::MCDCConditionID>(
113+
Params.Payload.BranchParameters.ConditionIDs[1])};
114+
}
115+
report_fatal_error("Bad LLVMRustMCDCParametersTag!");
116+
}
117+
65118
// FFI equivalent of struct `llvm::coverage::CounterMappingRegion`
66119
// https://github.com/rust-lang/llvm-project/blob/ea6fa9c2/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h#L211-L304
67120
struct LLVMRustCounterMappingRegion {
68121
LLVMRustCounter Count;
69122
LLVMRustCounter FalseCount;
123+
LLVMRustMCDCParameters MCDCParameters;
70124
uint32_t FileID;
71125
uint32_t ExpandedFileID;
72126
uint32_t LineStart;
@@ -135,7 +189,7 @@ extern "C" void LLVMRustCoverageWriteMappingToBuffer(
135189
MappingRegions.emplace_back(
136190
fromRust(Region.Count), fromRust(Region.FalseCount),
137191
#if LLVM_VERSION_GE(18, 0) && LLVM_VERSION_LT(19, 0)
138-
coverage::CounterMappingRegion::MCDCParameters{},
192+
fromRust(Region.MCDCParameters),
139193
#endif
140194
Region.FileID, Region.ExpandedFileID, // File IDs, then region info.
141195
Region.LineStart, Region.ColumnStart, Region.LineEnd, Region.ColumnEnd,

compiler/rustc_middle/src/mir/coverage.rs

Lines changed: 65 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,23 @@ rustc_index::newtype_index! {
5151
pub struct ExpressionId {}
5252
}
5353

54+
rustc_index::newtype_index! {
55+
/// ID of a mcdc condition. Used by llvm to check mcdc coverage.
56+
///
57+
/// Note that LLVM handles condition IDs as `int16_t`, so there is no need
58+
/// to use a larger representation on the Rust side.
59+
#[derive(HashStable)]
60+
#[encodable]
61+
#[orderable]
62+
#[max = 0xFFFF]
63+
#[debug_format = "ConditionId({})"]
64+
pub struct ConditionId {}
65+
}
66+
67+
impl ConditionId {
68+
pub const NONE: Self = Self::from_u32(0);
69+
}
70+
5471
/// Enum that can hold a constant zero value, the ID of an physical coverage
5572
/// counter, or the ID of a coverage-counter expression.
5673
///
@@ -171,17 +188,21 @@ pub enum MappingKind {
171188
/// Associates a normal region of code with a counter/expression/zero.
172189
Code(CovTerm),
173190
/// Associates a branch region with separate counters for true and false.
174-
Branch { true_term: CovTerm, false_term: CovTerm },
191+
Branch { true_term: CovTerm, false_term: CovTerm, mcdc_params: ConditionInfo },
192+
/// Associates a decision region with a bitmap and number of conditions.
193+
Decision(DecisionInfo),
175194
}
176195

177196
impl MappingKind {
178197
/// Iterator over all coverage terms in this mapping kind.
179198
pub fn terms(&self) -> impl Iterator<Item = CovTerm> {
180-
let one = |a| std::iter::once(a).chain(None);
181-
let two = |a, b| std::iter::once(a).chain(Some(b));
199+
let zero = || None.into_iter().chain(None);
200+
let one = |a| Some(a).into_iter().chain(None);
201+
let two = |a, b| Some(a).into_iter().chain(Some(b));
182202
match *self {
183203
Self::Code(term) => one(term),
184-
Self::Branch { true_term, false_term } => two(true_term, false_term),
204+
Self::Branch { true_term, false_term, .. } => two(true_term, false_term),
205+
Self::Decision(_) => zero(),
185206
}
186207
}
187208

@@ -190,9 +211,12 @@ impl MappingKind {
190211
pub fn map_terms(&self, map_fn: impl Fn(CovTerm) -> CovTerm) -> Self {
191212
match *self {
192213
Self::Code(term) => Self::Code(map_fn(term)),
193-
Self::Branch { true_term, false_term } => {
194-
Self::Branch { true_term: map_fn(true_term), false_term: map_fn(false_term) }
195-
}
214+
Self::Branch { true_term, false_term, mcdc_params } => Self::Branch {
215+
true_term: map_fn(true_term),
216+
false_term: map_fn(false_term),
217+
mcdc_params,
218+
},
219+
Self::Decision(param) => Self::Decision(param),
196220
}
197221
}
198222
}
@@ -226,12 +250,46 @@ pub struct BranchInfo {
226250
/// data structures without having to scan the entire body first.
227251
pub num_block_markers: usize,
228252
pub branch_spans: Vec<BranchSpan>,
253+
pub decision_spans: Vec<DecisionSpan>,
229254
}
230255

231256
#[derive(Clone, Debug)]
232257
#[derive(TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable, TypeVisitable)]
233258
pub struct BranchSpan {
234259
pub span: Span,
260+
pub condition_info: ConditionInfo,
235261
pub true_marker: BlockMarkerId,
236262
pub false_marker: BlockMarkerId,
237263
}
264+
265+
#[derive(Copy, Clone, Debug)]
266+
#[derive(TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable, TypeVisitable)]
267+
pub struct ConditionInfo {
268+
pub condition_id: ConditionId,
269+
pub true_next_id: ConditionId,
270+
pub false_next_id: ConditionId,
271+
}
272+
273+
impl Default for ConditionInfo {
274+
fn default() -> Self {
275+
Self {
276+
condition_id: ConditionId::NONE,
277+
true_next_id: ConditionId::NONE,
278+
false_next_id: ConditionId::NONE,
279+
}
280+
}
281+
}
282+
283+
#[derive(Copy, Clone, Debug)]
284+
#[derive(TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable, TypeVisitable)]
285+
pub struct DecisionInfo {
286+
pub bitmap_idx: u32,
287+
pub conditions_num: u16,
288+
}
289+
290+
#[derive(Clone, Debug)]
291+
#[derive(TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable, TypeVisitable)]
292+
pub struct DecisionSpan {
293+
pub span: Span,
294+
pub conditions_num: u16,
295+
}

0 commit comments

Comments
 (0)