From 90ee252588f67b55e783012a50ed84901380490c Mon Sep 17 00:00:00 2001 From: Arpad Borsos Date: Tue, 19 Oct 2021 11:09:43 +0200 Subject: [PATCH] Update CoverageMappingFormat Support to Version6 Version 5 adds Branch Regions which are a prerequisite for branch coverage. Version 6 can use the zeroth filename as prefix for other relative files. --- .../src/coverageinfo/mapgen.rs | 20 ++++----- compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 42 ++++++++++++++++++- .../rustc_codegen_ssa/src/coverageinfo/ffi.rs | 8 ++-- .../llvm-wrapper/CoverageMappingWrapper.cpp | 9 +++- compiler/rustc_middle/src/mir/coverage.rs | 6 +-- .../src/compiler-flags/instrument-coverage.md | 4 +- 6 files changed, 66 insertions(+), 23 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs index 6830864ba04b4..67cd5363a7ba5 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs @@ -17,10 +17,10 @@ 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. +/// This Coverage Map complies with Coverage Mapping Format version 5 (zero-based encoded as 4), +/// 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). +/// This version is 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,12 +30,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. - let version = coverageinfo::mapping_version(); - if version != 3 { - tcx.sess.fatal("rustc option `-Z instrument-coverage` requires LLVM 11 or higher."); - } + // While our bundled LLVM might support Coverage Map Version 6 + // (encoded as a zero-based value: 5), we clamp that to Version 5, + // as Version 6 would require us to use the 0-th filename as a path prefix + // for all other relative paths, which we don't take advantage of right now. + let _version = coverageinfo::mapping_version(); + let version = 4; debug!("Generating coverage map for CodegenUnit: `{}`", cx.codegen_unit.name()); diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 749eec459aca1..5e613042fe448 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -678,7 +678,7 @@ pub type InlineAsmDiagHandler = unsafe extern "C" fn(&SMDiagnostic, *const c_voi 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 { @@ -697,11 +697,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 @@ -714,6 +719,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. @@ -751,6 +760,7 @@ pub mod coverageinfo { ) -> Self { Self { counter, + false_counter: coverage_map::Counter::zero(), file_id, expanded_file_id: 0, start_line, @@ -761,6 +771,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)] @@ -774,6 +809,7 @@ pub mod coverageinfo { ) -> Self { Self { counter: coverage_map::Counter::zero(), + false_counter: coverage_map::Counter::zero(), file_id, expanded_file_id, start_line, @@ -796,6 +832,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, @@ -819,6 +856,7 @@ pub mod coverageinfo { ) -> Self { Self { counter, + false_counter: coverage_map::Counter::zero(), file_id, expanded_file_id: 0, start_line, diff --git a/compiler/rustc_codegen_ssa/src/coverageinfo/ffi.rs b/compiler/rustc_codegen_ssa/src/coverageinfo/ffi.rs index 962c01c2ee7a6..e288760a02b18 100644 --- a/compiler/rustc_codegen_ssa/src/coverageinfo/ffi.rs +++ b/compiler/rustc_codegen_ssa/src/coverageinfo/ffi.rs @@ -1,6 +1,6 @@ use rustc_middle::mir::coverage::{CounterValueReference, MappedExpressionIndex}; -/// 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) +/// 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) #[derive(Copy, Clone, Debug)] #[repr(C)] pub enum CounterKind { @@ -17,7 +17,7 @@ pub enum CounterKind { /// `instrprof.increment()`) /// * For `CounterKind::Expression`, `id` is the index into the coverage map's array of /// counter expressions. -/// 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) +/// 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) /// Important: The Rust struct layout (order and types of fields) must match its C++ counterpart. #[derive(Copy, Clone, Debug)] #[repr(C)] @@ -59,7 +59,7 @@ impl Counter { } } -/// 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) +/// 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) #[derive(Copy, Clone, Debug)] #[repr(C)] pub enum ExprKind { @@ -67,7 +67,7 @@ pub enum ExprKind { Add = 1, } -/// 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) +/// 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) /// Important: The Rust struct layout (order and types of fields) must match its C++ /// counterpart. #[derive(Copy, Clone, Debug)] diff --git a/compiler/rustc_llvm/llvm-wrapper/CoverageMappingWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/CoverageMappingWrapper.cpp index 8cd2bd12450e3..154f554d607df 100644 --- a/compiler/rustc_llvm/llvm-wrapper/CoverageMappingWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/CoverageMappingWrapper.cpp @@ -10,6 +10,7 @@ using namespace llvm; struct LLVMRustCounterMappingRegion { coverage::Counter Count; + coverage::Counter FalseCount; uint32_t FileID; uint32_t ExpandedFileID; uint32_t LineStart; @@ -53,7 +54,7 @@ extern "C" void LLVMRustCoverageWriteMappingToBuffer( MappingRegions.reserve(NumMappingRegions); for (const auto &Region : makeArrayRef(RustMappingRegions, NumMappingRegions)) { MappingRegions.emplace_back( - Region.Count, Region.FileID, Region.ExpandedFileID, + Region.Count, Region.FalseCount, Region.FileID, Region.ExpandedFileID, Region.LineStart, Region.ColumnStart, Region.LineEnd, Region.ColumnEnd, Region.Kind); } @@ -108,5 +109,9 @@ extern "C" void LLVMRustCoverageWriteMappingVarNameToString(RustStringRef Str) { } extern "C" uint32_t LLVMRustCoverageMappingVersion() { - return coverage::CovMapVersion::Version4; +#if LLVM_VERSION_GE(13, 0) + return coverage::CovMapVersion::Version6; +#else + return coverage::CovMapVersion::Version5; +#endif } diff --git a/compiler/rustc_middle/src/mir/coverage.rs b/compiler/rustc_middle/src/mir/coverage.rs index ddb1a84fe7bdf..640d3a5a02b0f 100644 --- a/compiler/rustc_middle/src/mir/coverage.rs +++ b/compiler/rustc_middle/src/mir/coverage.rs @@ -21,9 +21,9 @@ rustc_index::newtype_index! { impl ExpressionOperandId { /// An expression operand for a "zero counter", as described in the following references: /// - /// * - /// * - /// * + /// * + /// * + /// * /// /// This operand can be used to count two or more separate code regions with a single counter, /// if they run sequentially with no branches, by injecting the `Counter` in a `BasicBlock` for diff --git a/src/doc/unstable-book/src/compiler-flags/instrument-coverage.md b/src/doc/unstable-book/src/compiler-flags/instrument-coverage.md index f7c2a26f01817..0424ed08dafbe 100644 --- a/src/doc/unstable-book/src/compiler-flags/instrument-coverage.md +++ b/src/doc/unstable-book/src/compiler-flags/instrument-coverage.md @@ -20,7 +20,7 @@ This document describes how to enable and use the LLVM instrumentation-based cov When `-Z instrument-coverage` is enabled, the Rust compiler enhances rust-based libraries and binaries by: - 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. -- 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. +- Embedding additional information in the data section of each library and binary (using the [LLVM Code Coverage Mapping Format] _Version 5_, supported _only_ in LLVM 12 and up), to define the code regions (start and end positions in the source code) being counted. 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. @@ -123,7 +123,7 @@ If `LLVM_PROFILE_FILE` contains a path to a non-existent directory, the missing ## Installing LLVM coverage tools -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.): +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.): - The LLVM tools may be installed (or installable) directly to your OS (such as via `apt-get`, for Linux). - 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-*`.