Skip to content

Commit 1dc6c3c

Browse files
authored
Rollup merge of #73011 - richkadel:llvm-count-from-mir-pass, r=tmandry
first stage of implementing LLVM code coverage This PR replaces #70680 (WIP toward LLVM Code Coverage for Rust) since I am re-implementing the Rust LLVM code coverage feature in a different part of the compiler (in MIR pass(es) vs AST). This PR updates rustc with `-Zinstrument-coverage` option that injects the llvm intrinsic `instrprof.increment()` for code generation. This initial version only injects counters at the top of each function, and does not yet implement the required coverage map. Upcoming PRs will add the coverage map, and add more counters and/or counter expressions for each conditional code branch. Rust compiler MCP rust-lang/compiler-team#278 Relevant issue: #34701 - Implement support for LLVMs code coverage instrumentation ***[I put together some development notes here, under a separate branch.](https://github.com/richkadel/rust/blob/cfa0b21d34ee64e4ebee226101bd2ef0c6757865/src/test/codegen/coverage-experiments/README-THIS-IS-TEMPORARY.md)***
2 parents 7cc4518 + 36c9014 commit 1dc6c3c

File tree

25 files changed

+383
-4
lines changed

25 files changed

+383
-4
lines changed

config.toml.example

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,8 @@
209209
# Build the sanitizer runtimes
210210
#sanitizers = false
211211

212-
# Build the profiler runtime
212+
# Build the profiler runtime (required when compiling with options that depend
213+
# on this runtime, such as `-C profile-generate` or `-Z instrument-coverage`).
213214
#profiler = false
214215

215216
# Indicates whether the native libraries linked into Cargo will be statically

src/libcore/intrinsics.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1941,6 +1941,13 @@ extern "rust-intrinsic" {
19411941
///
19421942
/// Perma-unstable: do not use.
19431943
pub fn miri_start_panic(payload: *mut u8) -> !;
1944+
1945+
/// Internal placeholder for injecting code coverage counters when the "instrument-coverage"
1946+
/// option is enabled. The placeholder is replaced with `llvm.instrprof.increment` during code
1947+
/// generation.
1948+
#[cfg(not(bootstrap))]
1949+
#[lang = "count_code_region"]
1950+
pub fn count_code_region(index: u32);
19441951
}
19451952

19461953
// Some functions are defined here because they accidentally got made

src/librustc_codegen_llvm/builder.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -997,6 +997,33 @@ impl BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
997997
self.call_lifetime_intrinsic("llvm.lifetime.end.p0i8", ptr, size);
998998
}
999999

1000+
fn instrprof_increment(
1001+
&mut self,
1002+
fn_name: &'ll Value,
1003+
hash: &'ll Value,
1004+
num_counters: &'ll Value,
1005+
index: &'ll Value,
1006+
) -> &'ll Value {
1007+
debug!(
1008+
"instrprof_increment() with args ({:?}, {:?}, {:?}, {:?})",
1009+
fn_name, hash, num_counters, index
1010+
);
1011+
1012+
let llfn = unsafe { llvm::LLVMRustGetInstrprofIncrementIntrinsic(self.cx().llmod) };
1013+
let args = &[fn_name, hash, num_counters, index];
1014+
let args = self.check_call("call", llfn, args);
1015+
1016+
unsafe {
1017+
llvm::LLVMRustBuildCall(
1018+
self.llbuilder,
1019+
llfn,
1020+
args.as_ptr() as *const &llvm::Value,
1021+
args.len() as c_uint,
1022+
None,
1023+
)
1024+
}
1025+
}
1026+
10001027
fn call(
10011028
&mut self,
10021029
llfn: &'ll Value,

src/librustc_codegen_llvm/context.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -749,6 +749,8 @@ impl CodegenCx<'b, 'tcx> {
749749
ifn!("llvm.lifetime.start.p0i8", fn(t_i64, i8p) -> void);
750750
ifn!("llvm.lifetime.end.p0i8", fn(t_i64, i8p) -> void);
751751

752+
ifn!("llvm.instrprof.increment", fn(i8p, t_i64, t_i32, t_i32) -> void);
753+
752754
ifn!("llvm.expect.i1", fn(i1, i1) -> i1);
753755
ifn!("llvm.eh.typeid.for", fn(i8p) -> t_i32);
754756
ifn!("llvm.localescape", fn(...) -> void);

src/librustc_codegen_llvm/intrinsic.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ use crate::type_of::LayoutLlvmExt;
77
use crate::va_arg::emit_va_arg;
88
use crate::value::Value;
99

10+
use log::debug;
11+
1012
use rustc_ast::ast;
1113
use rustc_codegen_ssa::base::{compare_simd_types, to_immediate, wants_msvc_seh};
1214
use rustc_codegen_ssa::common::span_invalid_monomorphization_error;
@@ -21,6 +23,7 @@ use rustc_middle::ty::layout::{FnAbiExt, HasTyCtxt};
2123
use rustc_middle::ty::{self, Ty};
2224
use rustc_middle::{bug, span_bug};
2325
use rustc_span::Span;
26+
use rustc_span::Symbol;
2427
use rustc_target::abi::{self, HasDataLayout, LayoutOf, Primitive};
2528
use rustc_target::spec::PanicStrategy;
2629

@@ -86,6 +89,7 @@ impl IntrinsicCallMethods<'tcx> for Builder<'a, 'll, 'tcx> {
8689
args: &[OperandRef<'tcx, &'ll Value>],
8790
llresult: &'ll Value,
8891
span: Span,
92+
caller_instance: ty::Instance<'tcx>,
8993
) {
9094
let tcx = self.tcx;
9195
let callee_ty = instance.monomorphic_ty(tcx);
@@ -136,6 +140,28 @@ impl IntrinsicCallMethods<'tcx> for Builder<'a, 'll, 'tcx> {
136140
let llfn = self.get_intrinsic(&("llvm.debugtrap"));
137141
self.call(llfn, &[], None)
138142
}
143+
"count_code_region" => {
144+
if let ty::InstanceDef::Item(fn_def_id) = caller_instance.def {
145+
let caller_fn_path = tcx.def_path_str(fn_def_id);
146+
debug!(
147+
"count_code_region to llvm.instrprof.increment(fn_name={})",
148+
caller_fn_path
149+
);
150+
151+
// FIXME(richkadel): (1) Replace raw function name with mangled function name;
152+
// (2) Replace hardcoded `1234` in `hash` with a computed hash (as discussed in)
153+
// the MCP (compiler-team/issues/278); and replace the hardcoded `1` for
154+
// `num_counters` with the actual number of counters per function (when the
155+
// changes are made to inject more than one counter per function).
156+
let (fn_name, _len_val) = self.const_str(Symbol::intern(&caller_fn_path));
157+
let index = args[0].immediate();
158+
let hash = self.const_u64(1234);
159+
let num_counters = self.const_u32(1);
160+
self.instrprof_increment(fn_name, hash, num_counters, index)
161+
} else {
162+
bug!("intrinsic count_code_region: no src.instance");
163+
}
164+
}
139165
"va_start" => self.va_start(args[0].immediate()),
140166
"va_end" => self.va_end(args[0].immediate()),
141167
"va_copy" => {

src/librustc_codegen_llvm/llvm/ffi.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1360,6 +1360,7 @@ extern "C" {
13601360

13611361
// Miscellaneous instructions
13621362
pub fn LLVMBuildPhi(B: &Builder<'a>, Ty: &'a Type, Name: *const c_char) -> &'a Value;
1363+
pub fn LLVMRustGetInstrprofIncrementIntrinsic(M: &Module) -> &'a Value;
13631364
pub fn LLVMRustBuildCall(
13641365
B: &Builder<'a>,
13651366
Fn: &'a Value,

src/librustc_codegen_ssa/back/write.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,12 @@ impl ModuleConfig {
175175
if sess.opts.debugging_opts.profile && !is_compiler_builtins {
176176
passes.push("insert-gcov-profiling".to_owned());
177177
}
178+
179+
// The rustc option `-Zinstrument_coverage` injects intrinsic calls to
180+
// `llvm.instrprof.increment()`, which requires the LLVM `instrprof` pass.
181+
if sess.opts.debugging_opts.instrument_coverage {
182+
passes.push("instrprof".to_owned());
183+
}
178184
passes
179185
},
180186
vec![]

src/librustc_codegen_ssa/mir/block.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -693,6 +693,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
693693
&args,
694694
dest,
695695
terminator.source_info.span,
696+
self.instance,
696697
);
697698

698699
if let ReturnDest::IndirectOperand(dst, _) = ret_dest {

src/librustc_codegen_ssa/traits/builder.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,14 @@ pub trait BuilderMethods<'a, 'tcx>:
260260
/// Called for `StorageDead`
261261
fn lifetime_end(&mut self, ptr: Self::Value, size: Size);
262262

263+
fn instrprof_increment(
264+
&mut self,
265+
fn_name: Self::Value,
266+
hash: Self::Value,
267+
num_counters: Self::Value,
268+
index: Self::Value,
269+
) -> Self::Value;
270+
263271
fn call(
264272
&mut self,
265273
llfn: Self::Value,

src/librustc_codegen_ssa/traits/intrinsic.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ pub trait IntrinsicCallMethods<'tcx>: BackendTypes {
1515
args: &[OperandRef<'tcx, Self::Value>],
1616
llresult: Self::Value,
1717
span: Span,
18+
caller_instance: ty::Instance<'tcx>,
1819
);
1920

2021
fn abort(&mut self);

0 commit comments

Comments
 (0)