about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/librustc_codegen_llvm/coverageinfo/mod.rs12
-rw-r--r--src/librustc_codegen_llvm/intrinsic.rs14
-rw-r--r--src/librustc_codegen_llvm/llvm/ffi.rs2
-rw-r--r--src/librustc_codegen_ssa/back/symbol_export.rs15
-rw-r--r--src/librustc_codegen_ssa/traits/coverageinfo.rs2
-rw-r--r--src/librustc_mir/transform/instrument_coverage.rs15
-rw-r--r--src/rustllvm/CoverageMappingWrapper.cpp5
-rw-r--r--src/test/run-make-fulldeps/instrument-coverage/Makefile78
-rw-r--r--src/test/run-make-fulldeps/instrument-coverage/expected_export_coverage.json2
-rw-r--r--src/test/run-make-fulldeps/instrument-coverage/filecheck-patterns.txt51
-rw-r--r--src/test/run-make-fulldeps/instrument-coverage/testprog.rs (renamed from src/test/run-make-fulldeps/instrument-coverage/main.rs)0
-rw-r--r--src/test/run-make-fulldeps/instrument-coverage/typical_show_coverage.txt4
12 files changed, 161 insertions, 39 deletions
diff --git a/src/librustc_codegen_llvm/coverageinfo/mod.rs b/src/librustc_codegen_llvm/coverageinfo/mod.rs
index 90831f0bcfb..da567293d7b 100644
--- a/src/librustc_codegen_llvm/coverageinfo/mod.rs
+++ b/src/librustc_codegen_llvm/coverageinfo/mod.rs
@@ -8,7 +8,7 @@ use llvm::coverageinfo::CounterMappingRegion;
 use log::debug;
 use rustc_codegen_ssa::coverageinfo::map::{CounterExpression, ExprKind, FunctionCoverage, Region};
 use rustc_codegen_ssa::traits::{
-    BaseTypeMethods, CoverageInfoBuilderMethods, CoverageInfoMethods, StaticMethods,
+    BaseTypeMethods, CoverageInfoBuilderMethods, CoverageInfoMethods, MiscMethods, StaticMethods,
 };
 use rustc_data_structures::fx::FxHashMap;
 use rustc_llvm::RustString;
@@ -44,6 +44,16 @@ impl CoverageInfoMethods for CodegenCx<'ll, 'tcx> {
 }
 
 impl CoverageInfoBuilderMethods<'tcx> for Builder<'a, 'll, 'tcx> {
+    /// Calls llvm::createPGOFuncNameVar() with the given function instance's mangled function name.
+    /// The LLVM API returns an llvm::GlobalVariable containing the function name, with the specific
+    /// variable name and linkage required by LLVM InstrProf source-based coverage instrumentation.
+    fn create_pgo_func_name_var(&self, instance: Instance<'tcx>) -> Self::Value {
+        let llfn = self.cx.get_fn(instance);
+        let mangled_fn_name = CString::new(self.tcx.symbol_name(instance).name)
+            .expect("error converting function name to C string");
+        unsafe { llvm::LLVMRustCoverageCreatePGOFuncNameVar(llfn, mangled_fn_name.as_ptr()) }
+    }
+
     fn add_counter_region(
         &mut self,
         instance: Instance<'tcx>,
diff --git a/src/librustc_codegen_llvm/intrinsic.rs b/src/librustc_codegen_llvm/intrinsic.rs
index dfd5104a31f..14aec1ff5e1 100644
--- a/src/librustc_codegen_llvm/intrinsic.rs
+++ b/src/librustc_codegen_llvm/intrinsic.rs
@@ -215,19 +215,19 @@ impl IntrinsicCallMethods<'tcx> for Builder<'a, 'll, 'tcx> {
                 self.call(llfn, &[], None)
             }
             sym::count_code_region => {
-                let coverageinfo = tcx.coverageinfo(caller_instance.def_id());
-                let mangled_fn = tcx.symbol_name(caller_instance);
-                let (mangled_fn_name, _len_val) = self.const_str(Symbol::intern(mangled_fn.name));
-                let num_counters = self.const_u32(coverageinfo.num_counters);
                 use coverage::count_code_region_args::*;
+                let coverageinfo = tcx.coverageinfo(caller_instance.def_id());
+
+                let fn_name = self.create_pgo_func_name_var(caller_instance);
                 let hash = args[FUNCTION_SOURCE_HASH].immediate();
+                let num_counters = self.const_u32(coverageinfo.num_counters);
                 let index = args[COUNTER_ID].immediate();
                 debug!(
                     "translating Rust intrinsic `count_code_region()` to LLVM intrinsic: \
-                    instrprof.increment(fn_name={}, hash={:?}, num_counters={:?}, index={:?})",
-                    mangled_fn.name, hash, num_counters, index,
+                    instrprof.increment(fn_name={:?}, hash={:?}, num_counters={:?}, index={:?})",
+                    fn_name, hash, num_counters, index,
                 );
-                self.instrprof_increment(mangled_fn_name, hash, num_counters, index)
+                self.instrprof_increment(fn_name, hash, num_counters, index)
             }
             sym::va_start => self.va_start(args[0].immediate()),
             sym::va_end => self.va_end(args[0].immediate()),
diff --git a/src/librustc_codegen_llvm/llvm/ffi.rs b/src/librustc_codegen_llvm/llvm/ffi.rs
index f094ad86894..63b0aa64bd3 100644
--- a/src/librustc_codegen_llvm/llvm/ffi.rs
+++ b/src/librustc_codegen_llvm/llvm/ffi.rs
@@ -1786,6 +1786,8 @@ extern "C" {
         BufferOut: &RustString,
     );
 
+    pub fn LLVMRustCoverageCreatePGOFuncNameVar(F: &'a Value, FuncName: *const c_char)
+    -> &'a Value;
     pub fn LLVMRustCoverageComputeHash(Name: *const c_char) -> u64;
 
     #[allow(improper_ctypes)]
diff --git a/src/librustc_codegen_ssa/back/symbol_export.rs b/src/librustc_codegen_ssa/back/symbol_export.rs
index 7d742e7a7af..e7c789ad210 100644
--- a/src/librustc_codegen_ssa/back/symbol_export.rs
+++ b/src/librustc_codegen_ssa/back/symbol_export.rs
@@ -190,7 +190,9 @@ fn exported_symbols_provider_local(
         }
     }
 
-    if tcx.sess.opts.cg.profile_generate.enabled() {
+    if tcx.sess.opts.debugging_opts.instrument_coverage
+        || tcx.sess.opts.cg.profile_generate.enabled()
+    {
         // These are weak symbols that point to the profile version and the
         // profile name, which need to be treated as exported so LTO doesn't nix
         // them.
@@ -203,17 +205,6 @@ fn exported_symbols_provider_local(
         }));
     }
 
-    if tcx.sess.opts.debugging_opts.instrument_coverage {
-        // Similar to PGO profiling, preserve symbols used by LLVM InstrProf coverage profiling.
-        const COVERAGE_WEAK_SYMBOLS: [&str; 3] =
-            ["__llvm_profile_filename", "__llvm_coverage_mapping", "__llvm_covmap"];
-
-        symbols.extend(COVERAGE_WEAK_SYMBOLS.iter().map(|sym| {
-            let exported_symbol = ExportedSymbol::NoDefId(SymbolName::new(tcx, sym));
-            (exported_symbol, SymbolExportLevel::C)
-        }));
-    }
-
     if tcx.sess.opts.debugging_opts.sanitizer.contains(SanitizerSet::MEMORY) {
         // Similar to profiling, preserve weak msan symbol during LTO.
         const MSAN_WEAK_SYMBOLS: [&str; 2] = ["__msan_track_origins", "__msan_keep_going"];
diff --git a/src/librustc_codegen_ssa/traits/coverageinfo.rs b/src/librustc_codegen_ssa/traits/coverageinfo.rs
index 2b5878f46bc..5602599b0c2 100644
--- a/src/librustc_codegen_ssa/traits/coverageinfo.rs
+++ b/src/librustc_codegen_ssa/traits/coverageinfo.rs
@@ -7,6 +7,8 @@ pub trait CoverageInfoMethods: BackendTypes {
 }
 
 pub trait CoverageInfoBuilderMethods<'tcx>: BackendTypes {
+    fn create_pgo_func_name_var(&self, instance: Instance<'tcx>) -> Self::Value;
+
     fn add_counter_region(
         &mut self,
         instance: Instance<'tcx>,
diff --git a/src/librustc_mir/transform/instrument_coverage.rs b/src/librustc_mir/transform/instrument_coverage.rs
index 5b2954dd5b0..500d66ece06 100644
--- a/src/librustc_mir/transform/instrument_coverage.rs
+++ b/src/librustc_mir/transform/instrument_coverage.rs
@@ -295,6 +295,21 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> {
 
         let (file_name, start_line, start_col, end_line, end_col) = self.code_region(&span);
 
+        // FIXME(richkadel): Note that `const_str()` results in the creation of an `Allocation` to
+        // hold one copy of each unique filename. It looks like that `Allocation` may translate into
+        // the creation of an `@alloc` in LLVM IR that is never actually used by runtime code.
+        //
+        // Example LLVM IR:
+        //
+        // @alloc4 = private unnamed_addr constant <{ [43 x i8] }> \
+        //   <{ [43 x i8] c"C:\\msys64\\home\\richkadel\\rust\\rust_basic.rs" }>, align 1
+        //
+        // Can I flag the alloc as something not to be added to codegen? Or somehow remove it before
+        // it gets added to the LLVM IR? Do we need some kind of reference counting to know it's
+        // not used by any runtime code?
+        //
+        // This question is moot if I convert the Call Terminators to Statements, I believe:
+        // https://rust-lang.zulipchat.com/#narrow/stream/233931-t-compiler.2Fmajor-changes/topic/Implement.20LLVM-compatible.20source-based.20cod.20compiler-team.23278/near/206731748
         args.push(self.const_str(&file_name, inject_at));
         args.push(self.const_u32(start_line, inject_at));
         args.push(self.const_u32(start_col, inject_at));
diff --git a/src/rustllvm/CoverageMappingWrapper.cpp b/src/rustllvm/CoverageMappingWrapper.cpp
index 7c8481540aa..4d15e31df15 100644
--- a/src/rustllvm/CoverageMappingWrapper.cpp
+++ b/src/rustllvm/CoverageMappingWrapper.cpp
@@ -38,6 +38,11 @@ extern "C" void LLVMRustCoverageWriteMappingToBuffer(
   CoverageMappingWriter.write(OS);
 }
 
+extern "C" LLVMValueRef LLVMRustCoverageCreatePGOFuncNameVar(LLVMValueRef F, const char *FuncName) {
+  StringRef FuncNameRef(FuncName);
+  return wrap(createPGOFuncNameVar(*cast<Function>(unwrap(F)), FuncNameRef));
+}
+
 extern "C" uint64_t LLVMRustCoverageComputeHash(const char *Name) {
   StringRef NameRef(Name);
   return IndexedInstrProf::ComputeHash(NameRef);
diff --git a/src/test/run-make-fulldeps/instrument-coverage/Makefile b/src/test/run-make-fulldeps/instrument-coverage/Makefile
index df47305b547..4392cfec080 100644
--- a/src/test/run-make-fulldeps/instrument-coverage/Makefile
+++ b/src/test/run-make-fulldeps/instrument-coverage/Makefile
@@ -3,55 +3,101 @@
 
 # FIXME(richkadel): Debug the following problem, and reenable on Windows (by
 # removing the `# ignore-msvc` directive above). The current implementation
-# generates a segfault when running the instrumented `main` executable,
-# after the `main` program code executes, but before the process terminates.
-# This most likely points to a problem generating the LLVM "main.profraw"
+# generates a segfault when running the instrumented `testprog` executable,
+# after the `main()` function completes, but before the process terminates.
+# This most likely points to a problem generating the LLVM "testprog.profraw"
 # file.
 
 -include ../tools.mk
 
+UNAME = $(shell uname)
+
+ifeq ($(UNAME),Darwin)
+	INSTR_PROF_DATA_SUFFIX=,regular,live_support
+	DATA_SECTION_PREFIX=__DATA,
+	LLVM_COV_SECTION_PREFIX=__LLVM_COV,
+else
+	INSTR_PROF_DATA_SUFFIX=
+	DATA_SECTION_PREFIX=
+	LLVM_COV_SECTION_PREFIX=
+endif
+
 # This test makes sure that LLVM coverage maps are genereated in LLVM IR.
 
 COMMON_FLAGS=-Zinstrument-coverage
 
 all:
 	# Compile the test program with instrumentation, and also generate LLVM IR
-	$(RUSTC) $(COMMON_FLAGS) main.rs
+	$(RUSTC) $(COMMON_FLAGS) testprog.rs \
+			--emit=link,llvm-ir
+
+	# check the LLVM IR
+ifdef IS_WIN32
+	cat "$(TMPDIR)"/testprog.ll | "$(LLVM_FILECHECK)" filecheck-patterns.txt \
+			-check-prefixes=CHECK,WIN32 \
+			-DPRIVATE_GLOBAL="internal global" \
+			-DINSTR_PROF_DATA=".lprfd$$M" \
+			-DINSTR_PROF_NAME=".lprfn$$M" \
+			-DINSTR_PROF_CNTS=".lprfc$$M" \
+			-DINSTR_PROF_VALS=".lprfv$$M" \
+			-DINSTR_PROF_VNODES=".lprfnd$$M" \
+			-DINSTR_PROF_COVMAP=".lcovmap$$M" \
+			-DINSTR_PROF_ORDERFILE=".lorderfile$$M"
+else
+	cat "$(TMPDIR)"/testprog.ll | "$(LLVM_FILECHECK)" filecheck-patterns.txt \
+			-check-prefixes=CHECK \
+			-DPRIVATE_GLOBAL="private global" \
+			-DINSTR_PROF_DATA="$(DATA_SECTION_PREFIX)__llvm_prf_data$(INSTR_PROF_DATA_SUFFIX)" \
+			-DINSTR_PROF_NAME="$(DATA_SECTION_PREFIX)__llvm_prf_names" \
+			-DINSTR_PROF_CNTS="$(DATA_SECTION_PREFIX)__llvm_prf_cnts" \
+			-DINSTR_PROF_VALS="$(DATA_SECTION_PREFIX)__llvm_prf_vals" \
+			-DINSTR_PROF_VNODES="$(DATA_SECTION_PREFIX)__llvm_prf_vnds" \
+			-DINSTR_PROF_COVMAP="$(LLVM_COV_SECTION_PREFIX)__llvm_covmap" \
+			-DINSTR_PROF_ORDERFILE="$(DATA_SECTION_PREFIX)__llvm_orderfile"
+endif
 
 	# Run it in order to generate some profiling data,
 	# with `LLVM_PROFILE_FILE=<profdata_file>` environment variable set to
 	# output the coverage stats for this run.
-	LLVM_PROFILE_FILE="$(TMPDIR)"/main.profraw \
-	  $(call RUN,main)
+	LLVM_PROFILE_FILE="$(TMPDIR)"/testprog.profraw \
+			$(call RUN,testprog)
 
 	# Postprocess the profiling data so it can be used by the llvm-cov tool
 	"$(LLVM_BIN_DIR)"/llvm-profdata merge --sparse \
-	  "$(TMPDIR)"/main.profraw \
-		-o "$(TMPDIR)"/main.profdata
+			"$(TMPDIR)"/testprog.profraw \
+			-o "$(TMPDIR)"/testprog.profdata
 
 	# Generate a coverage report using `llvm-cov show`. The output ordering
 	# can be non-deterministic, so ignore the return status. If the test fails
 	# when comparing the JSON `export`, the `show` output may be useful when
 	# debugging.
 	"$(LLVM_BIN_DIR)"/llvm-cov show \
-	  --Xdemangler="$(RUST_DEMANGLER)" \
-	  --show-line-counts-or-regions \
-	  --instr-profile="$(TMPDIR)"/main.profdata \
-		$(call BIN,"$(TMPDIR)"/main) \
+			--Xdemangler="$(RUST_DEMANGLER)" \
+			--show-line-counts-or-regions \
+			--instr-profile="$(TMPDIR)"/testprog.profdata \
+			$(call BIN,"$(TMPDIR)"/testprog) \
 		> "$(TMPDIR)"/actual_show_coverage.txt
 
+ifdef RUSTC_BLESS_TEST
+	cp "$(TMPDIR)"/actual_show_coverage.txt typical_show_coverage.txt
+else
 	# Compare the show coverage output
 	$(DIFF) typical_show_coverage.txt "$(TMPDIR)"/actual_show_coverage.txt || \
-	  >&2 echo 'diff failed for `llvm-cov show` (might not be an error)'
+		>&2 echo 'diff failed for `llvm-cov show` (might not be an error)'
+endif
 
 	# Generate a coverage report in JSON, using `llvm-cov export`, and fail if
 	# there are differences from the expected output.
 	"$(LLVM_BIN_DIR)"/llvm-cov export \
-	  --summary-only \
-	  --instr-profile="$(TMPDIR)"/main.profdata \
-		$(call BIN,"$(TMPDIR)"/main) \
+			--summary-only \
+			--instr-profile="$(TMPDIR)"/testprog.profdata \
+			$(call BIN,"$(TMPDIR)"/testprog) \
 		| "$(PYTHON)" prettify_json.py \
 		> "$(TMPDIR)"/actual_export_coverage.json
 
+ifdef RUSTC_BLESS_TEST
+	cp "$(TMPDIR)"/actual_export_coverage.json expected_export_coverage.json
+else
 	# Check that the exported JSON coverage data matches what we expect
 	$(DIFF) expected_export_coverage.json "$(TMPDIR)"/actual_export_coverage.json
+endif
diff --git a/src/test/run-make-fulldeps/instrument-coverage/expected_export_coverage.json b/src/test/run-make-fulldeps/instrument-coverage/expected_export_coverage.json
index 9d739a89114..5881cf4b7db 100644
--- a/src/test/run-make-fulldeps/instrument-coverage/expected_export_coverage.json
+++ b/src/test/run-make-fulldeps/instrument-coverage/expected_export_coverage.json
@@ -3,7 +3,7 @@
     {
       "files": [
         {
-          "filename": "main.rs",
+          "filename": "testprog.rs",
           "summary": {
             "functions": {
               "count": 7,
diff --git a/src/test/run-make-fulldeps/instrument-coverage/filecheck-patterns.txt b/src/test/run-make-fulldeps/instrument-coverage/filecheck-patterns.txt
new file mode 100644
index 00000000000..5a7cc9a1882
--- /dev/null
+++ b/src/test/run-make-fulldeps/instrument-coverage/filecheck-patterns.txt
@@ -0,0 +1,51 @@
+# Check for metadata, variables, declarations, and function definitions injected
+# into LLVM IR when compiling with -Zinstrument-coverage.
+
+WIN32:      $__llvm_profile_runtime_user = comdat any
+
+CHECK:      @__llvm_coverage_mapping = internal constant
+CHECK-SAME: section "[[INSTR_PROF_COVMAP]]", align 8
+
+WIN32:      @__llvm_profile_runtime = external global i32
+
+CHECK:      @__profc__R{{[a-zA-Z0-9_]+}}testprog14will_be_called = [[PRIVATE_GLOBAL]]
+CHECK-SAME: section "[[INSTR_PROF_CNTS]]", align 8
+
+CHECK:      @__profd__R{{[a-zA-Z0-9_]+}}testprog14will_be_called = [[PRIVATE_GLOBAL]]
+CHECK-SAME: @__profc__R{{[a-zA-Z0-9_]+}}testprog14will_be_called,
+CHECK-SAME: ()* @_R{{[a-zA-Z0-9_]+}}testprog14will_be_called to i8*),
+CHECK-SAME: section "[[INSTR_PROF_DATA]]", align 8
+
+CHECK:      @__profc__R{{[a-zA-Z0-9_]+}}testprog4main = [[PRIVATE_GLOBAL]]
+CHECK-SAME: section "[[INSTR_PROF_CNTS]]", align 8
+
+CHECK:      @__profd__R{{[a-zA-Z0-9_]+}}testprog4main = [[PRIVATE_GLOBAL]]
+CHECK-SAME: @__profc__R{{[a-zA-Z0-9_]+}}testprog4main,
+CHECK-SAME: ()* @_R{{[a-zA-Z0-9_]+}}testprog4main to i8*),
+CHECK-SAME: section "[[INSTR_PROF_DATA]]", align 8
+
+CHECK:      @__llvm_prf_nm = private constant
+CHECK-SAME: section "[[INSTR_PROF_NAME]]", align 1
+
+CHECK:      @llvm.used = appending global
+CHECK-SAME: i8* bitcast ({ {{.*}} }* @__llvm_coverage_mapping to i8*)
+WIN32-SAME: i8* bitcast (i32 ()* @__llvm_profile_runtime_user to i8*)
+CHECK-SAME: i8* bitcast ({ {{.*}} }* @__profd__R{{[a-zA-Z0-9_]*}}testprog4main to i8*)
+CHECK-SAME: i8* getelementptr inbounds ({{.*}}* @__llvm_prf_nm, i32 0, i32 0)
+CHECK-SAME: section "llvm.metadata"
+
+CHECK:      define hidden { {{.*}} } @_R{{[a-zA-Z0-9_]+}}testprog14will_be_called() unnamed_addr #{{[0-9]+}} {
+CHECK-NEXT: start:
+CHECK-NOT:  bb{{[0-9]+}}:
+CHECK:      %pgocount = load i64, i64* getelementptr inbounds
+CHECK-SAME: * @__profc__R{{[a-zA-Z0-9_]+}}testprog14will_be_called,
+
+CHECK:      declare void @llvm.instrprof.increment(i8*, i64, i32, i32) #[[LLVM_INSTRPROF_INCREMENT_ATTR:[0-9]+]]
+
+WIN32:      define linkonce_odr hidden i32 @__llvm_profile_runtime_user() #[[LLVM_PROFILE_RUNTIME_USER_ATTR:[0-9]+]] comdat {
+WIN32-NEXT: %1 = load i32, i32* @__llvm_profile_runtime
+WIN32-NEXT: ret i32 %1
+WIN32-NEXT: }
+
+CHECK:      attributes #[[LLVM_INSTRPROF_INCREMENT_ATTR]] = { nounwind }
+WIN32:      attributes #[[LLVM_PROFILE_RUNTIME_USER_ATTR]] = { noinline }
\ No newline at end of file
diff --git a/src/test/run-make-fulldeps/instrument-coverage/main.rs b/src/test/run-make-fulldeps/instrument-coverage/testprog.rs
index 358c25677ae..358c25677ae 100644
--- a/src/test/run-make-fulldeps/instrument-coverage/main.rs
+++ b/src/test/run-make-fulldeps/instrument-coverage/testprog.rs
diff --git a/src/test/run-make-fulldeps/instrument-coverage/typical_show_coverage.txt b/src/test/run-make-fulldeps/instrument-coverage/typical_show_coverage.txt
index 9c593d0809d..ae123afff04 100644
--- a/src/test/run-make-fulldeps/instrument-coverage/typical_show_coverage.txt
+++ b/src/test/run-make-fulldeps/instrument-coverage/typical_show_coverage.txt
@@ -25,14 +25,14 @@
    25|      2|    }
    26|      2|}
   ------------------
-  | main[317d481089b8c8fe]::wrap_with::<main[317d481089b8c8fe]::main::{closure#0}, &str>:
+  | testprog[317d481089b8c8fe]::wrap_with::<testprog[317d481089b8c8fe]::main::{closure#0}, &str>:
   |   22|      1|{
   |   23|      1|    if should_wrap {
   |   24|      1|        wrapper(&inner)
   |   25|      1|    }
   |   26|      1|}
   ------------------
-  | main[317d481089b8c8fe]::wrap_with::<main[317d481089b8c8fe]::main::{closure#1}, &str>:
+  | testprog[317d481089b8c8fe]::wrap_with::<testprog[317d481089b8c8fe]::main::{closure#1}, &str>:
   |   22|      1|{
   |   23|      1|    if should_wrap {
   |   24|      1|        wrapper(&inner)