Skip to content

Commit f7b7fa5

Browse files
authored
Rollup merge of rust-lang#75037 - richkadel:llvm-coverage-map-gen-5.2, r=wesleywiser
Completes support for coverage in external crates Follow-up to rust-lang#74959 : The prior PR corrected for errors encountered when trying to generate the coverage map on source code inlined from external crates (including macros and generics) by avoiding adding external DefIds to the coverage map. This made it possible to generate a coverage report including external crates, but the external crate coverage was incomplete (did not include coverage for the DefIds that were eliminated. The root issue was that the coverage map was converting Span locations to source file and locations, using the SourceMap for the current crate, and this would not work for spans from external crates (compliled with a different SourceMap). The solution was to convert the Spans to filename and location during MIR generation instead, so precompiled external crates would already have the correct source code locations embedded in their MIR, when imported into another crate. @wesleywiser FYI r? @tmandry
2 parents 1b0ff9e + d55f927 commit f7b7fa5

File tree

14 files changed

+352
-325
lines changed

14 files changed

+352
-325
lines changed

library/core/src/intrinsics.rs

+27-9
Original file line numberDiff line numberDiff line change
@@ -1950,15 +1950,20 @@ extern "rust-intrinsic" {
19501950
pub fn ptr_offset_from<T>(ptr: *const T, base: *const T) -> isize;
19511951

19521952
/// Internal placeholder for injecting code coverage counters when the "instrument-coverage"
1953-
/// option is enabled. The placeholder is replaced with `llvm.instrprof.increment` during code
1954-
/// generation.
1953+
/// option is enabled. The source code region information is extracted prior to code generation,
1954+
/// and added to the "coverage map", which is injected into the generated code as additional
1955+
/// data. This intrinsic then triggers the generation of LLVM intrinsic call
1956+
/// `instrprof.increment`, using the remaining args (`function_source_hash` and `index`).
19551957
#[cfg(not(bootstrap))]
19561958
#[lang = "count_code_region"]
19571959
pub fn count_code_region(
19581960
function_source_hash: u64,
19591961
index: u32,
1960-
start_byte_pos: u32,
1961-
end_byte_pos: u32,
1962+
file_name: &'static str,
1963+
start_line: u32,
1964+
start_col: u32,
1965+
end_line: u32,
1966+
end_col: u32,
19621967
);
19631968

19641969
/// Internal marker for code coverage expressions, injected into the MIR when the
@@ -1973,8 +1978,11 @@ extern "rust-intrinsic" {
19731978
index: u32,
19741979
left_index: u32,
19751980
right_index: u32,
1976-
start_byte_pos: u32,
1977-
end_byte_pos: u32,
1981+
file_name: &'static str,
1982+
start_line: u32,
1983+
start_col: u32,
1984+
end_line: u32,
1985+
end_col: u32,
19781986
);
19791987

19801988
/// This marker identifies a code region and two other counters or counter expressions
@@ -1986,14 +1994,24 @@ extern "rust-intrinsic" {
19861994
index: u32,
19871995
left_index: u32,
19881996
right_index: u32,
1989-
start_byte_pos: u32,
1990-
end_byte_pos: u32,
1997+
file_name: &'static str,
1998+
start_line: u32,
1999+
start_col: u32,
2000+
end_line: u32,
2001+
end_col: u32,
19912002
);
19922003

19932004
/// This marker identifies a code region to be added to the "coverage map" to indicate source
19942005
/// code that can never be reached.
19952006
/// (See `coverage_counter_add` for more information.)
1996-
pub fn coverage_unreachable(start_byte_pos: u32, end_byte_pos: u32);
2007+
#[cfg(not(bootstrap))]
2008+
pub fn coverage_unreachable(
2009+
file_name: &'static str,
2010+
start_line: u32,
2011+
start_col: u32,
2012+
end_line: u32,
2013+
end_col: u32,
2014+
);
19972015

19982016
/// See documentation of `<*const T>::guaranteed_eq` for details.
19992017
#[rustc_const_unstable(feature = "const_raw_ptr_comparison", issue = "53020")]

src/librustc_codegen_llvm/coverageinfo/mapgen.rs

+12-12
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ impl CoverageMapGenerator {
9292
fn write_coverage_mappings(
9393
&mut self,
9494
expressions: Vec<CounterExpression>,
95-
counter_regions: impl Iterator<Item = (Counter, &'a Region)>,
95+
counter_regions: impl Iterator<Item = (Counter, &'tcx Region<'tcx>)>,
9696
coverage_mappings_buffer: &RustString,
9797
) {
9898
let mut counter_regions = counter_regions.collect::<Vec<_>>();
@@ -102,7 +102,7 @@ impl CoverageMapGenerator {
102102

103103
let mut virtual_file_mapping = Vec::new();
104104
let mut mapping_regions = Vec::new();
105-
let mut current_file_path = None;
105+
let mut current_file_name = None;
106106
let mut current_file_id = 0;
107107

108108
// Convert the list of (Counter, Region) pairs to an array of `CounterMappingRegion`, sorted
@@ -112,22 +112,22 @@ impl CoverageMapGenerator {
112112
// `filenames` array.
113113
counter_regions.sort_unstable_by_key(|(_counter, region)| *region);
114114
for (counter, region) in counter_regions {
115-
let (file_path, start_line, start_col, end_line, end_col) = region.file_start_and_end();
116-
let same_file = current_file_path.as_ref().map_or(false, |p| p == file_path);
115+
let Region { file_name, start_line, start_col, end_line, end_col } = *region;
116+
let same_file = current_file_name.as_ref().map_or(false, |p| p == file_name);
117117
if !same_file {
118-
if current_file_path.is_some() {
118+
if current_file_name.is_some() {
119119
current_file_id += 1;
120120
}
121-
current_file_path = Some(file_path.clone());
122-
let filename = CString::new(file_path.to_string_lossy().to_string())
123-
.expect("null error converting filename to C string");
124-
debug!(" file_id: {} = '{:?}'", current_file_id, filename);
125-
let filenames_index = match self.filename_to_index.get(&filename) {
121+
current_file_name = Some(file_name.to_string());
122+
let c_filename =
123+
CString::new(file_name).expect("null error converting filename to C string");
124+
debug!(" file_id: {} = '{:?}'", current_file_id, c_filename);
125+
let filenames_index = match self.filename_to_index.get(&c_filename) {
126126
Some(index) => *index,
127127
None => {
128128
let index = self.filenames.len() as u32;
129-
self.filenames.push(filename.clone());
130-
self.filename_to_index.insert(filename.clone(), index);
129+
self.filenames.push(c_filename.clone());
130+
self.filename_to_index.insert(c_filename.clone(), index);
131131
index
132132
}
133133
};

src/librustc_codegen_llvm/coverageinfo/mod.rs

+13-27
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use crate::common::CodegenCx;
66
use libc::c_uint;
77
use llvm::coverageinfo::CounterMappingRegion;
88
use log::debug;
9-
use rustc_codegen_ssa::coverageinfo::map::{CounterExpression, ExprKind, FunctionCoverage};
9+
use rustc_codegen_ssa::coverageinfo::map::{CounterExpression, ExprKind, FunctionCoverage, Region};
1010
use rustc_codegen_ssa::traits::{
1111
BaseTypeMethods, CoverageInfoBuilderMethods, CoverageInfoMethods, StaticMethods,
1212
};
@@ -49,19 +49,18 @@ impl CoverageInfoBuilderMethods<'tcx> for Builder<'a, 'll, 'tcx> {
4949
instance: Instance<'tcx>,
5050
function_source_hash: u64,
5151
id: u32,
52-
start_byte_pos: u32,
53-
end_byte_pos: u32,
52+
region: Region<'tcx>,
5453
) {
5554
debug!(
5655
"adding counter to coverage_regions: instance={:?}, function_source_hash={}, id={}, \
57-
byte range {}..{}",
58-
instance, function_source_hash, id, start_byte_pos, end_byte_pos,
56+
at {:?}",
57+
instance, function_source_hash, id, region,
5958
);
6059
let mut coverage_regions = self.coverage_context().function_coverage_map.borrow_mut();
6160
coverage_regions
6261
.entry(instance)
6362
.or_insert_with(|| FunctionCoverage::new(self.tcx, instance))
64-
.add_counter(function_source_hash, id, start_byte_pos, end_byte_pos);
63+
.add_counter(function_source_hash, id, region);
6564
}
6665

6766
fn add_counter_expression_region(
@@ -71,43 +70,30 @@ impl CoverageInfoBuilderMethods<'tcx> for Builder<'a, 'll, 'tcx> {
7170
lhs: u32,
7271
op: ExprKind,
7372
rhs: u32,
74-
start_byte_pos: u32,
75-
end_byte_pos: u32,
73+
region: Region<'tcx>,
7674
) {
7775
debug!(
7876
"adding counter expression to coverage_regions: instance={:?}, id={}, {} {:?} {}, \
79-
byte range {}..{}",
80-
instance, id_descending_from_max, lhs, op, rhs, start_byte_pos, end_byte_pos,
77+
at {:?}",
78+
instance, id_descending_from_max, lhs, op, rhs, region,
8179
);
8280
let mut coverage_regions = self.coverage_context().function_coverage_map.borrow_mut();
8381
coverage_regions
8482
.entry(instance)
8583
.or_insert_with(|| FunctionCoverage::new(self.tcx, instance))
86-
.add_counter_expression(
87-
id_descending_from_max,
88-
lhs,
89-
op,
90-
rhs,
91-
start_byte_pos,
92-
end_byte_pos,
93-
);
84+
.add_counter_expression(id_descending_from_max, lhs, op, rhs, region);
9485
}
9586

96-
fn add_unreachable_region(
97-
&mut self,
98-
instance: Instance<'tcx>,
99-
start_byte_pos: u32,
100-
end_byte_pos: u32,
101-
) {
87+
fn add_unreachable_region(&mut self, instance: Instance<'tcx>, region: Region<'tcx>) {
10288
debug!(
103-
"adding unreachable code to coverage_regions: instance={:?}, byte range {}..{}",
104-
instance, start_byte_pos, end_byte_pos,
89+
"adding unreachable code to coverage_regions: instance={:?}, at {:?}",
90+
instance, region,
10591
);
10692
let mut coverage_regions = self.coverage_context().function_coverage_map.borrow_mut();
10793
coverage_regions
10894
.entry(instance)
10995
.or_insert_with(|| FunctionCoverage::new(self.tcx, instance))
110-
.add_unreachable_region(start_byte_pos, end_byte_pos);
96+
.add_unreachable_region(region);
11197
}
11298
}
11399

src/librustc_codegen_llvm/intrinsic.rs

+61-60
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use rustc_ast::ast;
1313
use rustc_codegen_ssa::base::{compare_simd_types, to_immediate, wants_msvc_seh};
1414
use rustc_codegen_ssa::common::span_invalid_monomorphization_error;
1515
use rustc_codegen_ssa::common::{IntPredicate, TypeKind};
16-
use rustc_codegen_ssa::coverageinfo::ExprKind;
16+
use rustc_codegen_ssa::coverageinfo;
1717
use rustc_codegen_ssa::glue;
1818
use rustc_codegen_ssa::mir::operand::{OperandRef, OperandValue};
1919
use rustc_codegen_ssa::mir::place::PlaceRef;
@@ -93,64 +93,64 @@ impl IntrinsicCallMethods<'tcx> for Builder<'a, 'll, 'tcx> {
9393
let mut is_codegen_intrinsic = true;
9494
// Set `is_codegen_intrinsic` to `false` to bypass `codegen_intrinsic_call()`.
9595

96-
if self.tcx.sess.opts.debugging_opts.instrument_coverage {
97-
// If the intrinsic is from the local MIR, add the coverage information to the Codegen
98-
// context, to be encoded into the local crate's coverage map.
99-
if caller_instance.def_id().is_local() {
100-
// FIXME(richkadel): Make sure to add coverage analysis tests on a crate with
101-
// external crate dependencies, where:
102-
// 1. Both binary and dependent crates are compiled with `-Zinstrument-coverage`
103-
// 2. Only binary is compiled with `-Zinstrument-coverage`
104-
// 3. Only dependent crates are compiled with `-Zinstrument-coverage`
105-
match intrinsic {
106-
sym::count_code_region => {
107-
use coverage::count_code_region_args::*;
108-
self.add_counter_region(
109-
caller_instance,
110-
op_to_u64(&args[FUNCTION_SOURCE_HASH]),
111-
op_to_u32(&args[COUNTER_ID]),
112-
op_to_u32(&args[START_BYTE_POS]),
113-
op_to_u32(&args[END_BYTE_POS]),
114-
);
115-
}
116-
sym::coverage_counter_add | sym::coverage_counter_subtract => {
117-
use coverage::coverage_counter_expression_args::*;
118-
self.add_counter_expression_region(
119-
caller_instance,
120-
op_to_u32(&args[EXPRESSION_ID]),
121-
op_to_u32(&args[LEFT_ID]),
122-
if intrinsic == sym::coverage_counter_add {
123-
ExprKind::Add
124-
} else {
125-
ExprKind::Subtract
126-
},
127-
op_to_u32(&args[RIGHT_ID]),
128-
op_to_u32(&args[START_BYTE_POS]),
129-
op_to_u32(&args[END_BYTE_POS]),
130-
);
131-
}
132-
sym::coverage_unreachable => {
133-
use coverage::coverage_unreachable_args::*;
134-
self.add_unreachable_region(
135-
caller_instance,
136-
op_to_u32(&args[START_BYTE_POS]),
137-
op_to_u32(&args[END_BYTE_POS]),
138-
);
139-
}
140-
_ => {}
141-
}
96+
// FIXME(richkadel): Make sure to add coverage analysis tests on a crate with
97+
// external crate dependencies, where:
98+
// 1. Both binary and dependent crates are compiled with `-Zinstrument-coverage`
99+
// 2. Only binary is compiled with `-Zinstrument-coverage`
100+
// 3. Only dependent crates are compiled with `-Zinstrument-coverage`
101+
match intrinsic {
102+
sym::count_code_region => {
103+
use coverage::count_code_region_args::*;
104+
self.add_counter_region(
105+
caller_instance,
106+
op_to_u64(&args[FUNCTION_SOURCE_HASH]),
107+
op_to_u32(&args[COUNTER_ID]),
108+
coverageinfo::Region::new(
109+
op_to_str_slice(&args[FILE_NAME]),
110+
op_to_u32(&args[START_LINE]),
111+
op_to_u32(&args[START_COL]),
112+
op_to_u32(&args[END_LINE]),
113+
op_to_u32(&args[END_COL]),
114+
),
115+
);
142116
}
143-
144-
// Only the `count_code_region` coverage intrinsic is translated into an actual LLVM
145-
// intrinsic call (local or not); otherwise, set `is_codegen_intrinsic` to `false`.
146-
match intrinsic {
147-
sym::coverage_counter_add
148-
| sym::coverage_counter_subtract
149-
| sym::coverage_unreachable => {
150-
is_codegen_intrinsic = false;
151-
}
152-
_ => {}
117+
sym::coverage_counter_add | sym::coverage_counter_subtract => {
118+
is_codegen_intrinsic = false;
119+
use coverage::coverage_counter_expression_args::*;
120+
self.add_counter_expression_region(
121+
caller_instance,
122+
op_to_u32(&args[EXPRESSION_ID]),
123+
op_to_u32(&args[LEFT_ID]),
124+
if intrinsic == sym::coverage_counter_add {
125+
coverageinfo::ExprKind::Add
126+
} else {
127+
coverageinfo::ExprKind::Subtract
128+
},
129+
op_to_u32(&args[RIGHT_ID]),
130+
coverageinfo::Region::new(
131+
op_to_str_slice(&args[FILE_NAME]),
132+
op_to_u32(&args[START_LINE]),
133+
op_to_u32(&args[START_COL]),
134+
op_to_u32(&args[END_LINE]),
135+
op_to_u32(&args[END_COL]),
136+
),
137+
);
153138
}
139+
sym::coverage_unreachable => {
140+
is_codegen_intrinsic = false;
141+
use coverage::coverage_unreachable_args::*;
142+
self.add_unreachable_region(
143+
caller_instance,
144+
coverageinfo::Region::new(
145+
op_to_str_slice(&args[FILE_NAME]),
146+
op_to_u32(&args[START_LINE]),
147+
op_to_u32(&args[START_COL]),
148+
op_to_u32(&args[END_LINE]),
149+
op_to_u32(&args[END_COL]),
150+
),
151+
);
152+
}
153+
_ => {}
154154
}
155155
is_codegen_intrinsic
156156
}
@@ -215,9 +215,6 @@ impl IntrinsicCallMethods<'tcx> for Builder<'a, 'll, 'tcx> {
215215
self.call(llfn, &[], None)
216216
}
217217
sym::count_code_region => {
218-
// FIXME(richkadel): The current implementation assumes the MIR for the given
219-
// caller_instance represents a single function. Validate and/or correct if inlining
220-
// and/or monomorphization invalidates these assumptions.
221218
let coverageinfo = tcx.coverageinfo(caller_instance.def_id());
222219
let mangled_fn = tcx.symbol_name(caller_instance);
223220
let (mangled_fn_name, _len_val) = self.const_str(Symbol::intern(mangled_fn.name));
@@ -2283,6 +2280,10 @@ fn float_type_width(ty: Ty<'_>) -> Option<u64> {
22832280
}
22842281
}
22852282

2283+
fn op_to_str_slice<'tcx>(op: &Operand<'tcx>) -> &'tcx str {
2284+
Operand::value_from_const(op).try_to_str_slice().expect("Value is &str")
2285+
}
2286+
22862287
fn op_to_u32<'tcx>(op: &Operand<'tcx>) -> u32 {
22872288
Operand::scalar_from_const(op).to_u32().expect("Scalar is u32")
22882289
}

0 commit comments

Comments
 (0)