2020-07-02 18:27:15 +00:00
|
|
|
use crate::llvm;
|
|
|
|
|
coverage bug fixes and optimization support
Adjusted LLVM codegen for code compiled with `-Zinstrument-coverage` to
address multiple, somewhat related issues.
Fixed a significant flaw in prior coverage solution: Every counter
generated a new counter variable, but there should have only been one
counter variable per function. This appears to have bloated .profraw
files significantly. (For a small program, it increased the size by
about 40%. I have not tested large programs, but there is anecdotal
evidence that profraw files were way too large. This is a good fix,
regardless, but hopefully it also addresses related issues.
Fixes: #82144
Invalid LLVM coverage data produced when compiled with -C opt-level=1
Existing tests now work up to at least `opt-level=3`. This required a
detailed analysis of the LLVM IR, comparisons with Clang C++ LLVM IR
when compiled with coverage, and a lot of trial and error with codegen
adjustments.
The biggest hurdle was figuring out how to continue to support coverage
results for unused functions and generics. Rust's coverage results have
three advantages over Clang's coverage results:
1. Rust's coverage map does not include any overlapping code regions,
making coverage counting unambiguous.
2. Rust generates coverage results (showing zero counts) for all unused
functions, including generics. (Clang does not generate coverage for
uninstantiated template functions.)
3. Rust's unused functions produce minimal stubbed functions in LLVM IR,
sufficient for including in the coverage results; while Clang must
generate the complete LLVM IR for each unused function, even though
it will never be called.
This PR removes the previous hack of attempting to inject coverage into
some other existing function instance, and generates dedicated instances
for each unused function. This change, and a few other adjustments
(similar to what is required for `-C link-dead-code`, but with lower
impact), makes it possible to support LLVM optimizations.
Fixes: #79651
Coverage report: "Unexecuted instantiation:..." for a generic function
from multiple crates
Fixed by removing the aforementioned hack. Some "Unexecuted
instantiation" notices are unavoidable, as explained in the
`used_crate.rs` test, but `-Zinstrument-coverage` has new options to
back off support for either unused generics, or all unused functions,
which avoids the notice, at the cost of less coverage of unused
functions.
Fixes: #82875
Invalid LLVM coverage data produced with crate brotli_decompressor
Fixed by disabling the LLVM function attribute that forces inlining, if
`-Z instrument-coverage` is enabled. This attribute is applied to
Rust functions with `#[inline(always)], and in some cases, the forced
inlining breaks coverage instrumentation and reports.
2021-03-15 23:32:45 +00:00
|
|
|
use crate::abi::{Abi, FnAbi};
|
2020-06-22 06:29:08 +00:00
|
|
|
use crate::builder::Builder;
|
|
|
|
use crate::common::CodegenCx;
|
2020-07-02 18:27:15 +00:00
|
|
|
|
|
|
|
use libc::c_uint;
|
2020-07-29 06:09:16 +00:00
|
|
|
use llvm::coverageinfo::CounterMappingRegion;
|
2020-08-15 11:42:13 +00:00
|
|
|
use rustc_codegen_ssa::coverageinfo::map::{CounterExpression, FunctionCoverage};
|
2020-07-02 18:27:15 +00:00
|
|
|
use rustc_codegen_ssa::traits::{
|
coverage bug fixes and optimization support
Adjusted LLVM codegen for code compiled with `-Zinstrument-coverage` to
address multiple, somewhat related issues.
Fixed a significant flaw in prior coverage solution: Every counter
generated a new counter variable, but there should have only been one
counter variable per function. This appears to have bloated .profraw
files significantly. (For a small program, it increased the size by
about 40%. I have not tested large programs, but there is anecdotal
evidence that profraw files were way too large. This is a good fix,
regardless, but hopefully it also addresses related issues.
Fixes: #82144
Invalid LLVM coverage data produced when compiled with -C opt-level=1
Existing tests now work up to at least `opt-level=3`. This required a
detailed analysis of the LLVM IR, comparisons with Clang C++ LLVM IR
when compiled with coverage, and a lot of trial and error with codegen
adjustments.
The biggest hurdle was figuring out how to continue to support coverage
results for unused functions and generics. Rust's coverage results have
three advantages over Clang's coverage results:
1. Rust's coverage map does not include any overlapping code regions,
making coverage counting unambiguous.
2. Rust generates coverage results (showing zero counts) for all unused
functions, including generics. (Clang does not generate coverage for
uninstantiated template functions.)
3. Rust's unused functions produce minimal stubbed functions in LLVM IR,
sufficient for including in the coverage results; while Clang must
generate the complete LLVM IR for each unused function, even though
it will never be called.
This PR removes the previous hack of attempting to inject coverage into
some other existing function instance, and generates dedicated instances
for each unused function. This change, and a few other adjustments
(similar to what is required for `-C link-dead-code`, but with lower
impact), makes it possible to support LLVM optimizations.
Fixes: #79651
Coverage report: "Unexecuted instantiation:..." for a generic function
from multiple crates
Fixed by removing the aforementioned hack. Some "Unexecuted
instantiation" notices are unavoidable, as explained in the
`used_crate.rs` test, but `-Zinstrument-coverage` has new options to
back off support for either unused generics, or all unused functions,
which avoids the notice, at the cost of less coverage of unused
functions.
Fixes: #82875
Invalid LLVM coverage data produced with crate brotli_decompressor
Fixed by disabling the LLVM function attribute that forces inlining, if
`-Z instrument-coverage` is enabled. This attribute is applied to
Rust functions with `#[inline(always)], and in some cases, the forced
inlining breaks coverage instrumentation and reports.
2021-03-15 23:32:45 +00:00
|
|
|
BaseTypeMethods, BuilderMethods, ConstMethods, CoverageInfoBuilderMethods, CoverageInfoMethods,
|
|
|
|
MiscMethods, StaticMethods,
|
2020-07-02 18:27:15 +00:00
|
|
|
};
|
2020-06-22 06:29:08 +00:00
|
|
|
use rustc_data_structures::fx::FxHashMap;
|
coverage bug fixes and optimization support
Adjusted LLVM codegen for code compiled with `-Zinstrument-coverage` to
address multiple, somewhat related issues.
Fixed a significant flaw in prior coverage solution: Every counter
generated a new counter variable, but there should have only been one
counter variable per function. This appears to have bloated .profraw
files significantly. (For a small program, it increased the size by
about 40%. I have not tested large programs, but there is anecdotal
evidence that profraw files were way too large. This is a good fix,
regardless, but hopefully it also addresses related issues.
Fixes: #82144
Invalid LLVM coverage data produced when compiled with -C opt-level=1
Existing tests now work up to at least `opt-level=3`. This required a
detailed analysis of the LLVM IR, comparisons with Clang C++ LLVM IR
when compiled with coverage, and a lot of trial and error with codegen
adjustments.
The biggest hurdle was figuring out how to continue to support coverage
results for unused functions and generics. Rust's coverage results have
three advantages over Clang's coverage results:
1. Rust's coverage map does not include any overlapping code regions,
making coverage counting unambiguous.
2. Rust generates coverage results (showing zero counts) for all unused
functions, including generics. (Clang does not generate coverage for
uninstantiated template functions.)
3. Rust's unused functions produce minimal stubbed functions in LLVM IR,
sufficient for including in the coverage results; while Clang must
generate the complete LLVM IR for each unused function, even though
it will never be called.
This PR removes the previous hack of attempting to inject coverage into
some other existing function instance, and generates dedicated instances
for each unused function. This change, and a few other adjustments
(similar to what is required for `-C link-dead-code`, but with lower
impact), makes it possible to support LLVM optimizations.
Fixes: #79651
Coverage report: "Unexecuted instantiation:..." for a generic function
from multiple crates
Fixed by removing the aforementioned hack. Some "Unexecuted
instantiation" notices are unavoidable, as explained in the
`used_crate.rs` test, but `-Zinstrument-coverage` has new options to
back off support for either unused generics, or all unused functions,
which avoids the notice, at the cost of less coverage of unused
functions.
Fixes: #82875
Invalid LLVM coverage data produced with crate brotli_decompressor
Fixed by disabling the LLVM function attribute that forces inlining, if
`-Z instrument-coverage` is enabled. This attribute is applied to
Rust functions with `#[inline(always)], and in some cases, the forced
inlining breaks coverage instrumentation and reports.
2021-03-15 23:32:45 +00:00
|
|
|
use rustc_hir as hir;
|
|
|
|
use rustc_hir::def_id::DefId;
|
2020-07-02 18:27:15 +00:00
|
|
|
use rustc_llvm::RustString;
|
coverage bug fixes and optimization support
Adjusted LLVM codegen for code compiled with `-Zinstrument-coverage` to
address multiple, somewhat related issues.
Fixed a significant flaw in prior coverage solution: Every counter
generated a new counter variable, but there should have only been one
counter variable per function. This appears to have bloated .profraw
files significantly. (For a small program, it increased the size by
about 40%. I have not tested large programs, but there is anecdotal
evidence that profraw files were way too large. This is a good fix,
regardless, but hopefully it also addresses related issues.
Fixes: #82144
Invalid LLVM coverage data produced when compiled with -C opt-level=1
Existing tests now work up to at least `opt-level=3`. This required a
detailed analysis of the LLVM IR, comparisons with Clang C++ LLVM IR
when compiled with coverage, and a lot of trial and error with codegen
adjustments.
The biggest hurdle was figuring out how to continue to support coverage
results for unused functions and generics. Rust's coverage results have
three advantages over Clang's coverage results:
1. Rust's coverage map does not include any overlapping code regions,
making coverage counting unambiguous.
2. Rust generates coverage results (showing zero counts) for all unused
functions, including generics. (Clang does not generate coverage for
uninstantiated template functions.)
3. Rust's unused functions produce minimal stubbed functions in LLVM IR,
sufficient for including in the coverage results; while Clang must
generate the complete LLVM IR for each unused function, even though
it will never be called.
This PR removes the previous hack of attempting to inject coverage into
some other existing function instance, and generates dedicated instances
for each unused function. This change, and a few other adjustments
(similar to what is required for `-C link-dead-code`, but with lower
impact), makes it possible to support LLVM optimizations.
Fixes: #79651
Coverage report: "Unexecuted instantiation:..." for a generic function
from multiple crates
Fixed by removing the aforementioned hack. Some "Unexecuted
instantiation" notices are unavoidable, as explained in the
`used_crate.rs` test, but `-Zinstrument-coverage` has new options to
back off support for either unused generics, or all unused functions,
which avoids the notice, at the cost of less coverage of unused
functions.
Fixes: #82875
Invalid LLVM coverage data produced with crate brotli_decompressor
Fixed by disabling the LLVM function attribute that forces inlining, if
`-Z instrument-coverage` is enabled. This attribute is applied to
Rust functions with `#[inline(always)], and in some cases, the forced
inlining breaks coverage instrumentation and reports.
2021-03-15 23:32:45 +00:00
|
|
|
use rustc_middle::bug;
|
2020-08-15 11:42:13 +00:00
|
|
|
use rustc_middle::mir::coverage::{
|
2020-10-05 23:36:10 +00:00
|
|
|
CodeRegion, CounterValueReference, ExpressionOperandId, InjectedExpressionId, Op,
|
2020-08-15 11:42:13 +00:00
|
|
|
};
|
coverage bug fixes and optimization support
Adjusted LLVM codegen for code compiled with `-Zinstrument-coverage` to
address multiple, somewhat related issues.
Fixed a significant flaw in prior coverage solution: Every counter
generated a new counter variable, but there should have only been one
counter variable per function. This appears to have bloated .profraw
files significantly. (For a small program, it increased the size by
about 40%. I have not tested large programs, but there is anecdotal
evidence that profraw files were way too large. This is a good fix,
regardless, but hopefully it also addresses related issues.
Fixes: #82144
Invalid LLVM coverage data produced when compiled with -C opt-level=1
Existing tests now work up to at least `opt-level=3`. This required a
detailed analysis of the LLVM IR, comparisons with Clang C++ LLVM IR
when compiled with coverage, and a lot of trial and error with codegen
adjustments.
The biggest hurdle was figuring out how to continue to support coverage
results for unused functions and generics. Rust's coverage results have
three advantages over Clang's coverage results:
1. Rust's coverage map does not include any overlapping code regions,
making coverage counting unambiguous.
2. Rust generates coverage results (showing zero counts) for all unused
functions, including generics. (Clang does not generate coverage for
uninstantiated template functions.)
3. Rust's unused functions produce minimal stubbed functions in LLVM IR,
sufficient for including in the coverage results; while Clang must
generate the complete LLVM IR for each unused function, even though
it will never be called.
This PR removes the previous hack of attempting to inject coverage into
some other existing function instance, and generates dedicated instances
for each unused function. This change, and a few other adjustments
(similar to what is required for `-C link-dead-code`, but with lower
impact), makes it possible to support LLVM optimizations.
Fixes: #79651
Coverage report: "Unexecuted instantiation:..." for a generic function
from multiple crates
Fixed by removing the aforementioned hack. Some "Unexecuted
instantiation" notices are unavoidable, as explained in the
`used_crate.rs` test, but `-Zinstrument-coverage` has new options to
back off support for either unused generics, or all unused functions,
which avoids the notice, at the cost of less coverage of unused
functions.
Fixes: #82875
Invalid LLVM coverage data produced with crate brotli_decompressor
Fixed by disabling the LLVM function attribute that forces inlining, if
`-Z instrument-coverage` is enabled. This attribute is applied to
Rust functions with `#[inline(always)], and in some cases, the forced
inlining breaks coverage instrumentation and reports.
2021-03-15 23:32:45 +00:00
|
|
|
use rustc_middle::ty;
|
|
|
|
use rustc_middle::ty::layout::FnAbiExt;
|
|
|
|
use rustc_middle::ty::subst::InternalSubsts;
|
2020-06-22 06:29:08 +00:00
|
|
|
use rustc_middle::ty::Instance;
|
|
|
|
|
|
|
|
use std::cell::RefCell;
|
2020-07-02 18:27:15 +00:00
|
|
|
use std::ffi::CString;
|
|
|
|
|
coverage bug fixes and optimization support
Adjusted LLVM codegen for code compiled with `-Zinstrument-coverage` to
address multiple, somewhat related issues.
Fixed a significant flaw in prior coverage solution: Every counter
generated a new counter variable, but there should have only been one
counter variable per function. This appears to have bloated .profraw
files significantly. (For a small program, it increased the size by
about 40%. I have not tested large programs, but there is anecdotal
evidence that profraw files were way too large. This is a good fix,
regardless, but hopefully it also addresses related issues.
Fixes: #82144
Invalid LLVM coverage data produced when compiled with -C opt-level=1
Existing tests now work up to at least `opt-level=3`. This required a
detailed analysis of the LLVM IR, comparisons with Clang C++ LLVM IR
when compiled with coverage, and a lot of trial and error with codegen
adjustments.
The biggest hurdle was figuring out how to continue to support coverage
results for unused functions and generics. Rust's coverage results have
three advantages over Clang's coverage results:
1. Rust's coverage map does not include any overlapping code regions,
making coverage counting unambiguous.
2. Rust generates coverage results (showing zero counts) for all unused
functions, including generics. (Clang does not generate coverage for
uninstantiated template functions.)
3. Rust's unused functions produce minimal stubbed functions in LLVM IR,
sufficient for including in the coverage results; while Clang must
generate the complete LLVM IR for each unused function, even though
it will never be called.
This PR removes the previous hack of attempting to inject coverage into
some other existing function instance, and generates dedicated instances
for each unused function. This change, and a few other adjustments
(similar to what is required for `-C link-dead-code`, but with lower
impact), makes it possible to support LLVM optimizations.
Fixes: #79651
Coverage report: "Unexecuted instantiation:..." for a generic function
from multiple crates
Fixed by removing the aforementioned hack. Some "Unexecuted
instantiation" notices are unavoidable, as explained in the
`used_crate.rs` test, but `-Zinstrument-coverage` has new options to
back off support for either unused generics, or all unused functions,
which avoids the notice, at the cost of less coverage of unused
functions.
Fixes: #82875
Invalid LLVM coverage data produced with crate brotli_decompressor
Fixed by disabling the LLVM function attribute that forces inlining, if
`-Z instrument-coverage` is enabled. This attribute is applied to
Rust functions with `#[inline(always)], and in some cases, the forced
inlining breaks coverage instrumentation and reports.
2021-03-15 23:32:45 +00:00
|
|
|
use std::iter;
|
2020-08-15 11:42:13 +00:00
|
|
|
use tracing::debug;
|
|
|
|
|
2020-07-02 18:27:15 +00:00
|
|
|
pub mod mapgen;
|
|
|
|
|
coverage bug fixes and optimization support
Adjusted LLVM codegen for code compiled with `-Zinstrument-coverage` to
address multiple, somewhat related issues.
Fixed a significant flaw in prior coverage solution: Every counter
generated a new counter variable, but there should have only been one
counter variable per function. This appears to have bloated .profraw
files significantly. (For a small program, it increased the size by
about 40%. I have not tested large programs, but there is anecdotal
evidence that profraw files were way too large. This is a good fix,
regardless, but hopefully it also addresses related issues.
Fixes: #82144
Invalid LLVM coverage data produced when compiled with -C opt-level=1
Existing tests now work up to at least `opt-level=3`. This required a
detailed analysis of the LLVM IR, comparisons with Clang C++ LLVM IR
when compiled with coverage, and a lot of trial and error with codegen
adjustments.
The biggest hurdle was figuring out how to continue to support coverage
results for unused functions and generics. Rust's coverage results have
three advantages over Clang's coverage results:
1. Rust's coverage map does not include any overlapping code regions,
making coverage counting unambiguous.
2. Rust generates coverage results (showing zero counts) for all unused
functions, including generics. (Clang does not generate coverage for
uninstantiated template functions.)
3. Rust's unused functions produce minimal stubbed functions in LLVM IR,
sufficient for including in the coverage results; while Clang must
generate the complete LLVM IR for each unused function, even though
it will never be called.
This PR removes the previous hack of attempting to inject coverage into
some other existing function instance, and generates dedicated instances
for each unused function. This change, and a few other adjustments
(similar to what is required for `-C link-dead-code`, but with lower
impact), makes it possible to support LLVM optimizations.
Fixes: #79651
Coverage report: "Unexecuted instantiation:..." for a generic function
from multiple crates
Fixed by removing the aforementioned hack. Some "Unexecuted
instantiation" notices are unavoidable, as explained in the
`used_crate.rs` test, but `-Zinstrument-coverage` has new options to
back off support for either unused generics, or all unused functions,
which avoids the notice, at the cost of less coverage of unused
functions.
Fixes: #82875
Invalid LLVM coverage data produced with crate brotli_decompressor
Fixed by disabling the LLVM function attribute that forces inlining, if
`-Z instrument-coverage` is enabled. This attribute is applied to
Rust functions with `#[inline(always)], and in some cases, the forced
inlining breaks coverage instrumentation and reports.
2021-03-15 23:32:45 +00:00
|
|
|
const UNUSED_FUNCTION_COUNTER_ID: CounterValueReference = CounterValueReference::START;
|
|
|
|
|
2020-11-23 20:56:07 +00:00
|
|
|
const VAR_ALIGN_BYTES: usize = 8;
|
2020-06-22 06:29:08 +00:00
|
|
|
|
|
|
|
/// A context object for maintaining all state needed by the coverageinfo module.
|
coverage bug fixes and optimization support
Adjusted LLVM codegen for code compiled with `-Zinstrument-coverage` to
address multiple, somewhat related issues.
Fixed a significant flaw in prior coverage solution: Every counter
generated a new counter variable, but there should have only been one
counter variable per function. This appears to have bloated .profraw
files significantly. (For a small program, it increased the size by
about 40%. I have not tested large programs, but there is anecdotal
evidence that profraw files were way too large. This is a good fix,
regardless, but hopefully it also addresses related issues.
Fixes: #82144
Invalid LLVM coverage data produced when compiled with -C opt-level=1
Existing tests now work up to at least `opt-level=3`. This required a
detailed analysis of the LLVM IR, comparisons with Clang C++ LLVM IR
when compiled with coverage, and a lot of trial and error with codegen
adjustments.
The biggest hurdle was figuring out how to continue to support coverage
results for unused functions and generics. Rust's coverage results have
three advantages over Clang's coverage results:
1. Rust's coverage map does not include any overlapping code regions,
making coverage counting unambiguous.
2. Rust generates coverage results (showing zero counts) for all unused
functions, including generics. (Clang does not generate coverage for
uninstantiated template functions.)
3. Rust's unused functions produce minimal stubbed functions in LLVM IR,
sufficient for including in the coverage results; while Clang must
generate the complete LLVM IR for each unused function, even though
it will never be called.
This PR removes the previous hack of attempting to inject coverage into
some other existing function instance, and generates dedicated instances
for each unused function. This change, and a few other adjustments
(similar to what is required for `-C link-dead-code`, but with lower
impact), makes it possible to support LLVM optimizations.
Fixes: #79651
Coverage report: "Unexecuted instantiation:..." for a generic function
from multiple crates
Fixed by removing the aforementioned hack. Some "Unexecuted
instantiation" notices are unavoidable, as explained in the
`used_crate.rs` test, but `-Zinstrument-coverage` has new options to
back off support for either unused generics, or all unused functions,
which avoids the notice, at the cost of less coverage of unused
functions.
Fixes: #82875
Invalid LLVM coverage data produced with crate brotli_decompressor
Fixed by disabling the LLVM function attribute that forces inlining, if
`-Z instrument-coverage` is enabled. This attribute is applied to
Rust functions with `#[inline(always)], and in some cases, the forced
inlining breaks coverage instrumentation and reports.
2021-03-15 23:32:45 +00:00
|
|
|
pub struct CrateCoverageContext<'ll, 'tcx> {
|
2020-10-05 23:36:10 +00:00
|
|
|
// Coverage data for each instrumented function identified by DefId.
|
|
|
|
pub(crate) function_coverage_map: RefCell<FxHashMap<Instance<'tcx>, FunctionCoverage<'tcx>>>,
|
coverage bug fixes and optimization support
Adjusted LLVM codegen for code compiled with `-Zinstrument-coverage` to
address multiple, somewhat related issues.
Fixed a significant flaw in prior coverage solution: Every counter
generated a new counter variable, but there should have only been one
counter variable per function. This appears to have bloated .profraw
files significantly. (For a small program, it increased the size by
about 40%. I have not tested large programs, but there is anecdotal
evidence that profraw files were way too large. This is a good fix,
regardless, but hopefully it also addresses related issues.
Fixes: #82144
Invalid LLVM coverage data produced when compiled with -C opt-level=1
Existing tests now work up to at least `opt-level=3`. This required a
detailed analysis of the LLVM IR, comparisons with Clang C++ LLVM IR
when compiled with coverage, and a lot of trial and error with codegen
adjustments.
The biggest hurdle was figuring out how to continue to support coverage
results for unused functions and generics. Rust's coverage results have
three advantages over Clang's coverage results:
1. Rust's coverage map does not include any overlapping code regions,
making coverage counting unambiguous.
2. Rust generates coverage results (showing zero counts) for all unused
functions, including generics. (Clang does not generate coverage for
uninstantiated template functions.)
3. Rust's unused functions produce minimal stubbed functions in LLVM IR,
sufficient for including in the coverage results; while Clang must
generate the complete LLVM IR for each unused function, even though
it will never be called.
This PR removes the previous hack of attempting to inject coverage into
some other existing function instance, and generates dedicated instances
for each unused function. This change, and a few other adjustments
(similar to what is required for `-C link-dead-code`, but with lower
impact), makes it possible to support LLVM optimizations.
Fixes: #79651
Coverage report: "Unexecuted instantiation:..." for a generic function
from multiple crates
Fixed by removing the aforementioned hack. Some "Unexecuted
instantiation" notices are unavoidable, as explained in the
`used_crate.rs` test, but `-Zinstrument-coverage` has new options to
back off support for either unused generics, or all unused functions,
which avoids the notice, at the cost of less coverage of unused
functions.
Fixes: #82875
Invalid LLVM coverage data produced with crate brotli_decompressor
Fixed by disabling the LLVM function attribute that forces inlining, if
`-Z instrument-coverage` is enabled. This attribute is applied to
Rust functions with `#[inline(always)], and in some cases, the forced
inlining breaks coverage instrumentation and reports.
2021-03-15 23:32:45 +00:00
|
|
|
pub(crate) pgo_func_name_var_map: RefCell<FxHashMap<Instance<'tcx>, &'ll llvm::Value>>,
|
2020-06-22 06:29:08 +00:00
|
|
|
}
|
|
|
|
|
coverage bug fixes and optimization support
Adjusted LLVM codegen for code compiled with `-Zinstrument-coverage` to
address multiple, somewhat related issues.
Fixed a significant flaw in prior coverage solution: Every counter
generated a new counter variable, but there should have only been one
counter variable per function. This appears to have bloated .profraw
files significantly. (For a small program, it increased the size by
about 40%. I have not tested large programs, but there is anecdotal
evidence that profraw files were way too large. This is a good fix,
regardless, but hopefully it also addresses related issues.
Fixes: #82144
Invalid LLVM coverage data produced when compiled with -C opt-level=1
Existing tests now work up to at least `opt-level=3`. This required a
detailed analysis of the LLVM IR, comparisons with Clang C++ LLVM IR
when compiled with coverage, and a lot of trial and error with codegen
adjustments.
The biggest hurdle was figuring out how to continue to support coverage
results for unused functions and generics. Rust's coverage results have
three advantages over Clang's coverage results:
1. Rust's coverage map does not include any overlapping code regions,
making coverage counting unambiguous.
2. Rust generates coverage results (showing zero counts) for all unused
functions, including generics. (Clang does not generate coverage for
uninstantiated template functions.)
3. Rust's unused functions produce minimal stubbed functions in LLVM IR,
sufficient for including in the coverage results; while Clang must
generate the complete LLVM IR for each unused function, even though
it will never be called.
This PR removes the previous hack of attempting to inject coverage into
some other existing function instance, and generates dedicated instances
for each unused function. This change, and a few other adjustments
(similar to what is required for `-C link-dead-code`, but with lower
impact), makes it possible to support LLVM optimizations.
Fixes: #79651
Coverage report: "Unexecuted instantiation:..." for a generic function
from multiple crates
Fixed by removing the aforementioned hack. Some "Unexecuted
instantiation" notices are unavoidable, as explained in the
`used_crate.rs` test, but `-Zinstrument-coverage` has new options to
back off support for either unused generics, or all unused functions,
which avoids the notice, at the cost of less coverage of unused
functions.
Fixes: #82875
Invalid LLVM coverage data produced with crate brotli_decompressor
Fixed by disabling the LLVM function attribute that forces inlining, if
`-Z instrument-coverage` is enabled. This attribute is applied to
Rust functions with `#[inline(always)], and in some cases, the forced
inlining breaks coverage instrumentation and reports.
2021-03-15 23:32:45 +00:00
|
|
|
impl<'ll, 'tcx> CrateCoverageContext<'ll, 'tcx> {
|
2020-06-22 06:29:08 +00:00
|
|
|
pub fn new() -> Self {
|
coverage bug fixes and optimization support
Adjusted LLVM codegen for code compiled with `-Zinstrument-coverage` to
address multiple, somewhat related issues.
Fixed a significant flaw in prior coverage solution: Every counter
generated a new counter variable, but there should have only been one
counter variable per function. This appears to have bloated .profraw
files significantly. (For a small program, it increased the size by
about 40%. I have not tested large programs, but there is anecdotal
evidence that profraw files were way too large. This is a good fix,
regardless, but hopefully it also addresses related issues.
Fixes: #82144
Invalid LLVM coverage data produced when compiled with -C opt-level=1
Existing tests now work up to at least `opt-level=3`. This required a
detailed analysis of the LLVM IR, comparisons with Clang C++ LLVM IR
when compiled with coverage, and a lot of trial and error with codegen
adjustments.
The biggest hurdle was figuring out how to continue to support coverage
results for unused functions and generics. Rust's coverage results have
three advantages over Clang's coverage results:
1. Rust's coverage map does not include any overlapping code regions,
making coverage counting unambiguous.
2. Rust generates coverage results (showing zero counts) for all unused
functions, including generics. (Clang does not generate coverage for
uninstantiated template functions.)
3. Rust's unused functions produce minimal stubbed functions in LLVM IR,
sufficient for including in the coverage results; while Clang must
generate the complete LLVM IR for each unused function, even though
it will never be called.
This PR removes the previous hack of attempting to inject coverage into
some other existing function instance, and generates dedicated instances
for each unused function. This change, and a few other adjustments
(similar to what is required for `-C link-dead-code`, but with lower
impact), makes it possible to support LLVM optimizations.
Fixes: #79651
Coverage report: "Unexecuted instantiation:..." for a generic function
from multiple crates
Fixed by removing the aforementioned hack. Some "Unexecuted
instantiation" notices are unavoidable, as explained in the
`used_crate.rs` test, but `-Zinstrument-coverage` has new options to
back off support for either unused generics, or all unused functions,
which avoids the notice, at the cost of less coverage of unused
functions.
Fixes: #82875
Invalid LLVM coverage data produced with crate brotli_decompressor
Fixed by disabling the LLVM function attribute that forces inlining, if
`-Z instrument-coverage` is enabled. This attribute is applied to
Rust functions with `#[inline(always)], and in some cases, the forced
inlining breaks coverage instrumentation and reports.
2021-03-15 23:32:45 +00:00
|
|
|
Self {
|
|
|
|
function_coverage_map: Default::default(),
|
|
|
|
pgo_func_name_var_map: Default::default(),
|
|
|
|
}
|
2020-06-22 06:29:08 +00:00
|
|
|
}
|
|
|
|
|
2020-10-05 23:36:10 +00:00
|
|
|
pub fn take_function_coverage_map(&self) -> FxHashMap<Instance<'tcx>, FunctionCoverage<'tcx>> {
|
2020-07-02 18:27:15 +00:00
|
|
|
self.function_coverage_map.replace(FxHashMap::default())
|
2020-06-22 06:29:08 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
coverage bug fixes and optimization support
Adjusted LLVM codegen for code compiled with `-Zinstrument-coverage` to
address multiple, somewhat related issues.
Fixed a significant flaw in prior coverage solution: Every counter
generated a new counter variable, but there should have only been one
counter variable per function. This appears to have bloated .profraw
files significantly. (For a small program, it increased the size by
about 40%. I have not tested large programs, but there is anecdotal
evidence that profraw files were way too large. This is a good fix,
regardless, but hopefully it also addresses related issues.
Fixes: #82144
Invalid LLVM coverage data produced when compiled with -C opt-level=1
Existing tests now work up to at least `opt-level=3`. This required a
detailed analysis of the LLVM IR, comparisons with Clang C++ LLVM IR
when compiled with coverage, and a lot of trial and error with codegen
adjustments.
The biggest hurdle was figuring out how to continue to support coverage
results for unused functions and generics. Rust's coverage results have
three advantages over Clang's coverage results:
1. Rust's coverage map does not include any overlapping code regions,
making coverage counting unambiguous.
2. Rust generates coverage results (showing zero counts) for all unused
functions, including generics. (Clang does not generate coverage for
uninstantiated template functions.)
3. Rust's unused functions produce minimal stubbed functions in LLVM IR,
sufficient for including in the coverage results; while Clang must
generate the complete LLVM IR for each unused function, even though
it will never be called.
This PR removes the previous hack of attempting to inject coverage into
some other existing function instance, and generates dedicated instances
for each unused function. This change, and a few other adjustments
(similar to what is required for `-C link-dead-code`, but with lower
impact), makes it possible to support LLVM optimizations.
Fixes: #79651
Coverage report: "Unexecuted instantiation:..." for a generic function
from multiple crates
Fixed by removing the aforementioned hack. Some "Unexecuted
instantiation" notices are unavoidable, as explained in the
`used_crate.rs` test, but `-Zinstrument-coverage` has new options to
back off support for either unused generics, or all unused functions,
which avoids the notice, at the cost of less coverage of unused
functions.
Fixes: #82875
Invalid LLVM coverage data produced with crate brotli_decompressor
Fixed by disabling the LLVM function attribute that forces inlining, if
`-Z instrument-coverage` is enabled. This attribute is applied to
Rust functions with `#[inline(always)], and in some cases, the forced
inlining breaks coverage instrumentation and reports.
2021-03-15 23:32:45 +00:00
|
|
|
impl CoverageInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> {
|
2020-06-22 06:29:08 +00:00
|
|
|
fn coverageinfo_finalize(&self) {
|
2020-07-02 18:27:15 +00:00
|
|
|
mapgen::finalize(self)
|
2020-06-22 06:29:08 +00:00
|
|
|
}
|
|
|
|
|
coverage bug fixes and optimization support
Adjusted LLVM codegen for code compiled with `-Zinstrument-coverage` to
address multiple, somewhat related issues.
Fixed a significant flaw in prior coverage solution: Every counter
generated a new counter variable, but there should have only been one
counter variable per function. This appears to have bloated .profraw
files significantly. (For a small program, it increased the size by
about 40%. I have not tested large programs, but there is anecdotal
evidence that profraw files were way too large. This is a good fix,
regardless, but hopefully it also addresses related issues.
Fixes: #82144
Invalid LLVM coverage data produced when compiled with -C opt-level=1
Existing tests now work up to at least `opt-level=3`. This required a
detailed analysis of the LLVM IR, comparisons with Clang C++ LLVM IR
when compiled with coverage, and a lot of trial and error with codegen
adjustments.
The biggest hurdle was figuring out how to continue to support coverage
results for unused functions and generics. Rust's coverage results have
three advantages over Clang's coverage results:
1. Rust's coverage map does not include any overlapping code regions,
making coverage counting unambiguous.
2. Rust generates coverage results (showing zero counts) for all unused
functions, including generics. (Clang does not generate coverage for
uninstantiated template functions.)
3. Rust's unused functions produce minimal stubbed functions in LLVM IR,
sufficient for including in the coverage results; while Clang must
generate the complete LLVM IR for each unused function, even though
it will never be called.
This PR removes the previous hack of attempting to inject coverage into
some other existing function instance, and generates dedicated instances
for each unused function. This change, and a few other adjustments
(similar to what is required for `-C link-dead-code`, but with lower
impact), makes it possible to support LLVM optimizations.
Fixes: #79651
Coverage report: "Unexecuted instantiation:..." for a generic function
from multiple crates
Fixed by removing the aforementioned hack. Some "Unexecuted
instantiation" notices are unavoidable, as explained in the
`used_crate.rs` test, but `-Zinstrument-coverage` has new options to
back off support for either unused generics, or all unused functions,
which avoids the notice, at the cost of less coverage of unused
functions.
Fixes: #82875
Invalid LLVM coverage data produced with crate brotli_decompressor
Fixed by disabling the LLVM function attribute that forces inlining, if
`-Z instrument-coverage` is enabled. This attribute is applied to
Rust functions with `#[inline(always)], and in some cases, the forced
inlining breaks coverage instrumentation and reports.
2021-03-15 23:32:45 +00:00
|
|
|
fn get_pgo_func_name_var(&self, instance: Instance<'tcx>) -> &'ll llvm::Value {
|
|
|
|
if let Some(coverage_context) = self.coverage_context() {
|
|
|
|
debug!("getting pgo_func_name_var for instance={:?}", instance);
|
|
|
|
let mut pgo_func_name_var_map = coverage_context.pgo_func_name_var_map.borrow_mut();
|
|
|
|
pgo_func_name_var_map
|
|
|
|
.entry(instance)
|
|
|
|
.or_insert_with(|| self.create_pgo_func_name_var(instance))
|
|
|
|
} else {
|
|
|
|
bug!("Could not get the `coverage_context`");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// 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. Use `bx.get_pgo_func_name_var()` to ensure the variable
|
|
|
|
/// is only created once per `Instance`.
|
|
|
|
fn create_pgo_func_name_var(&self, instance: Instance<'tcx>) -> &'ll llvm::Value {
|
LLVM IR coverage encoding aligns closer to Clang's
I found some areas for improvement while attempting to debug the
SegFault issue when running rust programs compiled using MSVC, with
coverage instrumentation.
I discovered that LLVM's coverage writer was generating incomplete
function name variable names (that's not a typo: the name of the
variable that holds a function name).
The existing implementation used one-up numbers to distinguish
variables, and correcting the names did not fix the MSVC coverage bug,
but the fix in this PR makes the names and resulting LLVM IR easier to
follow and more consistent with Clang's implementation.
I also changed the way the `-Zinstrument-coverage` option is supported
in symbol_export.rs. The original implementation was incorrect, and the
corrected version matches the handling for `-Zprofile-generate`, as it
turns out.
(An argument could be made that maybe `-Zinstrument-coverage` should
automatically enable `-Cprofile-generate`. In fact, if
`-Cprofile-generate` is analagous to Clang's `-fprofile-generate`, as
some documentation implies, Clang always requires this flag for its
implementation of source-based code coverage. This would require a
little more validation, and if implemented, would probably require
updating some of the user-facing messages related to
`-Cprofile-generate` to not be so specific to the PGO use case.)
None of these changes fixed the MSVC coverage problems, but they should
still be welcome improvements.
Lastly, I added some additional FIXME comments in instrument_coverage.rs
describing issues I found with the generated LLVM IR that would be
resolved if the coverage instrumentation is injected with a `Statement`
instead of as a new `BasicBlock`. I describe seven advantages of this
change, but it requires some discussion before making a change like
this.
2020-08-06 05:53:11 +00:00
|
|
|
let mangled_fn_name = CString::new(self.tcx.symbol_name(instance).name)
|
|
|
|
.expect("error converting function name to C string");
|
coverage bug fixes and optimization support
Adjusted LLVM codegen for code compiled with `-Zinstrument-coverage` to
address multiple, somewhat related issues.
Fixed a significant flaw in prior coverage solution: Every counter
generated a new counter variable, but there should have only been one
counter variable per function. This appears to have bloated .profraw
files significantly. (For a small program, it increased the size by
about 40%. I have not tested large programs, but there is anecdotal
evidence that profraw files were way too large. This is a good fix,
regardless, but hopefully it also addresses related issues.
Fixes: #82144
Invalid LLVM coverage data produced when compiled with -C opt-level=1
Existing tests now work up to at least `opt-level=3`. This required a
detailed analysis of the LLVM IR, comparisons with Clang C++ LLVM IR
when compiled with coverage, and a lot of trial and error with codegen
adjustments.
The biggest hurdle was figuring out how to continue to support coverage
results for unused functions and generics. Rust's coverage results have
three advantages over Clang's coverage results:
1. Rust's coverage map does not include any overlapping code regions,
making coverage counting unambiguous.
2. Rust generates coverage results (showing zero counts) for all unused
functions, including generics. (Clang does not generate coverage for
uninstantiated template functions.)
3. Rust's unused functions produce minimal stubbed functions in LLVM IR,
sufficient for including in the coverage results; while Clang must
generate the complete LLVM IR for each unused function, even though
it will never be called.
This PR removes the previous hack of attempting to inject coverage into
some other existing function instance, and generates dedicated instances
for each unused function. This change, and a few other adjustments
(similar to what is required for `-C link-dead-code`, but with lower
impact), makes it possible to support LLVM optimizations.
Fixes: #79651
Coverage report: "Unexecuted instantiation:..." for a generic function
from multiple crates
Fixed by removing the aforementioned hack. Some "Unexecuted
instantiation" notices are unavoidable, as explained in the
`used_crate.rs` test, but `-Zinstrument-coverage` has new options to
back off support for either unused generics, or all unused functions,
which avoids the notice, at the cost of less coverage of unused
functions.
Fixes: #82875
Invalid LLVM coverage data produced with crate brotli_decompressor
Fixed by disabling the LLVM function attribute that forces inlining, if
`-Z instrument-coverage` is enabled. This attribute is applied to
Rust functions with `#[inline(always)], and in some cases, the forced
inlining breaks coverage instrumentation and reports.
2021-03-15 23:32:45 +00:00
|
|
|
let llfn = self.get_fn(instance);
|
LLVM IR coverage encoding aligns closer to Clang's
I found some areas for improvement while attempting to debug the
SegFault issue when running rust programs compiled using MSVC, with
coverage instrumentation.
I discovered that LLVM's coverage writer was generating incomplete
function name variable names (that's not a typo: the name of the
variable that holds a function name).
The existing implementation used one-up numbers to distinguish
variables, and correcting the names did not fix the MSVC coverage bug,
but the fix in this PR makes the names and resulting LLVM IR easier to
follow and more consistent with Clang's implementation.
I also changed the way the `-Zinstrument-coverage` option is supported
in symbol_export.rs. The original implementation was incorrect, and the
corrected version matches the handling for `-Zprofile-generate`, as it
turns out.
(An argument could be made that maybe `-Zinstrument-coverage` should
automatically enable `-Cprofile-generate`. In fact, if
`-Cprofile-generate` is analagous to Clang's `-fprofile-generate`, as
some documentation implies, Clang always requires this flag for its
implementation of source-based code coverage. This would require a
little more validation, and if implemented, would probably require
updating some of the user-facing messages related to
`-Cprofile-generate` to not be so specific to the PGO use case.)
None of these changes fixed the MSVC coverage problems, but they should
still be welcome improvements.
Lastly, I added some additional FIXME comments in instrument_coverage.rs
describing issues I found with the generated LLVM IR that would be
resolved if the coverage instrumentation is injected with a `Statement`
instead of as a new `BasicBlock`. I describe seven advantages of this
change, but it requires some discussion before making a change like
this.
2020-08-06 05:53:11 +00:00
|
|
|
unsafe { llvm::LLVMRustCoverageCreatePGOFuncNameVar(llfn, mangled_fn_name.as_ptr()) }
|
|
|
|
}
|
|
|
|
|
coverage bug fixes and optimization support
Adjusted LLVM codegen for code compiled with `-Zinstrument-coverage` to
address multiple, somewhat related issues.
Fixed a significant flaw in prior coverage solution: Every counter
generated a new counter variable, but there should have only been one
counter variable per function. This appears to have bloated .profraw
files significantly. (For a small program, it increased the size by
about 40%. I have not tested large programs, but there is anecdotal
evidence that profraw files were way too large. This is a good fix,
regardless, but hopefully it also addresses related issues.
Fixes: #82144
Invalid LLVM coverage data produced when compiled with -C opt-level=1
Existing tests now work up to at least `opt-level=3`. This required a
detailed analysis of the LLVM IR, comparisons with Clang C++ LLVM IR
when compiled with coverage, and a lot of trial and error with codegen
adjustments.
The biggest hurdle was figuring out how to continue to support coverage
results for unused functions and generics. Rust's coverage results have
three advantages over Clang's coverage results:
1. Rust's coverage map does not include any overlapping code regions,
making coverage counting unambiguous.
2. Rust generates coverage results (showing zero counts) for all unused
functions, including generics. (Clang does not generate coverage for
uninstantiated template functions.)
3. Rust's unused functions produce minimal stubbed functions in LLVM IR,
sufficient for including in the coverage results; while Clang must
generate the complete LLVM IR for each unused function, even though
it will never be called.
This PR removes the previous hack of attempting to inject coverage into
some other existing function instance, and generates dedicated instances
for each unused function. This change, and a few other adjustments
(similar to what is required for `-C link-dead-code`, but with lower
impact), makes it possible to support LLVM optimizations.
Fixes: #79651
Coverage report: "Unexecuted instantiation:..." for a generic function
from multiple crates
Fixed by removing the aforementioned hack. Some "Unexecuted
instantiation" notices are unavoidable, as explained in the
`used_crate.rs` test, but `-Zinstrument-coverage` has new options to
back off support for either unused generics, or all unused functions,
which avoids the notice, at the cost of less coverage of unused
functions.
Fixes: #82875
Invalid LLVM coverage data produced with crate brotli_decompressor
Fixed by disabling the LLVM function attribute that forces inlining, if
`-Z instrument-coverage` is enabled. This attribute is applied to
Rust functions with `#[inline(always)], and in some cases, the forced
inlining breaks coverage instrumentation and reports.
2021-03-15 23:32:45 +00:00
|
|
|
fn define_unused_fn(&self, def_id: DefId) {
|
|
|
|
let instance = declare_unused_fn(self, &def_id);
|
|
|
|
codegen_unused_fn_and_counter(self, instance);
|
|
|
|
add_function_coverage(self, instance, def_id);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl CoverageInfoBuilderMethods<'tcx> for Builder<'a, 'll, 'tcx> {
|
2020-10-22 21:30:03 +00:00
|
|
|
fn set_function_source_hash(
|
|
|
|
&mut self,
|
|
|
|
instance: Instance<'tcx>,
|
|
|
|
function_source_hash: u64,
|
|
|
|
) -> bool {
|
2020-10-05 23:36:10 +00:00
|
|
|
if let Some(coverage_context) = self.coverage_context() {
|
|
|
|
debug!(
|
|
|
|
"ensuring function source hash is set for instance={:?}; function_source_hash={}",
|
|
|
|
instance, function_source_hash,
|
|
|
|
);
|
|
|
|
let mut coverage_map = coverage_context.function_coverage_map.borrow_mut();
|
|
|
|
coverage_map
|
|
|
|
.entry(instance)
|
|
|
|
.or_insert_with(|| FunctionCoverage::new(self.tcx, instance))
|
|
|
|
.set_function_source_hash(function_source_hash);
|
2020-10-22 21:30:03 +00:00
|
|
|
true
|
2020-10-05 23:36:10 +00:00
|
|
|
} else {
|
|
|
|
false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn add_coverage_counter(
|
2020-06-22 06:29:08 +00:00
|
|
|
&mut self,
|
|
|
|
instance: Instance<'tcx>,
|
2020-08-15 11:42:13 +00:00
|
|
|
id: CounterValueReference,
|
|
|
|
region: CodeRegion,
|
2020-10-23 18:41:56 +00:00
|
|
|
) -> bool {
|
|
|
|
if let Some(coverage_context) = self.coverage_context() {
|
|
|
|
debug!(
|
2020-10-25 18:13:16 +00:00
|
|
|
"adding counter to coverage_map: instance={:?}, id={:?}, region={:?}",
|
|
|
|
instance, id, region,
|
2020-10-23 18:41:56 +00:00
|
|
|
);
|
2020-10-05 23:36:10 +00:00
|
|
|
let mut coverage_map = coverage_context.function_coverage_map.borrow_mut();
|
|
|
|
coverage_map
|
2020-10-23 18:41:56 +00:00
|
|
|
.entry(instance)
|
|
|
|
.or_insert_with(|| FunctionCoverage::new(self.tcx, instance))
|
2020-10-25 18:13:16 +00:00
|
|
|
.add_counter(id, region);
|
2020-10-22 21:30:03 +00:00
|
|
|
true
|
2020-10-23 18:41:56 +00:00
|
|
|
} else {
|
|
|
|
false
|
|
|
|
}
|
2020-06-22 06:29:08 +00:00
|
|
|
}
|
|
|
|
|
2020-10-05 23:36:10 +00:00
|
|
|
fn add_coverage_counter_expression(
|
2020-06-22 06:29:08 +00:00
|
|
|
&mut self,
|
|
|
|
instance: Instance<'tcx>,
|
2020-10-05 23:36:10 +00:00
|
|
|
id: InjectedExpressionId,
|
2020-08-15 11:42:13 +00:00
|
|
|
lhs: ExpressionOperandId,
|
|
|
|
op: Op,
|
|
|
|
rhs: ExpressionOperandId,
|
2020-10-05 23:36:10 +00:00
|
|
|
region: Option<CodeRegion>,
|
2020-10-22 21:30:03 +00:00
|
|
|
) -> bool {
|
|
|
|
if let Some(coverage_context) = self.coverage_context() {
|
2020-10-23 18:41:56 +00:00
|
|
|
debug!(
|
2020-10-05 23:36:10 +00:00
|
|
|
"adding counter expression to coverage_map: instance={:?}, id={:?}, {:?} {:?} {:?}; \
|
|
|
|
region: {:?}",
|
2020-10-23 18:41:56 +00:00
|
|
|
instance, id, lhs, op, rhs, region,
|
|
|
|
);
|
2020-10-05 23:36:10 +00:00
|
|
|
let mut coverage_map = coverage_context.function_coverage_map.borrow_mut();
|
|
|
|
coverage_map
|
2020-10-23 18:41:56 +00:00
|
|
|
.entry(instance)
|
|
|
|
.or_insert_with(|| FunctionCoverage::new(self.tcx, instance))
|
|
|
|
.add_counter_expression(id, lhs, op, rhs, region);
|
2020-10-22 21:30:03 +00:00
|
|
|
true
|
2020-10-23 18:41:56 +00:00
|
|
|
} else {
|
|
|
|
false
|
|
|
|
}
|
2020-06-22 06:29:08 +00:00
|
|
|
}
|
|
|
|
|
2020-10-05 23:36:10 +00:00
|
|
|
fn add_coverage_unreachable(&mut self, instance: Instance<'tcx>, region: CodeRegion) -> bool {
|
2020-10-23 18:41:56 +00:00
|
|
|
if let Some(coverage_context) = self.coverage_context() {
|
2020-10-22 21:30:03 +00:00
|
|
|
debug!(
|
|
|
|
"adding unreachable code to coverage_map: instance={:?}, at {:?}",
|
|
|
|
instance, region,
|
|
|
|
);
|
2020-10-05 23:36:10 +00:00
|
|
|
let mut coverage_map = coverage_context.function_coverage_map.borrow_mut();
|
|
|
|
coverage_map
|
2020-10-23 18:41:56 +00:00
|
|
|
.entry(instance)
|
|
|
|
.or_insert_with(|| FunctionCoverage::new(self.tcx, instance))
|
|
|
|
.add_unreachable_region(region);
|
2020-10-22 21:30:03 +00:00
|
|
|
true
|
2020-10-23 18:41:56 +00:00
|
|
|
} else {
|
|
|
|
false
|
|
|
|
}
|
2020-07-02 18:27:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
coverage bug fixes and optimization support
Adjusted LLVM codegen for code compiled with `-Zinstrument-coverage` to
address multiple, somewhat related issues.
Fixed a significant flaw in prior coverage solution: Every counter
generated a new counter variable, but there should have only been one
counter variable per function. This appears to have bloated .profraw
files significantly. (For a small program, it increased the size by
about 40%. I have not tested large programs, but there is anecdotal
evidence that profraw files were way too large. This is a good fix,
regardless, but hopefully it also addresses related issues.
Fixes: #82144
Invalid LLVM coverage data produced when compiled with -C opt-level=1
Existing tests now work up to at least `opt-level=3`. This required a
detailed analysis of the LLVM IR, comparisons with Clang C++ LLVM IR
when compiled with coverage, and a lot of trial and error with codegen
adjustments.
The biggest hurdle was figuring out how to continue to support coverage
results for unused functions and generics. Rust's coverage results have
three advantages over Clang's coverage results:
1. Rust's coverage map does not include any overlapping code regions,
making coverage counting unambiguous.
2. Rust generates coverage results (showing zero counts) for all unused
functions, including generics. (Clang does not generate coverage for
uninstantiated template functions.)
3. Rust's unused functions produce minimal stubbed functions in LLVM IR,
sufficient for including in the coverage results; while Clang must
generate the complete LLVM IR for each unused function, even though
it will never be called.
This PR removes the previous hack of attempting to inject coverage into
some other existing function instance, and generates dedicated instances
for each unused function. This change, and a few other adjustments
(similar to what is required for `-C link-dead-code`, but with lower
impact), makes it possible to support LLVM optimizations.
Fixes: #79651
Coverage report: "Unexecuted instantiation:..." for a generic function
from multiple crates
Fixed by removing the aforementioned hack. Some "Unexecuted
instantiation" notices are unavoidable, as explained in the
`used_crate.rs` test, but `-Zinstrument-coverage` has new options to
back off support for either unused generics, or all unused functions,
which avoids the notice, at the cost of less coverage of unused
functions.
Fixes: #82875
Invalid LLVM coverage data produced with crate brotli_decompressor
Fixed by disabling the LLVM function attribute that forces inlining, if
`-Z instrument-coverage` is enabled. This attribute is applied to
Rust functions with `#[inline(always)], and in some cases, the forced
inlining breaks coverage instrumentation and reports.
2021-03-15 23:32:45 +00:00
|
|
|
fn declare_unused_fn(cx: &CodegenCx<'ll, 'tcx>, def_id: &DefId) -> Instance<'tcx> {
|
|
|
|
let tcx = cx.tcx;
|
|
|
|
|
|
|
|
let instance = Instance::new(
|
|
|
|
*def_id,
|
|
|
|
InternalSubsts::for_item(tcx, *def_id, |param, _| {
|
|
|
|
if let ty::GenericParamDefKind::Lifetime = param.kind {
|
|
|
|
tcx.lifetimes.re_erased.into()
|
|
|
|
} else {
|
|
|
|
tcx.mk_param_from_def(param)
|
|
|
|
}
|
|
|
|
}),
|
|
|
|
);
|
|
|
|
|
|
|
|
let llfn = cx.declare_fn(
|
|
|
|
&tcx.symbol_name(instance).name,
|
|
|
|
&FnAbi::of_fn_ptr(
|
|
|
|
cx,
|
|
|
|
ty::Binder::dummy(tcx.mk_fn_sig(
|
|
|
|
iter::once(tcx.mk_unit()),
|
|
|
|
tcx.mk_unit(),
|
|
|
|
false,
|
|
|
|
hir::Unsafety::Unsafe,
|
|
|
|
Abi::Rust,
|
|
|
|
)),
|
|
|
|
&[],
|
|
|
|
),
|
|
|
|
);
|
|
|
|
|
|
|
|
unsafe {
|
|
|
|
llvm::LLVMRustSetLinkage(llfn, llvm::Linkage::ExternalLinkage);
|
|
|
|
llvm::LLVMRustSetVisibility(llfn, llvm::Visibility::Hidden);
|
|
|
|
}
|
|
|
|
|
|
|
|
cx.instances.borrow_mut().insert(instance, llfn);
|
|
|
|
|
|
|
|
instance
|
|
|
|
}
|
|
|
|
|
|
|
|
fn codegen_unused_fn_and_counter(cx: &CodegenCx<'ll, 'tcx>, instance: Instance<'tcx>) {
|
|
|
|
let llfn = cx.get_fn(instance);
|
|
|
|
let mut bx = Builder::new_block(cx, llfn, "unused_function");
|
|
|
|
let fn_name = bx.get_pgo_func_name_var(instance);
|
|
|
|
let hash = bx.const_u64(0);
|
|
|
|
let num_counters = bx.const_u32(1);
|
|
|
|
let index = bx.const_u32(u32::from(UNUSED_FUNCTION_COUNTER_ID));
|
|
|
|
debug!(
|
|
|
|
"codegen intrinsic instrprof.increment(fn_name={:?}, hash={:?}, num_counters={:?},
|
|
|
|
index={:?}) for unused function: {:?}",
|
|
|
|
fn_name, hash, num_counters, index, instance
|
|
|
|
);
|
|
|
|
bx.instrprof_increment(fn_name, hash, num_counters, index);
|
|
|
|
bx.ret_void();
|
|
|
|
}
|
|
|
|
|
|
|
|
fn add_function_coverage(cx: &CodegenCx<'ll, 'tcx>, instance: Instance<'tcx>, def_id: DefId) {
|
|
|
|
let tcx = cx.tcx;
|
|
|
|
|
|
|
|
let mut function_coverage = FunctionCoverage::unused(tcx, instance);
|
|
|
|
for (index, &code_region) in tcx.covered_code_regions(def_id).iter().enumerate() {
|
|
|
|
if index == 0 {
|
|
|
|
// Insert at least one real counter so the LLVM CoverageMappingReader will find expected
|
|
|
|
// definitions.
|
|
|
|
function_coverage.add_counter(UNUSED_FUNCTION_COUNTER_ID, code_region.clone());
|
|
|
|
}
|
|
|
|
// Add a Zero Counter for every code region.
|
|
|
|
//
|
|
|
|
// Even though the first coverage region already has an actual Counter, `llvm-cov` will not
|
|
|
|
// always report it. Re-adding an unreachable region (zero counter) for the same region
|
|
|
|
// seems to help produce the expected coverage.
|
|
|
|
function_coverage.add_unreachable_region(code_region.clone());
|
|
|
|
}
|
|
|
|
|
|
|
|
if let Some(coverage_context) = cx.coverage_context() {
|
|
|
|
coverage_context.function_coverage_map.borrow_mut().insert(instance, function_coverage);
|
|
|
|
} else {
|
|
|
|
bug!("Could not get the `coverage_context`");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-08 03:42:29 +00:00
|
|
|
pub(crate) fn write_filenames_section_to_buffer<'a>(
|
|
|
|
filenames: impl IntoIterator<Item = &'a CString>,
|
|
|
|
buffer: &RustString,
|
|
|
|
) {
|
|
|
|
let c_str_vec = filenames.into_iter().map(|cstring| cstring.as_ptr()).collect::<Vec<_>>();
|
2020-07-02 18:27:15 +00:00
|
|
|
unsafe {
|
|
|
|
llvm::LLVMRustCoverageWriteFilenamesSectionToBuffer(
|
|
|
|
c_str_vec.as_ptr(),
|
|
|
|
c_str_vec.len(),
|
|
|
|
buffer,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) fn write_mapping_to_buffer(
|
|
|
|
virtual_file_mapping: Vec<u32>,
|
2020-07-25 04:14:28 +00:00
|
|
|
expressions: Vec<CounterExpression>,
|
2021-02-20 15:43:05 +00:00
|
|
|
mapping_regions: Vec<CounterMappingRegion>,
|
2020-07-02 18:27:15 +00:00
|
|
|
buffer: &RustString,
|
|
|
|
) {
|
|
|
|
unsafe {
|
|
|
|
llvm::LLVMRustCoverageWriteMappingToBuffer(
|
|
|
|
virtual_file_mapping.as_ptr(),
|
|
|
|
virtual_file_mapping.len() as c_uint,
|
|
|
|
expressions.as_ptr(),
|
2020-07-25 04:14:28 +00:00
|
|
|
expressions.len() as c_uint,
|
2021-02-20 15:43:05 +00:00
|
|
|
mapping_regions.as_ptr(),
|
2020-07-25 04:14:28 +00:00
|
|
|
mapping_regions.len() as c_uint,
|
2020-07-02 18:27:15 +00:00
|
|
|
buffer,
|
|
|
|
);
|
2020-06-22 06:29:08 +00:00
|
|
|
}
|
|
|
|
}
|
coverage bug fixes and optimization support
Adjusted LLVM codegen for code compiled with `-Zinstrument-coverage` to
address multiple, somewhat related issues.
Fixed a significant flaw in prior coverage solution: Every counter
generated a new counter variable, but there should have only been one
counter variable per function. This appears to have bloated .profraw
files significantly. (For a small program, it increased the size by
about 40%. I have not tested large programs, but there is anecdotal
evidence that profraw files were way too large. This is a good fix,
regardless, but hopefully it also addresses related issues.
Fixes: #82144
Invalid LLVM coverage data produced when compiled with -C opt-level=1
Existing tests now work up to at least `opt-level=3`. This required a
detailed analysis of the LLVM IR, comparisons with Clang C++ LLVM IR
when compiled with coverage, and a lot of trial and error with codegen
adjustments.
The biggest hurdle was figuring out how to continue to support coverage
results for unused functions and generics. Rust's coverage results have
three advantages over Clang's coverage results:
1. Rust's coverage map does not include any overlapping code regions,
making coverage counting unambiguous.
2. Rust generates coverage results (showing zero counts) for all unused
functions, including generics. (Clang does not generate coverage for
uninstantiated template functions.)
3. Rust's unused functions produce minimal stubbed functions in LLVM IR,
sufficient for including in the coverage results; while Clang must
generate the complete LLVM IR for each unused function, even though
it will never be called.
This PR removes the previous hack of attempting to inject coverage into
some other existing function instance, and generates dedicated instances
for each unused function. This change, and a few other adjustments
(similar to what is required for `-C link-dead-code`, but with lower
impact), makes it possible to support LLVM optimizations.
Fixes: #79651
Coverage report: "Unexecuted instantiation:..." for a generic function
from multiple crates
Fixed by removing the aforementioned hack. Some "Unexecuted
instantiation" notices are unavoidable, as explained in the
`used_crate.rs` test, but `-Zinstrument-coverage` has new options to
back off support for either unused generics, or all unused functions,
which avoids the notice, at the cost of less coverage of unused
functions.
Fixes: #82875
Invalid LLVM coverage data produced with crate brotli_decompressor
Fixed by disabling the LLVM function attribute that forces inlining, if
`-Z instrument-coverage` is enabled. This attribute is applied to
Rust functions with `#[inline(always)], and in some cases, the forced
inlining breaks coverage instrumentation and reports.
2021-03-15 23:32:45 +00:00
|
|
|
|
2020-11-23 20:56:07 +00:00
|
|
|
pub(crate) fn hash_str(strval: &str) -> u64 {
|
|
|
|
let strval = CString::new(strval).expect("null error converting hashable str to C string");
|
|
|
|
unsafe { llvm::LLVMRustCoverageHashCString(strval.as_ptr()) }
|
|
|
|
}
|
2020-07-02 18:27:15 +00:00
|
|
|
|
2020-11-23 20:56:07 +00:00
|
|
|
pub(crate) fn hash_bytes(bytes: Vec<u8>) -> u64 {
|
|
|
|
unsafe { llvm::LLVMRustCoverageHashByteArray(bytes.as_ptr().cast(), bytes.len()) }
|
2020-07-02 18:27:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) fn mapping_version() -> u32 {
|
|
|
|
unsafe { llvm::LLVMRustCoverageMappingVersion() }
|
|
|
|
}
|
|
|
|
|
2020-11-23 20:56:07 +00:00
|
|
|
pub(crate) fn save_cov_data_to_mod<'ll, 'tcx>(
|
2020-07-02 18:27:15 +00:00
|
|
|
cx: &CodegenCx<'ll, 'tcx>,
|
|
|
|
cov_data_val: &'ll llvm::Value,
|
|
|
|
) {
|
|
|
|
let covmap_var_name = llvm::build_string(|s| unsafe {
|
|
|
|
llvm::LLVMRustCoverageWriteMappingVarNameToString(s);
|
|
|
|
})
|
|
|
|
.expect("Rust Coverage Mapping var name failed UTF-8 conversion");
|
|
|
|
debug!("covmap var name: {:?}", covmap_var_name);
|
|
|
|
|
|
|
|
let covmap_section_name = llvm::build_string(|s| unsafe {
|
2020-11-23 20:56:07 +00:00
|
|
|
llvm::LLVMRustCoverageWriteMapSectionNameToString(cx.llmod, s);
|
2020-07-02 18:27:15 +00:00
|
|
|
})
|
|
|
|
.expect("Rust Coverage section name failed UTF-8 conversion");
|
|
|
|
debug!("covmap section name: {:?}", covmap_section_name);
|
|
|
|
|
|
|
|
let llglobal = llvm::add_global(cx.llmod, cx.val_ty(cov_data_val), &covmap_var_name);
|
|
|
|
llvm::set_initializer(llglobal, cov_data_val);
|
|
|
|
llvm::set_global_constant(llglobal, true);
|
2020-11-23 20:56:07 +00:00
|
|
|
llvm::set_linkage(llglobal, llvm::Linkage::PrivateLinkage);
|
2020-07-02 18:27:15 +00:00
|
|
|
llvm::set_section(llglobal, &covmap_section_name);
|
2020-11-23 20:56:07 +00:00
|
|
|
llvm::set_alignment(llglobal, VAR_ALIGN_BYTES);
|
|
|
|
cx.add_used_global(llglobal);
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) fn save_func_record_to_mod<'ll, 'tcx>(
|
|
|
|
cx: &CodegenCx<'ll, 'tcx>,
|
|
|
|
func_name_hash: u64,
|
|
|
|
func_record_val: &'ll llvm::Value,
|
|
|
|
is_used: bool,
|
|
|
|
) {
|
|
|
|
// Assign a name to the function record. This is used to merge duplicates.
|
|
|
|
//
|
|
|
|
// In LLVM, a "translation unit" (effectively, a `Crate` in Rust) can describe functions that
|
|
|
|
// are included-but-not-used. If (or when) Rust generates functions that are
|
|
|
|
// included-but-not-used, note that a dummy description for a function included-but-not-used
|
|
|
|
// in a Crate can be replaced by full description provided by a different Crate. The two kinds
|
|
|
|
// of descriptions play distinct roles in LLVM IR; therefore, assign them different names (by
|
|
|
|
// appending "u" to the end of the function record var name, to prevent `linkonce_odr` merging.
|
|
|
|
let func_record_var_name =
|
|
|
|
format!("__covrec_{:X}{}", func_name_hash, if is_used { "u" } else { "" });
|
|
|
|
debug!("function record var name: {:?}", func_record_var_name);
|
|
|
|
|
|
|
|
let func_record_section_name = llvm::build_string(|s| unsafe {
|
|
|
|
llvm::LLVMRustCoverageWriteFuncSectionNameToString(cx.llmod, s);
|
|
|
|
})
|
|
|
|
.expect("Rust Coverage function record section name failed UTF-8 conversion");
|
|
|
|
debug!("function record section name: {:?}", func_record_section_name);
|
|
|
|
|
|
|
|
let llglobal = llvm::add_global(cx.llmod, cx.val_ty(func_record_val), &func_record_var_name);
|
|
|
|
llvm::set_initializer(llglobal, func_record_val);
|
|
|
|
llvm::set_global_constant(llglobal, true);
|
|
|
|
llvm::set_linkage(llglobal, llvm::Linkage::LinkOnceODRLinkage);
|
|
|
|
llvm::set_visibility(llglobal, llvm::Visibility::Hidden);
|
|
|
|
llvm::set_section(llglobal, &func_record_section_name);
|
|
|
|
llvm::set_alignment(llglobal, VAR_ALIGN_BYTES);
|
|
|
|
llvm::set_comdat(cx.llmod, llglobal, &func_record_var_name);
|
2020-07-02 18:27:15 +00:00
|
|
|
cx.add_used_global(llglobal);
|
|
|
|
}
|