Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rust function-level coverage now works on external crates #74959

Merged
merged 1 commit into from
Jul 31, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
109 changes: 57 additions & 52 deletions src/librustc_codegen_llvm/intrinsic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,64 +90,69 @@ impl IntrinsicCallMethods<'tcx> for Builder<'a, 'll, 'tcx> {
args: &Vec<Operand<'tcx>>,
caller_instance: ty::Instance<'tcx>,
) -> bool {
let mut is_codegen_intrinsic = true;
// Set `is_codegen_intrinsic` to `false` to bypass `codegen_intrinsic_call()`.

if self.tcx.sess.opts.debugging_opts.instrument_coverage {
// Add the coverage information from the MIR to the Codegen context. Some coverage
// intrinsics are used only to pass along the coverage information (returns `false`
// for `is_codegen_intrinsic()`), but `count_code_region` is also converted into an
// LLVM intrinsic to increment a coverage counter.
match intrinsic {
sym::count_code_region => {
use coverage::count_code_region_args::*;
self.add_counter_region(
caller_instance,
op_to_u64(&args[FUNCTION_SOURCE_HASH]),
op_to_u32(&args[COUNTER_ID]),
op_to_u32(&args[START_BYTE_POS]),
op_to_u32(&args[END_BYTE_POS]),
);
return true; // Also inject the counter increment in the backend
}
sym::coverage_counter_add | sym::coverage_counter_subtract => {
use coverage::coverage_counter_expression_args::*;
self.add_counter_expression_region(
caller_instance,
op_to_u32(&args[EXPRESSION_ID]),
op_to_u32(&args[LEFT_ID]),
if intrinsic == sym::coverage_counter_add {
ExprKind::Add
} else {
ExprKind::Subtract
},
op_to_u32(&args[RIGHT_ID]),
op_to_u32(&args[START_BYTE_POS]),
op_to_u32(&args[END_BYTE_POS]),
);
return false; // Does not inject backend code
// If the intrinsic is from the local MIR, add the coverage information to the Codegen
// context, to be encoded into the local crate's coverage map.
if caller_instance.def_id().is_local() {
// FIXME(richkadel): Make sure to add coverage analysis tests on a crate with
// external crate dependencies, where:
// 1. Both binary and dependent crates are compiled with `-Zinstrument-coverage`
// 2. Only binary is compiled with `-Zinstrument-coverage`
// 3. Only dependent crates are compiled with `-Zinstrument-coverage`
match intrinsic {
sym::count_code_region => {
use coverage::count_code_region_args::*;
self.add_counter_region(
caller_instance,
op_to_u64(&args[FUNCTION_SOURCE_HASH]),
op_to_u32(&args[COUNTER_ID]),
op_to_u32(&args[START_BYTE_POS]),
op_to_u32(&args[END_BYTE_POS]),
);
}
sym::coverage_counter_add | sym::coverage_counter_subtract => {
use coverage::coverage_counter_expression_args::*;
self.add_counter_expression_region(
caller_instance,
op_to_u32(&args[EXPRESSION_ID]),
op_to_u32(&args[LEFT_ID]),
if intrinsic == sym::coverage_counter_add {
ExprKind::Add
} else {
ExprKind::Subtract
},
op_to_u32(&args[RIGHT_ID]),
op_to_u32(&args[START_BYTE_POS]),
op_to_u32(&args[END_BYTE_POS]),
);
}
sym::coverage_unreachable => {
use coverage::coverage_unreachable_args::*;
self.add_unreachable_region(
caller_instance,
op_to_u32(&args[START_BYTE_POS]),
op_to_u32(&args[END_BYTE_POS]),
);
}
_ => {}
}
sym::coverage_unreachable => {
use coverage::coverage_unreachable_args::*;
self.add_unreachable_region(
caller_instance,
op_to_u32(&args[START_BYTE_POS]),
op_to_u32(&args[END_BYTE_POS]),
);
return false; // Does not inject backend code
}

// Only the `count_code_region` coverage intrinsic is translated into an actual LLVM
// intrinsic call (local or not); otherwise, set `is_codegen_intrinsic` to `false`.
match intrinsic {
sym::coverage_counter_add
| sym::coverage_counter_subtract
| sym::coverage_unreachable => {
is_codegen_intrinsic = false;
}
_ => {}
}
} else {
// NOT self.tcx.sess.opts.debugging_opts.instrument_coverage
if intrinsic == sym::count_code_region {
// An external crate may have been pre-compiled with coverage instrumentation, and
// some references from the current crate to the external crate might carry along
// the call terminators to coverage intrinsics, like `count_code_region` (for
// example, when instantiating a generic function). If the current crate has
// `instrument_coverage` disabled, the `count_code_region` call terminators should
// be ignored.
return false; // Do not inject coverage counters inlined from external crates
}
}
true // Unhandled intrinsics should be passed to `codegen_intrinsic_call()`
is_codegen_intrinsic
}

fn codegen_intrinsic_call(
Expand Down
6 changes: 5 additions & 1 deletion src/librustc_codegen_ssa/coverageinfo/map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,11 @@ impl Region {
pub fn new(source_map: &SourceMap, start_byte_pos: u32, end_byte_pos: u32) -> Self {
let start = source_map.lookup_char_pos(BytePos::from_u32(start_byte_pos));
let end = source_map.lookup_char_pos(BytePos::from_u32(end_byte_pos));
assert_eq!(start.file.name, end.file.name);
assert_eq!(
start.file.name, end.file.name,
"Region start ({} -> {:?}) and end ({} -> {:?}) don't come from the same source file!",
start_byte_pos, start, end_byte_pos, end
);
Self { start, end }
}

Expand Down