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=.`
This commit is contained in:
Matthias Krüger 2021-12-01 10:50:20 +01:00 committed by GitHub
commit d93df5775c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 101 additions and 24 deletions

View file

@ -9,6 +9,7 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet};
use rustc_hir::def_id::{DefId, DefIdSet};
use rustc_llvm::RustString;
use rustc_middle::mir::coverage::CodeRegion;
use rustc_middle::ty::TyCtxt;
use rustc_span::Symbol;
use std::ffi::CString;
@ -17,10 +18,11 @@ use tracing::debug;
/// Generates and exports the Coverage Map.
///
/// This Coverage Map complies with Coverage Mapping Format version 4 (zero-based encoded as 3),
/// 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)
/// and published in Rust's November 2020 fork of LLVM. This version is supported by the LLVM
/// coverage tools (`llvm-profdata` and `llvm-cov`) bundled with Rust's fork of LLVM.
/// Rust Coverage Map generation supports LLVM Coverage Mapping Format versions
/// 5 (LLVM 12, only) and 6 (zero-based encoded as 4 and 5, respectively), as defined at
/// [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).
/// These versions are supported by the LLVM coverage tools (`llvm-profdata` and `llvm-cov`)
/// bundled with Rust's fork of LLVM.
///
/// Consequently, Rust's bundled version of Clang also generates Coverage Maps compliant with
/// the same version. Clang's implementation of Coverage Map generation was referenced when
@ -30,11 +32,12 @@ use tracing::debug;
pub fn finalize<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>) {
let tcx = cx.tcx;
// Ensure LLVM supports Coverage Map Version 4 (encoded as a zero-based value: 3).
// If not, the LLVM Version must be less than 11.
// Ensure the installed version of LLVM supports at least Coverage Map
// Version 5 (encoded as a zero-based value: 4), which was introduced with
// LLVM 12.
let version = coverageinfo::mapping_version();
if version != 3 {
tcx.sess.fatal("rustc option `-Z instrument-coverage` requires LLVM 11 or higher.");
if version < 4 {
tcx.sess.fatal("rustc option `-Z instrument-coverage` requires LLVM 12 or higher.");
}
debug!("Generating coverage map for CodegenUnit: `{}`", cx.codegen_unit.name());
@ -57,7 +60,7 @@ pub fn finalize<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>) {
return;
}
let mut mapgen = CoverageMapGenerator::new();
let mut mapgen = CoverageMapGenerator::new(tcx, version);
// Encode coverage mappings and generate function records
let mut function_data = Vec::new();
@ -112,8 +115,26 @@ struct CoverageMapGenerator {
}
impl CoverageMapGenerator {
fn new() -> Self {
Self { filenames: FxIndexSet::default() }
fn new(tcx: TyCtxt<'_>, version: u32) -> Self {
let mut filenames = FxIndexSet::default();
if version >= 5 {
// LLVM Coverage Mapping Format version 6 (zero-based encoded as 5)
// requires setting the first filename to the compilation directory.
// Since rustc generates coverage maps with relative paths, the
// compilation directory can be combined with the the relative paths
// to get absolute paths, if needed.
let working_dir = tcx
.sess
.opts
.working_dir
.remapped_path_if_available()
.to_string_lossy()
.to_string();
let c_filename =
CString::new(working_dir).expect("null error converting filename to C string");
filenames.insert(c_filename);
}
Self { filenames }
}
/// Using the `expressions` and `counter_regions` collected for the current function, generate

View file

@ -685,7 +685,7 @@ pub type InlineAsmDiagHandlerTy = unsafe extern "C" fn(&SMDiagnostic, *const c_v
pub mod coverageinfo {
use super::coverage_map;
/// 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)
/// 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)
#[derive(Copy, Clone, Debug)]
#[repr(C)]
pub enum RegionKind {
@ -704,11 +704,16 @@ pub mod coverageinfo {
/// A GapRegion is like a CodeRegion, but its count is only set as the
/// line execution count when its the only region in the line.
GapRegion = 3,
/// A BranchRegion represents leaf-level boolean expressions and is
/// associated with two counters, each representing the number of times the
/// expression evaluates to true or false.
BranchRegion = 4,
}
/// This struct provides LLVM's representation of a "CoverageMappingRegion", encoded into the
/// coverage map, in accordance with the
/// [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).
/// [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).
/// The struct composes fields representing the `Counter` type and value(s) (injected counter
/// ID, or expression type and operands), the source file (an indirect index into a "filenames
/// array", encoded separately), and source location (start and end positions of the represented
@ -721,6 +726,10 @@ pub mod coverageinfo {
/// The counter type and type-dependent counter data, if any.
counter: coverage_map::Counter,
/// If the `RegionKind` is a `BranchRegion`, this represents the counter
/// for the false branch of the region.
false_counter: coverage_map::Counter,
/// An indirect reference to the source filename. In the LLVM Coverage Mapping Format, the
/// file_id is an index into a function-specific `virtual_file_mapping` array of indexes
/// that, in turn, are used to look up the filename for this region.
@ -758,6 +767,7 @@ pub mod coverageinfo {
) -> Self {
Self {
counter,
false_counter: coverage_map::Counter::zero(),
file_id,
expanded_file_id: 0,
start_line,
@ -768,6 +778,31 @@ pub mod coverageinfo {
}
}
// This function might be used in the future; the LLVM API is still evolving, as is coverage
// support.
#[allow(dead_code)]
crate fn branch_region(
counter: coverage_map::Counter,
false_counter: coverage_map::Counter,
file_id: u32,
start_line: u32,
start_col: u32,
end_line: u32,
end_col: u32,
) -> Self {
Self {
counter,
false_counter,
file_id,
expanded_file_id: 0,
start_line,
start_col,
end_line,
end_col,
kind: RegionKind::BranchRegion,
}
}
// This function might be used in the future; the LLVM API is still evolving, as is coverage
// support.
#[allow(dead_code)]
@ -781,6 +816,7 @@ pub mod coverageinfo {
) -> Self {
Self {
counter: coverage_map::Counter::zero(),
false_counter: coverage_map::Counter::zero(),
file_id,
expanded_file_id,
start_line,
@ -803,6 +839,7 @@ pub mod coverageinfo {
) -> Self {
Self {
counter: coverage_map::Counter::zero(),
false_counter: coverage_map::Counter::zero(),
file_id,
expanded_file_id: 0,
start_line,
@ -826,6 +863,7 @@ pub mod coverageinfo {
) -> Self {
Self {
counter,
false_counter: coverage_map::Counter::zero(),
file_id,
expanded_file_id: 0,
start_line,