Skip to content

Commit d93df57

Browse files
authored
Rollup merge of #91207 - richkadel:rk-bump-coverage-version, r=tmandry
Add support for LLVM coverage mapping format versions 5 and 6 This PR cherry-pick's Swatinem's initial commit in unsubmitted PR #90047. My additional commit augments Swatinem's great starting point, but adds full support for LLVM Coverage Mapping Format version 6, conditionally, if compiling with LLVM 13. Version 6 requires adding the compilation directory when file paths are relative, and since Rustc coverage maps use relative paths, we should add the expected compilation directory entry. Note, however, that with the compilation directory, coverage reports from `llvm-cov show` can now report file names (when the report includes more than one file) with the full absolute path to the file. This would be a problem for test results, but the workaround (for the rust coverage tests) is to include an additional `llvm-cov show` parameter: `--compilation-dir=.`
2 parents 2695e85 + 0c57fab commit d93df57

File tree

8 files changed

+101
-24
lines changed

8 files changed

+101
-24
lines changed

compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs

+32-11
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet};
99
use rustc_hir::def_id::{DefId, DefIdSet};
1010
use rustc_llvm::RustString;
1111
use rustc_middle::mir::coverage::CodeRegion;
12+
use rustc_middle::ty::TyCtxt;
1213
use rustc_span::Symbol;
1314

1415
use std::ffi::CString;
@@ -17,10 +18,11 @@ use tracing::debug;
1718

1819
/// Generates and exports the Coverage Map.
1920
///
20-
/// This Coverage Map complies with Coverage Mapping Format version 4 (zero-based encoded as 3),
21-
/// as defined at [LLVM Code Coverage Mapping Format](https://github.com/rust-lang/llvm-project/blob/rustc/11.0-2020-10-12/llvm/docs/CoverageMappingFormat.rst#llvm-code-coverage-mapping-format)
22-
/// and published in Rust's November 2020 fork of LLVM. This version is supported by the LLVM
23-
/// coverage tools (`llvm-profdata` and `llvm-cov`) bundled with Rust's fork of LLVM.
21+
/// Rust Coverage Map generation supports LLVM Coverage Mapping Format versions
22+
/// 5 (LLVM 12, only) and 6 (zero-based encoded as 4 and 5, respectively), as defined at
23+
/// [LLVM Code Coverage Mapping Format](https://github.com/rust-lang/llvm-project/blob/rustc/13.0-2021-09-30/llvm/docs/CoverageMappingFormat.rst#llvm-code-coverage-mapping-format).
24+
/// These versions are supported by the LLVM coverage tools (`llvm-profdata` and `llvm-cov`)
25+
/// bundled with Rust's fork of LLVM.
2426
///
2527
/// Consequently, Rust's bundled version of Clang also generates Coverage Maps compliant with
2628
/// the same version. Clang's implementation of Coverage Map generation was referenced when
@@ -30,11 +32,12 @@ use tracing::debug;
3032
pub fn finalize<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>) {
3133
let tcx = cx.tcx;
3234

33-
// Ensure LLVM supports Coverage Map Version 4 (encoded as a zero-based value: 3).
34-
// If not, the LLVM Version must be less than 11.
35+
// Ensure the installed version of LLVM supports at least Coverage Map
36+
// Version 5 (encoded as a zero-based value: 4), which was introduced with
37+
// LLVM 12.
3538
let version = coverageinfo::mapping_version();
36-
if version != 3 {
37-
tcx.sess.fatal("rustc option `-Z instrument-coverage` requires LLVM 11 or higher.");
39+
if version < 4 {
40+
tcx.sess.fatal("rustc option `-Z instrument-coverage` requires LLVM 12 or higher.");
3841
}
3942

4043
debug!("Generating coverage map for CodegenUnit: `{}`", cx.codegen_unit.name());
@@ -57,7 +60,7 @@ pub fn finalize<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>) {
5760
return;
5861
}
5962

60-
let mut mapgen = CoverageMapGenerator::new();
63+
let mut mapgen = CoverageMapGenerator::new(tcx, version);
6164

6265
// Encode coverage mappings and generate function records
6366
let mut function_data = Vec::new();
@@ -112,8 +115,26 @@ struct CoverageMapGenerator {
112115
}
113116

114117
impl CoverageMapGenerator {
115-
fn new() -> Self {
116-
Self { filenames: FxIndexSet::default() }
118+
fn new(tcx: TyCtxt<'_>, version: u32) -> Self {
119+
let mut filenames = FxIndexSet::default();
120+
if version >= 5 {
121+
// LLVM Coverage Mapping Format version 6 (zero-based encoded as 5)
122+
// requires setting the first filename to the compilation directory.
123+
// Since rustc generates coverage maps with relative paths, the
124+
// compilation directory can be combined with the the relative paths
125+
// to get absolute paths, if needed.
126+
let working_dir = tcx
127+
.sess
128+
.opts
129+
.working_dir
130+
.remapped_path_if_available()
131+
.to_string_lossy()
132+
.to_string();
133+
let c_filename =
134+
CString::new(working_dir).expect("null error converting filename to C string");
135+
filenames.insert(c_filename);
136+
}
137+
Self { filenames }
117138
}
118139

119140
/// Using the `expressions` and `counter_regions` collected for the current function, generate

compiler/rustc_codegen_llvm/src/llvm/ffi.rs

+40-2
Original file line numberDiff line numberDiff line change
@@ -685,7 +685,7 @@ pub type InlineAsmDiagHandlerTy = unsafe extern "C" fn(&SMDiagnostic, *const c_v
685685
pub mod coverageinfo {
686686
use super::coverage_map;
687687

688-
/// Aligns with [llvm::coverage::CounterMappingRegion::RegionKind](https://github.com/rust-lang/llvm-project/blob/rustc/11.0-2020-10-12/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h#L206-L222)
688+
/// Aligns with [llvm::coverage::CounterMappingRegion::RegionKind](https://github.com/rust-lang/llvm-project/blob/rustc/13.0-2021-09-30/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h#L209-L230)
689689
#[derive(Copy, Clone, Debug)]
690690
#[repr(C)]
691691
pub enum RegionKind {
@@ -704,11 +704,16 @@ pub mod coverageinfo {
704704
/// A GapRegion is like a CodeRegion, but its count is only set as the
705705
/// line execution count when its the only region in the line.
706706
GapRegion = 3,
707+
708+
/// A BranchRegion represents leaf-level boolean expressions and is
709+
/// associated with two counters, each representing the number of times the
710+
/// expression evaluates to true or false.
711+
BranchRegion = 4,
707712
}
708713

709714
/// This struct provides LLVM's representation of a "CoverageMappingRegion", encoded into the
710715
/// coverage map, in accordance with the
711-
/// [LLVM Code Coverage Mapping Format](https://github.com/rust-lang/llvm-project/blob/rustc/11.0-2020-10-12/llvm/docs/CoverageMappingFormat.rst#llvm-code-coverage-mapping-format).
716+
/// [LLVM Code Coverage Mapping Format](https://github.com/rust-lang/llvm-project/blob/rustc/13.0-2021-09-30/llvm/docs/CoverageMappingFormat.rst#llvm-code-coverage-mapping-format).
712717
/// The struct composes fields representing the `Counter` type and value(s) (injected counter
713718
/// ID, or expression type and operands), the source file (an indirect index into a "filenames
714719
/// array", encoded separately), and source location (start and end positions of the represented
@@ -721,6 +726,10 @@ pub mod coverageinfo {
721726
/// The counter type and type-dependent counter data, if any.
722727
counter: coverage_map::Counter,
723728

729+
/// If the `RegionKind` is a `BranchRegion`, this represents the counter
730+
/// for the false branch of the region.
731+
false_counter: coverage_map::Counter,
732+
724733
/// An indirect reference to the source filename. In the LLVM Coverage Mapping Format, the
725734
/// file_id is an index into a function-specific `virtual_file_mapping` array of indexes
726735
/// that, in turn, are used to look up the filename for this region.
@@ -758,6 +767,7 @@ pub mod coverageinfo {
758767
) -> Self {
759768
Self {
760769
counter,
770+
false_counter: coverage_map::Counter::zero(),
761771
file_id,
762772
expanded_file_id: 0,
763773
start_line,
@@ -768,6 +778,31 @@ pub mod coverageinfo {
768778
}
769779
}
770780

781+
// This function might be used in the future; the LLVM API is still evolving, as is coverage
782+
// support.
783+
#[allow(dead_code)]
784+
crate fn branch_region(
785+
counter: coverage_map::Counter,
786+
false_counter: coverage_map::Counter,
787+
file_id: u32,
788+
start_line: u32,
789+
start_col: u32,
790+
end_line: u32,
791+
end_col: u32,
792+
) -> Self {
793+
Self {
794+
counter,
795+
false_counter,
796+
file_id,
797+
expanded_file_id: 0,
798+
start_line,
799+
start_col,
800+
end_line,
801+
end_col,
802+
kind: RegionKind::BranchRegion,
803+
}
804+
}
805+
771806
// This function might be used in the future; the LLVM API is still evolving, as is coverage
772807
// support.
773808
#[allow(dead_code)]
@@ -781,6 +816,7 @@ pub mod coverageinfo {
781816
) -> Self {
782817
Self {
783818
counter: coverage_map::Counter::zero(),
819+
false_counter: coverage_map::Counter::zero(),
784820
file_id,
785821
expanded_file_id,
786822
start_line,
@@ -803,6 +839,7 @@ pub mod coverageinfo {
803839
) -> Self {
804840
Self {
805841
counter: coverage_map::Counter::zero(),
842+
false_counter: coverage_map::Counter::zero(),
806843
file_id,
807844
expanded_file_id: 0,
808845
start_line,
@@ -826,6 +863,7 @@ pub mod coverageinfo {
826863
) -> Self {
827864
Self {
828865
counter,
866+
false_counter: coverage_map::Counter::zero(),
829867
file_id,
830868
expanded_file_id: 0,
831869
start_line,

compiler/rustc_codegen_ssa/src/coverageinfo/ffi.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use rustc_middle::mir::coverage::{CounterValueReference, MappedExpressionIndex};
22

3-
/// Aligns with [llvm::coverage::Counter::CounterKind](https://github.com/rust-lang/llvm-project/blob/rustc/11.0-2020-10-12/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h#L206-L222)
3+
/// Aligns with [llvm::coverage::Counter::CounterKind](https://github.com/rust-lang/llvm-project/blob/rustc/13.0-2021-09-30/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h#L95)
44
#[derive(Copy, Clone, Debug)]
55
#[repr(C)]
66
pub enum CounterKind {
@@ -17,7 +17,7 @@ pub enum CounterKind {
1717
/// `instrprof.increment()`)
1818
/// * For `CounterKind::Expression`, `id` is the index into the coverage map's array of
1919
/// counter expressions.
20-
/// Aligns with [llvm::coverage::Counter](https://github.com/rust-lang/llvm-project/blob/rustc/11.0-2020-10-12/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h#L99-L100)
20+
/// Aligns with [llvm::coverage::Counter](https://github.com/rust-lang/llvm-project/blob/rustc/13.0-2021-09-30/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h#L102-L103)
2121
/// Important: The Rust struct layout (order and types of fields) must match its C++ counterpart.
2222
#[derive(Copy, Clone, Debug)]
2323
#[repr(C)]
@@ -59,15 +59,15 @@ impl Counter {
5959
}
6060
}
6161

62-
/// Aligns with [llvm::coverage::CounterExpression::ExprKind](https://github.com/rust-lang/llvm-project/blob/rustc/11.0-2020-10-12/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h#L147)
62+
/// Aligns with [llvm::coverage::CounterExpression::ExprKind](https://github.com/rust-lang/llvm-project/blob/rustc/13.0-2021-09-30/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h#L150)
6363
#[derive(Copy, Clone, Debug)]
6464
#[repr(C)]
6565
pub enum ExprKind {
6666
Subtract = 0,
6767
Add = 1,
6868
}
6969

70-
/// Aligns with [llvm::coverage::CounterExpression](https://github.com/rust-lang/llvm-project/blob/rustc/11.0-2020-10-12/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h#L148-L149)
70+
/// Aligns with [llvm::coverage::CounterExpression](https://github.com/rust-lang/llvm-project/blob/rustc/13.0-2021-09-30/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h#L151-L152)
7171
/// Important: The Rust struct layout (order and types of fields) must match its C++
7272
/// counterpart.
7373
#[derive(Copy, Clone, Debug)]

compiler/rustc_llvm/llvm-wrapper/CoverageMappingWrapper.cpp

+7-2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ using namespace llvm;
1010

1111
struct LLVMRustCounterMappingRegion {
1212
coverage::Counter Count;
13+
coverage::Counter FalseCount;
1314
uint32_t FileID;
1415
uint32_t ExpandedFileID;
1516
uint32_t LineStart;
@@ -53,7 +54,7 @@ extern "C" void LLVMRustCoverageWriteMappingToBuffer(
5354
MappingRegions.reserve(NumMappingRegions);
5455
for (const auto &Region : makeArrayRef(RustMappingRegions, NumMappingRegions)) {
5556
MappingRegions.emplace_back(
56-
Region.Count, Region.FileID, Region.ExpandedFileID,
57+
Region.Count, Region.FalseCount, Region.FileID, Region.ExpandedFileID,
5758
Region.LineStart, Region.ColumnStart, Region.LineEnd, Region.ColumnEnd,
5859
Region.Kind);
5960
}
@@ -108,5 +109,9 @@ extern "C" void LLVMRustCoverageWriteMappingVarNameToString(RustStringRef Str) {
108109
}
109110

110111
extern "C" uint32_t LLVMRustCoverageMappingVersion() {
111-
return coverage::CovMapVersion::Version4;
112+
#if LLVM_VERSION_GE(13, 0)
113+
return coverage::CovMapVersion::Version6;
114+
#else
115+
return coverage::CovMapVersion::Version5;
116+
#endif
112117
}

compiler/rustc_middle/src/mir/coverage.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,9 @@ rustc_index::newtype_index! {
2121
impl ExpressionOperandId {
2222
/// An expression operand for a "zero counter", as described in the following references:
2323
///
24-
/// * <https://github.com/rust-lang/llvm-project/blob/rustc/11.0-2020-10-12/llvm/docs/CoverageMappingFormat.rst#counter>
25-
/// * <https://github.com/rust-lang/llvm-project/blob/rustc/11.0-2020-10-12/llvm/docs/CoverageMappingFormat.rst#tag>
26-
/// * <https://github.com/rust-lang/llvm-project/blob/rustc/11.0-2020-10-12/llvm/docs/CoverageMappingFormat.rst#counter-expressions>
24+
/// * <https://github.com/rust-lang/llvm-project/blob/rustc/13.0-2021-09-30/llvm/docs/CoverageMappingFormat.rst#counter>
25+
/// * <https://github.com/rust-lang/llvm-project/blob/rustc/13.0-2021-09-30/llvm/docs/CoverageMappingFormat.rst#tag>
26+
/// * <https://github.com/rust-lang/llvm-project/blob/rustc/13.0-2021-09-30/llvm/docs/CoverageMappingFormat.rst#counter-expressions>
2727
///
2828
/// This operand can be used to count two or more separate code regions with a single counter,
2929
/// if they run sequentially with no branches, by injecting the `Counter` in a `BasicBlock` for

src/doc/unstable-book/src/compiler-flags/instrument-coverage.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ This document describes how to enable and use the LLVM instrumentation-based cov
2020
When `-Z instrument-coverage` is enabled, the Rust compiler enhances rust-based libraries and binaries by:
2121

2222
- Automatically injecting calls to an LLVM intrinsic ([`llvm.instrprof.increment`]), at functions and branches in compiled code, to increment counters when conditional sections of code are executed.
23-
- Embedding additional information in the data section of each library and binary (using the [LLVM Code Coverage Mapping Format] _Version 4_, supported _only_ in LLVM 11 and up), to define the code regions (start and end positions in the source code) being counted.
23+
- Embedding additional information in the data section of each library and binary (using the [LLVM Code Coverage Mapping Format] _Version 5_, if compiling with LLVM 12, or _Version 6_, if compiling with LLVM 13 or higher), to define the code regions (start and end positions in the source code) being counted.
2424

2525
When running a coverage-instrumented program, the counter values are written to a `profraw` file at program termination. LLVM bundles tools that read the counter results, combine those results with the coverage map (embedded in the program binary), and generate coverage reports in multiple formats.
2626

@@ -123,7 +123,7 @@ If `LLVM_PROFILE_FILE` contains a path to a non-existent directory, the missing
123123

124124
## Installing LLVM coverage tools
125125

126-
LLVM's supplies two tools—`llvm-profdata` and `llvm-cov`—that process coverage data and generate reports. There are several ways to find and/or install these tools, but note that the coverage mapping data generated by the Rust compiler requires LLVM version 11 or higher. (`llvm-cov --version` typically shows the tool's LLVM version number.):
126+
LLVM's supplies two tools—`llvm-profdata` and `llvm-cov`—that process coverage data and generate reports. There are several ways to find and/or install these tools, but note that the coverage mapping data generated by the Rust compiler requires LLVM version 12 or higher. (`llvm-cov --version` typically shows the tool's LLVM version number.):
127127

128128
- The LLVM tools may be installed (or installable) directly to your OS (such as via `apt-get`, for Linux).
129129
- If you are building the Rust compiler from source, you can optionally use the bundled LLVM tools, built from source. Those tool binaries can typically be found in your build platform directory at something like: `rust/build/x86_64-unknown-linux-gnu/llvm/bin/llvm-*`.

src/test/run-make-fulldeps/coverage-llvmir/Makefile

+6
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# needs-profiler-support
22

3+
# Rust coverage maps support LLVM Coverage Mapping Format versions 5 and 6,
4+
# corresponding with LLVM versions 12 and 13, respectively.
5+
# When upgrading LLVM versions, consider whether to enforce a minimum LLVM
6+
# version during testing, with an additional directive at the top of this file
7+
# that sets, for example: `min-llvm-version: 12.0`
8+
39
-include ../coverage/coverage_tools.mk
410

511
BASEDIR=../coverage-llvmir

src/test/run-make-fulldeps/coverage-reports/Makefile

+7
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
# needs-profiler-support
22
# ignore-windows-gnu
33

4+
# Rust coverage maps support LLVM Coverage Mapping Format versions 5 and 6,
5+
# corresponding with LLVM versions 12 and 13, respectively.
6+
# When upgrading LLVM versions, consider whether to enforce a minimum LLVM
7+
# version during testing, with an additional directive at the top of this file
8+
# that sets, for example: `min-llvm-version: 12.0`
9+
410
# FIXME(mati865): MinGW GCC miscompiles compiler-rt profiling library but with Clang it works
511
# properly. Since we only have GCC on the CI ignore the test for now.
612

@@ -115,6 +121,7 @@ endif
115121
"$(LLVM_BIN_DIR)"/llvm-cov show \
116122
$(DEBUG_FLAG) \
117123
$(LLVM_COV_IGNORE_FILES) \
124+
--compilation-dir=. \
118125
--Xdemangler="$(RUST_DEMANGLER)" \
119126
--show-line-counts-or-regions \
120127
--instr-profile="$(TMPDIR)"/[email protected] \

0 commit comments

Comments
 (0)