use llvm::coverageinfo::CounterMappingRegion;
use rustc_codegen_ssa::coverageinfo::map::{Counter, CounterExpression};
-use rustc_codegen_ssa::traits::{BaseTypeMethods, ConstMethods};
+use rustc_codegen_ssa::traits::ConstMethods;
use rustc_data_structures::fx::FxIndexSet;
use rustc_llvm::RustString;
use rustc_middle::mir::coverage::CodeRegion;
/// Generates and exports the Coverage Map.
///
-/// This Coverage Map complies with Coverage Mapping Format version 3 (zero-based encoded as 2),
-/// as defined at [LLVM Code Coverage Mapping Format](https://github.com/rust-lang/llvm-project/blob/llvmorg-8.0.0/llvm/docs/CoverageMappingFormat.rst#llvm-code-coverage-mapping-format)
-/// and published in Rust's current (July 2020) fork of LLVM. This version is supported by the
+/// 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 current (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.
///
/// Consequently, Rust's bundled version of Clang also generates Coverage Maps compliant with
/// undocumented details in Clang's implementation (that may or may not be important) were also
/// replicated for Rust's Coverage Map.
pub fn finalize<'ll, 'tcx>(cx: &CodegenCx<'ll, '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 {
+ cx.tcx.sess.fatal("rustc option `-Z instrument-coverage` requires LLVM 11 or higher.");
+ }```
+
let function_coverage_map = match cx.coverage_context() {
Some(ctx) => ctx.take_function_coverage_map(),
None => return,
let mut mapgen = CoverageMapGenerator::new();
// Encode coverage mappings and generate function records
- let mut function_records = Vec::<&'ll llvm::Value>::new();
- let coverage_mappings_buffer = llvm::build_byte_buffer(|coverage_mappings_buffer| {
- for (instance, function_coverage) in function_coverage_map.into_iter() {
- debug!("Generate coverage map for: {:?}", instance);
-
- let mangled_function_name = cx.tcx.symbol_name(instance).to_string();
- let function_source_hash = function_coverage.source_hash();
- let (expressions, counter_regions) =
- function_coverage.get_expressions_and_counter_regions();
-
- let old_len = coverage_mappings_buffer.len();
- mapgen.write_coverage_mappings(expressions, counter_regions, coverage_mappings_buffer);
- let mapping_data_size = coverage_mappings_buffer.len() - old_len;
- debug_assert!(
- mapping_data_size > 0,
- "Every `FunctionCoverage` should have at least one counter"
- );
-
- let function_record = mapgen.make_function_record(
- cx,
- mangled_function_name,
- function_source_hash,
- mapping_data_size,
- );
- function_records.push(function_record);
- }
- });
+ let mut function_data = Vec::new();
+ for (instance, function_coverage) in function_coverage_map {
+ debug!("Generate coverage map for: {:?}", instance);
+
+ let mangled_function_name = cx.tcx.symbol_name(instance).to_string();
+ let function_source_hash = function_coverage.source_hash();
+ let (expressions, counter_regions) =
+ function_coverage.get_expressions_and_counter_regions();
+
+ let coverage_mapping_buffer = llvm::build_byte_buffer(|coverage_mapping_buffer| {
+ mapgen.write_coverage_mapping(expressions, counter_regions, coverage_mapping_buffer);
+ });
+ debug_assert!(
+ coverage_mapping_buffer.len() > 0,
+ "Every `FunctionCoverage` should have at least one counter"
+ );
+
+ function_data.push((mangled_function_name, function_source_hash, coverage_mapping_buffer));
+ }
// Encode all filenames referenced by counters/expressions in this module
let filenames_buffer = llvm::build_byte_buffer(|filenames_buffer| {
coverageinfo::write_filenames_section_to_buffer(&mapgen.filenames, filenames_buffer);
});
+ let filenames_size = filenames_buffer.len();
+ let filenames_val = cx.const_bytes(&filenames_buffer[..]);
+ let filenames_ref = coverageinfo::hash_bytes(filenames_buffer);
+
// Generate the LLVM IR representation of the coverage map and store it in a well-known global
- mapgen.save_generated_coverage_map(
- cx,
- function_records,
- filenames_buffer,
- coverage_mappings_buffer,
- );
+ let cov_data_val = mapgen.generate_coverage_map(cx, version, filenames_size, filenames_val);
+
+ for (mangled_function_name, function_source_hash, coverage_mapping_buffer) in function_data {
+ save_function_record(
+ cx,
+ mangled_function_name,
+ function_source_hash,
+ filenames_ref,
+ coverage_mapping_buffer,
+ );
+ }
+
+ // Save the coverage data value to LLVM IR
+ coverageinfo::save_cov_data_to_mod(cx, cov_data_val);
}
struct CoverageMapGenerator {
/// Using the `expressions` and `counter_regions` collected for the current function, generate
/// the `mapping_regions` and `virtual_file_mapping`, and capture any new filenames. Then use
/// LLVM APIs to encode the `virtual_file_mapping`, `expressions`, and `mapping_regions` into
- /// the given `coverage_mappings` byte buffer, compliant with the LLVM Coverage Mapping format.
- fn write_coverage_mappings(
+ /// the given `coverage_mapping` byte buffer, compliant with the LLVM Coverage Mapping format.
+ fn write_coverage_mapping(
&mut self,
expressions: Vec<CounterExpression>,
counter_regions: impl Iterator<Item = (Counter, &'a CodeRegion)>,
- coverage_mappings_buffer: &RustString,
+ coverage_mapping_buffer: &RustString,
) {
let mut counter_regions = counter_regions.collect::<Vec<_>>();
if counter_regions.is_empty() {
virtual_file_mapping,
expressions,
mapping_regions,
- coverage_mappings_buffer,
+ coverage_mapping_buffer,
);
}
- /// Generate and return the function record `Value`
- fn make_function_record(
- &mut self,
- cx: &CodegenCx<'ll, 'tcx>,
- mangled_function_name: String,
- function_source_hash: u64,
- mapping_data_size: usize,
- ) -> &'ll llvm::Value {
- let name_ref = coverageinfo::compute_hash(&mangled_function_name);
- let name_ref_val = cx.const_u64(name_ref);
- let mapping_data_size_val = cx.const_u32(mapping_data_size as u32);
- let func_hash_val = cx.const_u64(function_source_hash);
- cx.const_struct(
- &[name_ref_val, mapping_data_size_val, func_hash_val],
- /*packed=*/ true,
- )
- }
-
- /// Combine the filenames and coverage mappings buffers, construct coverage map header and the
- /// array of function records, and combine everything into the complete coverage map. Save the
- /// coverage map data into the LLVM IR as a static global using a specific, well-known section
- /// and name.
- fn save_generated_coverage_map(
+ /// Construct coverage map header and the array of function records, and combine them into the
+ /// coverage map. Save the coverage map data into the LLVM IR as a static global using a
+ /// specific, well-known section and name.
+ fn generate_coverage_map(
self,
cx: &CodegenCx<'ll, 'tcx>,
- function_records: Vec<&'ll llvm::Value>,
- filenames_buffer: Vec<u8>,
- mut coverage_mappings_buffer: Vec<u8>,
- ) {
- // Concatenate the encoded filenames and encoded coverage mappings, and add additional zero
- // bytes as-needed to ensure 8-byte alignment.
- let mut coverage_size = coverage_mappings_buffer.len();
- let filenames_size = filenames_buffer.len();
- let remaining_bytes =
- (filenames_size + coverage_size) % coverageinfo::COVMAP_VAR_ALIGN_BYTES;
- if remaining_bytes > 0 {
- let pad = coverageinfo::COVMAP_VAR_ALIGN_BYTES - remaining_bytes;
- coverage_mappings_buffer.append(&mut [0].repeat(pad));
- coverage_size += pad;
- }
- let filenames_and_coverage_mappings = [filenames_buffer, coverage_mappings_buffer].concat();
- let filenames_and_coverage_mappings_val =
- cx.const_bytes(&filenames_and_coverage_mappings[..]);
-
- debug!(
- "cov map: n_records = {}, filenames_size = {}, coverage_size = {}, 0-based version = {}",
- function_records.len(),
- filenames_size,
- coverage_size,
- coverageinfo::mapping_version()
- );
+ version: u32,
+ filenames_size: usize,
+ filenames_val: &'ll llvm::Value,
+ ) -> &'ll llvm::Value {
+ debug!("cov map: filenames_size = {}, 0-based version = {}", filenames_size, version);
- // Create the coverage data header
- let n_records_val = cx.const_u32(function_records.len() as u32);
+ // Create the coverage data header (Note, fields 0 and 2 are now always zero,
+ // as of `llvm::coverage::CovMapVersion::Version4`.)
+ let zero_was_n_records_val = cx.const_u32(0);
let filenames_size_val = cx.const_u32(filenames_size as u32);
- let coverage_size_val = cx.const_u32(coverage_size as u32);
- let version_val = cx.const_u32(coverageinfo::mapping_version());
+ let zero_was_coverage_size_val = cx.const_u32(0);
+ let version_val = cx.const_u32(version);
let cov_data_header_val = cx.const_struct(
- &[n_records_val, filenames_size_val, coverage_size_val, version_val],
+ &[zero_was_n_records_val, filenames_size_val, zero_was_coverage_size_val, version_val],
/*packed=*/ false,
);
- // Create the function records array
- let name_ref_from_u64 = cx.type_i64();
- let mapping_data_size_from_u32 = cx.type_i32();
- let func_hash_from_u64 = cx.type_i64();
- let function_record_ty = cx.type_struct(
- &[name_ref_from_u64, mapping_data_size_from_u32, func_hash_from_u64],
- /*packed=*/ true,
- );
- let function_records_val = cx.const_array(function_record_ty, &function_records[..]);
-
// Create the complete LLVM coverage data value to add to the LLVM IR
- let cov_data_val = cx.const_struct(
- &[cov_data_header_val, function_records_val, filenames_and_coverage_mappings_val],
- /*packed=*/ false,
- );
-
- // Save the coverage data value to LLVM IR
- coverageinfo::save_map_to_mod(cx, cov_data_val);
+ cx.const_struct(&[cov_data_header_val, filenames_val], /*packed=*/ false)
}
}
+
+/// Construct a function record and combine it with the function's coverage mapping data.
+/// Save the function record into the LLVM IR as a static global using a
+/// specific, well-known section and name.
+fn save_function_record(
+ cx: &CodegenCx<'ll, 'tcx>,
+ mangled_function_name: String,
+ function_source_hash: u64,
+ filenames_ref: u64,
+ coverage_mapping_buffer: Vec<u8>,
+) {
+ // Concatenate the encoded coverage mappings
+ let coverage_mapping_size = coverage_mapping_buffer.len();
+ let coverage_mapping_val = cx.const_bytes(&coverage_mapping_buffer[..]);
+
+ let func_name_hash = coverageinfo::hash_str(&mangled_function_name);
+ let func_name_hash_val = cx.const_u64(func_name_hash);
+ let coverage_mapping_size_val = cx.const_u32(coverage_mapping_size as u32);
+ let func_hash_val = cx.const_u64(function_source_hash);
+ let filenames_ref_val = cx.const_u64(filenames_ref);
+ let func_record_val = cx.const_struct(
+ &[
+ func_name_hash_val,
+ coverage_mapping_size_val,
+ func_hash_val,
+ filenames_ref_val,
+ coverage_mapping_val,
+ ],
+ /*packed=*/ true,
+ );
+
+ // At the present time, the coverage map for Rust assumes every instrumented function `is_used`.
+ // Note that Clang marks functions as "unused" in `CodeGenPGO::emitEmptyCounterMapping`. (See:
+ // https://github.com/rust-lang/llvm-project/blob/de02a75e398415bad4df27b4547c25b896c8bf3b/clang%2Flib%2FCodeGen%2FCodeGenPGO.cpp#L877-L878
+ // for example.)
+ //
+ // It's not yet clear if or how this may be applied to Rust in the future, but the `is_used`
+ // argument is available and handled similarly.
+ let is_used = true;
+ coverageinfo::save_func_record_to_mod(cx, func_name_hash, func_record_val, is_used);
+}