2020-07-02 18:27:15 +00:00
use crate ::llvm ;
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-06-22 06:29:08 +00:00
use log ::debug ;
use rustc_codegen_ssa ::coverageinfo ::map ::* ;
2020-07-02 18:27:15 +00:00
use rustc_codegen_ssa ::traits ::{
BaseTypeMethods , CoverageInfoBuilderMethods , CoverageInfoMethods , StaticMethods ,
} ;
2020-06-22 06:29:08 +00:00
use rustc_data_structures ::fx ::FxHashMap ;
2020-07-02 18:27:15 +00:00
use rustc_llvm ::RustString ;
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 ;
pub mod mapgen ;
const COVMAP_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.
pub struct CrateCoverageContext < ' tcx > {
// Coverage region data for each instrumented function identified by DefId.
2020-07-25 04:14:28 +00:00
pub ( crate ) function_coverage_map : RefCell < FxHashMap < Instance < ' tcx > , FunctionCoverage < ' tcx > > > ,
2020-06-22 06:29:08 +00:00
}
impl < ' tcx > CrateCoverageContext < ' tcx > {
pub fn new ( ) -> Self {
2020-07-02 18:27:15 +00:00
Self { function_coverage_map : Default ::default ( ) }
2020-06-22 06:29:08 +00:00
}
2020-07-25 04:14:28 +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
}
}
impl CoverageInfoMethods for CodegenCx < ' ll , ' tcx > {
fn coverageinfo_finalize ( & self ) {
2020-07-02 18:27:15 +00:00
mapgen ::finalize ( self )
2020-06-22 06:29:08 +00:00
}
}
impl CoverageInfoBuilderMethods < ' tcx > for Builder < ' a , ' ll , ' tcx > {
fn add_counter_region (
& mut self ,
instance : Instance < ' tcx > ,
2020-07-02 18:27:15 +00:00
function_source_hash : u64 ,
2020-07-25 04:14:28 +00:00
id : u32 ,
2020-06-22 06:29:08 +00:00
start_byte_pos : u32 ,
end_byte_pos : u32 ,
) {
debug! (
2020-07-25 04:14:28 +00:00
" adding counter to coverage_regions: instance={:?}, function_source_hash={}, id={}, \
byte range { } .. { } " ,
instance , function_source_hash , id , start_byte_pos , end_byte_pos ,
2020-06-22 06:29:08 +00:00
) ;
2020-07-02 18:27:15 +00:00
let mut coverage_regions = self . coverage_context ( ) . function_coverage_map . borrow_mut ( ) ;
coverage_regions
. entry ( instance )
2020-07-25 04:14:28 +00:00
. or_insert_with ( | | FunctionCoverage ::new ( self . tcx , instance ) )
. add_counter ( function_source_hash , id , start_byte_pos , end_byte_pos ) ;
2020-06-22 06:29:08 +00:00
}
fn add_counter_expression_region (
& mut self ,
instance : Instance < ' tcx > ,
2020-07-25 04:14:28 +00:00
id_descending_from_max : u32 ,
2020-06-22 06:29:08 +00:00
lhs : u32 ,
2020-07-25 04:14:28 +00:00
op : ExprKind ,
2020-06-22 06:29:08 +00:00
rhs : u32 ,
start_byte_pos : u32 ,
end_byte_pos : u32 ,
) {
debug! (
2020-07-25 04:14:28 +00:00
" adding counter expression to coverage_regions: instance={:?}, id={}, {} {:?} {}, \
byte range { } .. { } " ,
instance , id_descending_from_max , lhs , op , rhs , start_byte_pos , end_byte_pos ,
2020-06-22 06:29:08 +00:00
) ;
2020-07-02 18:27:15 +00:00
let mut coverage_regions = self . coverage_context ( ) . function_coverage_map . borrow_mut ( ) ;
coverage_regions
. entry ( instance )
2020-07-25 04:14:28 +00:00
. or_insert_with ( | | FunctionCoverage ::new ( self . tcx , instance ) )
. add_counter_expression (
id_descending_from_max ,
lhs ,
op ,
rhs ,
start_byte_pos ,
end_byte_pos ,
) ;
2020-06-22 06:29:08 +00:00
}
fn add_unreachable_region (
& mut self ,
instance : Instance < ' tcx > ,
start_byte_pos : u32 ,
end_byte_pos : u32 ,
) {
debug! (
2020-07-02 18:27:15 +00:00
" adding unreachable code to coverage_regions: instance={:?}, byte range {}..{} " ,
2020-06-22 06:29:08 +00:00
instance , start_byte_pos , end_byte_pos ,
) ;
2020-07-02 18:27:15 +00:00
let mut coverage_regions = self . coverage_context ( ) . function_coverage_map . borrow_mut ( ) ;
coverage_regions
. entry ( instance )
2020-07-25 04:14:28 +00:00
. or_insert_with ( | | FunctionCoverage ::new ( self . tcx , instance ) )
. add_unreachable_region ( start_byte_pos , end_byte_pos ) ;
2020-07-02 18:27:15 +00:00
}
}
2020-07-27 23:25:08 +00:00
/// Aligns with [llvm::coverage::CounterMappingRegion::RegionKind](https://github.com/rust-lang/llvm-project/blob/rustc/10.0-2020-05-05/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h#L205-L221)
2020-07-25 04:14:28 +00:00
#[ derive(Copy, Clone, Debug) ]
#[ repr(C) ]
enum RegionKind {
/// A CodeRegion associates some code with a counter
2020-07-27 23:25:08 +00:00
CodeRegion = 0 ,
2020-07-02 18:27:15 +00:00
2020-07-25 04:14:28 +00:00
/// An ExpansionRegion represents a file expansion region that associates
/// a source range with the expansion of a virtual source file, such as
/// for a macro instantiation or #include file.
2020-07-27 23:25:08 +00:00
ExpansionRegion = 1 ,
2020-07-02 18:27:15 +00:00
2020-07-25 04:14:28 +00:00
/// A SkippedRegion represents a source range with code that was skipped
/// by a preprocessor or similar means.
2020-07-27 23:25:08 +00:00
SkippedRegion = 2 ,
2020-07-02 18:27:15 +00:00
2020-07-25 04:14:28 +00:00
/// A GapRegion is like a CodeRegion, but its count is only set as the
/// line execution count when its the only region in the line.
2020-07-27 23:25:08 +00:00
GapRegion = 3 ,
2020-07-02 18:27:15 +00:00
}
2020-07-25 04:14:28 +00:00
/// This struct provides LLVM's representation of a "CoverageMappingRegion", encoded into the
2020-07-27 23:25:08 +00:00
/// coverage map, in accordance with the
/// [LLVM Code Coverage Mapping Format](https://github.com/rust-lang/llvm-project/blob/llvmorg-8.0.0/llvm/docs/CoverageMappingFormat.rst#llvm-code-coverage-mapping-format).
/// The struct composes fields representing the `Counter` type and value(s) (injected counter ID,
/// or expression type and operands), the source file (an indirect index into a "filenames array",
/// encoded separately), and source location (start and end positions of the represented code
/// region).
2020-07-25 04:14:28 +00:00
///
2020-07-27 23:25:08 +00:00
/// Aligns with [llvm::coverage::CounterMappingRegion](https://github.com/rust-lang/llvm-project/blob/rustc/10.0-2020-05-05/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h#L223-L226)
/// Important: The Rust struct layout (order and types of fields) must match its C++ counterpart.
2020-07-25 04:14:28 +00:00
#[ derive(Copy, Clone, Debug) ]
#[ repr(C) ]
pub struct CounterMappingRegion {
/// The counter type and type-dependent counter data, if any.
counter : Counter ,
/// An indirect reference to the source filename. In the LLVM Coverage Mapping Format, the
/// file_id is an index into a function-specific `virtual_file_mapping` array of indexes that,
/// in turn, are used to look up the filename for this region.
file_id : u32 ,
2020-07-02 18:27:15 +00:00
2020-07-25 04:14:28 +00:00
/// If the `RegionKind` is an `ExpansionRegion`, the `expanded_file_id` can be used to find the
/// mapping regions created as a result of macro expansion, by checking if their file id matches
/// the expanded file id.
expanded_file_id : u32 ,
/// 1-based starting line of the mapping region.
start_line : u32 ,
/// 1-based starting column of the mapping region.
start_col : u32 ,
/// 1-based ending line of the mapping region.
end_line : u32 ,
/// 1-based ending column of the mapping region. If the high bit is set, the current mapping
/// region is a gap area.
end_col : u32 ,
kind : RegionKind ,
2020-07-02 18:27:15 +00:00
}
2020-07-25 04:14:28 +00:00
impl CounterMappingRegion {
pub fn code_region (
counter : Counter ,
file_id : u32 ,
start_line : u32 ,
start_col : u32 ,
end_line : u32 ,
end_col : u32 ,
) -> Self {
Self {
counter ,
file_id ,
expanded_file_id : 0 ,
start_line ,
start_col ,
end_line ,
end_col ,
kind : RegionKind ::CodeRegion ,
2020-07-02 18:27:15 +00:00
}
}
2020-07-25 04:14:28 +00:00
pub fn expansion_region (
file_id : u32 ,
expanded_file_id : u32 ,
start_line : u32 ,
start_col : u32 ,
end_line : u32 ,
end_col : u32 ,
) -> Self {
Self {
counter : Counter ::zero ( ) ,
file_id ,
expanded_file_id ,
start_line ,
start_col ,
end_line ,
end_col ,
kind : RegionKind ::ExpansionRegion ,
}
2020-07-02 18:27:15 +00:00
}
2020-07-25 04:14:28 +00:00
pub fn skipped_region (
2020-07-02 18:27:15 +00:00
file_id : u32 ,
2020-07-25 04:14:28 +00:00
start_line : u32 ,
start_col : u32 ,
end_line : u32 ,
end_col : u32 ,
) -> Self {
Self {
counter : Counter ::zero ( ) ,
file_id ,
expanded_file_id : 0 ,
start_line ,
start_col ,
end_line ,
end_col ,
kind : RegionKind ::SkippedRegion ,
2020-07-02 18:27:15 +00:00
}
}
2020-07-25 04:14:28 +00:00
pub fn gap_region (
counter : Counter ,
file_id : u32 ,
start_line : u32 ,
start_col : u32 ,
end_line : u32 ,
end_col : u32 ,
) -> Self {
Self {
counter ,
file_id ,
expanded_file_id : 0 ,
start_line ,
start_col ,
end_line ,
end_col : ( ( 1 as u32 ) < < 31 ) | end_col ,
kind : RegionKind ::GapRegion ,
2020-07-02 18:27:15 +00:00
}
}
}
pub ( crate ) fn write_filenames_section_to_buffer ( filenames : & Vec < CString > , buffer : & RustString ) {
let c_str_vec = filenames . iter ( ) . map ( | cstring | cstring . as_ptr ( ) ) . collect ::< Vec < _ > > ( ) ;
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 > ,
mut 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 ,
mapping_regions . as_mut_ptr ( ) ,
mapping_regions . len ( ) as c_uint ,
2020-07-02 18:27:15 +00:00
buffer ,
) ;
2020-06-22 06:29:08 +00:00
}
}
2020-07-02 18:27:15 +00:00
pub ( crate ) fn compute_hash ( name : & str ) -> u64 {
let name = CString ::new ( name ) . expect ( " null error converting hashable name to C string " ) ;
unsafe { llvm ::LLVMRustCoverageComputeHash ( name . as_ptr ( ) ) }
}
pub ( crate ) fn mapping_version ( ) -> u32 {
unsafe { llvm ::LLVMRustCoverageMappingVersion ( ) }
}
pub ( crate ) fn save_map_to_mod < ' ll , ' tcx > (
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 {
llvm ::LLVMRustCoverageWriteSectionNameToString ( cx . llmod , s ) ;
} )
. 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 ) ;
llvm ::set_linkage ( llglobal , llvm ::Linkage ::InternalLinkage ) ;
llvm ::set_section ( llglobal , & covmap_section_name ) ;
llvm ::set_alignment ( llglobal , COVMAP_VAR_ALIGN_BYTES ) ;
cx . add_used_global ( llglobal ) ;
}