@@ -53,31 +53,20 @@ pub(crate) fn finalize(cx: &CodegenCx<'_, '_>) {
53
53
None => return ,
54
54
} ;
55
55
56
- // The order of entries in this global file table needs to be deterministic,
57
- // and ideally should also be independent of the details of stable-hashing,
58
- // because coverage tests snapshots (`.cov-map`) can observe the order and
59
- // would need to be re-blessed if it changes. As long as those requirements
60
- // are satisfied, the order can be arbitrary.
61
- let mut global_file_table = GlobalFileTable :: new ( ) ;
62
-
63
56
let mut covfun_records = instances_used
64
57
. iter ( )
65
58
. copied ( )
66
59
// Sort by symbol name, so that the global file table is built in an
67
60
// order that doesn't depend on the stable-hash-based order in which
68
61
// instances were visited during codegen.
69
62
. sorted_by_cached_key ( |& instance| tcx. symbol_name ( instance) . name )
70
- . filter_map ( |instance| prepare_covfun_record ( tcx, & mut global_file_table , instance, true ) )
63
+ . filter_map ( |instance| prepare_covfun_record ( tcx, instance, true ) )
71
64
. collect :: < Vec < _ > > ( ) ;
72
65
73
66
// In a single designated CGU, also prepare covfun records for functions
74
67
// in this crate that were instrumented for coverage, but are unused.
75
68
if cx. codegen_unit . is_code_coverage_dead_code_cgu ( ) {
76
- unused:: prepare_covfun_records_for_unused_functions (
77
- cx,
78
- & mut global_file_table,
79
- & mut covfun_records,
80
- ) ;
69
+ unused:: prepare_covfun_records_for_unused_functions ( cx, & mut covfun_records) ;
81
70
}
82
71
83
72
// If there are no covfun records for this CGU, don't generate a covmap record.
@@ -89,68 +78,88 @@ pub(crate) fn finalize(cx: &CodegenCx<'_, '_>) {
89
78
return ;
90
79
}
91
80
92
- // Encode all filenames referenced by coverage mappings in this CGU.
93
- let filenames_buffer = global_file_table. make_filenames_buffer ( tcx) ;
94
- // The `llvm-cov` tool uses this hash to associate each covfun record with
95
- // its corresponding filenames table, since the final binary will typically
96
- // contain multiple covmap records from different compilation units.
97
- let filenames_hash = llvm_cov:: hash_bytes ( & filenames_buffer) ;
81
+ // Prepare the global file table for this CGU, containing all paths needed
82
+ // by one or more covfun records.
83
+ let global_file_table =
84
+ GlobalFileTable :: build ( tcx, covfun_records. iter ( ) . flat_map ( |c| c. all_source_files ( ) ) ) ;
98
85
99
86
for covfun in & covfun_records {
100
- covfun:: generate_covfun_record ( cx, filenames_hash , covfun)
87
+ covfun:: generate_covfun_record ( cx, & global_file_table , covfun)
101
88
}
102
89
103
90
// Generate the coverage map header, which contains the filenames used by
104
91
// this CGU's coverage mappings, and store it in a well-known global.
105
92
// (This is skipped if we returned early due to having no covfun records.)
106
- generate_covmap_record ( cx, covmap_version, & filenames_buffer) ;
93
+ generate_covmap_record ( cx, covmap_version, & global_file_table . filenames_buffer ) ;
107
94
}
108
95
109
- /// Maps "global" (per-CGU) file ID numbers to their underlying source files.
96
+ /// Maps "global" (per-CGU) file ID numbers to their underlying source file paths.
97
+ #[ derive( Debug ) ]
110
98
struct GlobalFileTable {
111
99
/// This "raw" table doesn't include the working dir, so a file's
112
100
/// global ID is its index in this set **plus one**.
113
- raw_file_table : FxIndexMap < StableSourceFileId , Arc < SourceFile > > ,
101
+ raw_file_table : FxIndexMap < StableSourceFileId , String > ,
102
+
103
+ /// The file table in encoded form (possibly compressed), which can be
104
+ /// included directly in this CGU's `__llvm_covmap` record.
105
+ filenames_buffer : Vec < u8 > ,
106
+
107
+ /// Truncated hash of the bytes in `filenames_buffer`.
108
+ ///
109
+ /// The `llvm-cov` tool uses this hash to associate each covfun record with
110
+ /// its corresponding filenames table, since the final binary will typically
111
+ /// contain multiple covmap records from different compilation units.
112
+ filenames_hash : u64 ,
114
113
}
115
114
116
115
impl GlobalFileTable {
117
- fn new ( ) -> Self {
118
- Self { raw_file_table : FxIndexMap :: default ( ) }
119
- }
116
+ /// Builds a "global file table" for this CGU, mapping numeric IDs to
117
+ /// path strings.
118
+ fn build < ' a > ( tcx : TyCtxt < ' _ > , all_files : impl Iterator < Item = & ' a SourceFile > ) -> Self {
119
+ let mut raw_file_table = FxIndexMap :: default ( ) ;
120
+
121
+ for file in all_files {
122
+ raw_file_table. entry ( file. stable_id ) . or_insert_with ( || {
123
+ file. name
124
+ . for_scope ( tcx. sess , RemapPathScopeComponents :: MACRO )
125
+ . to_string_lossy ( )
126
+ . into_owned ( )
127
+ } ) ;
128
+ }
129
+
130
+ // FIXME(Zalathar): Consider sorting the file table here, but maybe
131
+ // only after adding filename support to coverage-dump, so that the
132
+ // table order isn't directly visible in `.coverage-map` snapshots.
133
+
134
+ let mut table = Vec :: with_capacity ( raw_file_table. len ( ) + 1 ) ;
135
+
136
+ // Since version 6 of the LLVM coverage mapping format, the first entry
137
+ // in the global file table is treated as a base directory, used to
138
+ // resolve any other entries that are stored as relative paths.
139
+ let base_dir = tcx
140
+ . sess
141
+ . opts
142
+ . working_dir
143
+ . for_scope ( tcx. sess , RemapPathScopeComponents :: MACRO )
144
+ . to_string_lossy ( ) ;
145
+ table. push ( base_dir. as_ref ( ) ) ;
120
146
121
- fn global_file_id_for_file ( & mut self , file : & Arc < SourceFile > ) -> GlobalFileId {
122
- // Ensure the given file has a table entry, and get its index.
123
- let entry = self . raw_file_table . entry ( file. stable_id ) ;
124
- let raw_id = entry. index ( ) ;
125
- entry. or_insert_with ( || Arc :: clone ( file) ) ;
126
-
127
- // The raw file table doesn't include an entry for the working dir
128
- // (which has ID 0), so add 1 to get the correct ID.
129
- GlobalFileId :: from_usize ( raw_id + 1 )
130
- }
147
+ // Add the regular entries after the base directory.
148
+ table. extend ( raw_file_table. values ( ) . map ( |name| name. as_str ( ) ) ) ;
131
149
132
- fn make_filenames_buffer ( & self , tcx : TyCtxt < ' _ > ) -> Vec < u8 > {
133
- let mut table = Vec :: with_capacity ( self . raw_file_table . len ( ) + 1 ) ;
134
-
135
- // LLVM Coverage Mapping Format version 6 (zero-based encoded as 5)
136
- // requires setting the first filename to the compilation directory.
137
- // Since rustc generates coverage maps with relative paths, the
138
- // compilation directory can be combined with the relative paths
139
- // to get absolute paths, if needed.
140
- table. push (
141
- tcx. sess
142
- . opts
143
- . working_dir
144
- . for_scope ( tcx. sess , RemapPathScopeComponents :: MACRO )
145
- . to_string_lossy ( ) ,
146
- ) ;
150
+ // Encode the file table into a buffer, and get the hash of its encoded
151
+ // bytes, so that we can embed that hash in `__llvm_covfun` records.
152
+ let filenames_buffer = llvm_cov:: write_filenames_to_buffer ( & table) ;
153
+ let filenames_hash = llvm_cov:: hash_bytes ( & filenames_buffer) ;
147
154
148
- // Add the regular entries after the base directory.
149
- table. extend ( self . raw_file_table . values ( ) . map ( |file| {
150
- file. name . for_scope ( tcx. sess , RemapPathScopeComponents :: MACRO ) . to_string_lossy ( )
151
- } ) ) ;
155
+ Self { raw_file_table, filenames_buffer, filenames_hash }
156
+ }
152
157
153
- llvm_cov:: write_filenames_to_buffer ( & table)
158
+ fn get_existing_id ( & self , file : & SourceFile ) -> Option < GlobalFileId > {
159
+ let raw_id = self . raw_file_table . get_index_of ( & file. stable_id ) ?;
160
+ // The raw file table doesn't include an entry for the base dir
161
+ // (which has ID 0), so add 1 to get the correct ID.
162
+ Some ( GlobalFileId :: from_usize ( raw_id + 1 ) )
154
163
}
155
164
}
156
165
@@ -166,26 +175,31 @@ rustc_index::newtype_index! {
166
175
struct LocalFileId { }
167
176
}
168
177
169
- /// Holds a mapping from "local" (per-function) file IDs to "global" (per-CGU)
170
- /// file IDs .
178
+ /// Holds a mapping from "local" (per-function) file IDs to their corresponding
179
+ /// source files .
171
180
#[ derive( Debug , Default ) ]
172
181
struct VirtualFileMapping {
173
- local_to_global : IndexVec < LocalFileId , GlobalFileId > ,
174
- global_to_local : FxIndexMap < GlobalFileId , LocalFileId > ,
182
+ local_file_table : IndexVec < LocalFileId , Arc < SourceFile > > ,
175
183
}
176
184
177
185
impl VirtualFileMapping {
178
- fn local_id_for_global ( & mut self , global_file_id : GlobalFileId ) -> LocalFileId {
179
- * self
180
- . global_to_local
181
- . entry ( global_file_id)
182
- . or_insert_with ( || self . local_to_global . push ( global_file_id) )
186
+ fn push_file ( & mut self , source_file : & Arc < SourceFile > ) -> LocalFileId {
187
+ self . local_file_table . push ( Arc :: clone ( source_file) )
183
188
}
184
189
185
- fn to_vec ( & self ) -> Vec < u32 > {
186
- // This clone could be avoided by transmuting `&[GlobalFileId]` to `&[u32]`,
187
- // but it isn't hot or expensive enough to justify the extra unsafety.
188
- self . local_to_global . iter ( ) . map ( |& global| GlobalFileId :: as_u32 ( global) ) . collect ( )
190
+ /// Resolves all of the filenames in this local file mapping to a list of
191
+ /// global file IDs in its CGU, for inclusion in this function's
192
+ /// `__llvm_covfun` record.
193
+ ///
194
+ /// The global file IDs are returned as `u32` to make FFI easier.
195
+ fn resolve_all ( & self , global_file_table : & GlobalFileTable ) -> Option < Vec < u32 > > {
196
+ self . local_file_table
197
+ . iter ( )
198
+ . map ( |file| try {
199
+ let id = global_file_table. get_existing_id ( file) ?;
200
+ GlobalFileId :: as_u32 ( id)
201
+ } )
202
+ . collect :: < Option < Vec < _ > > > ( )
189
203
}
190
204
}
191
205
0 commit comments