Skip to content

Commit 3b18651

Browse files
committed
Auto merge of rust-lang#93816 - bjorn3:rlib_metadata_first, r=nagisa
Put crate metadata first in the rlib This should make metadata lookup faster Fixes rust-lang#93806
2 parents 6d7aa47 + 932559c commit 3b18651

File tree

2 files changed

+68
-40
lines changed

2 files changed

+68
-40
lines changed

compiler/rustc_codegen_ssa/src/back/link.rs

+60-37
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ use rustc_target::spec::{PanicStrategy, RelocModel, RelroLevel, SanitizerSet, Ta
2323
use super::archive::{find_library, ArchiveBuilder};
2424
use super::command::Command;
2525
use super::linker::{self, Linker};
26-
use super::metadata::create_rmeta_file;
26+
use super::metadata::{create_rmeta_file, MetadataPosition};
2727
use super::rpath::{self, RPathConfig};
2828
use crate::{
2929
looks_like_rust_object_file, CodegenResults, CompiledModule, CrateInfo, NativeLib,
@@ -267,6 +267,28 @@ fn link_rlib<'a, B: ArchiveBuilder<'a>>(
267267

268268
let mut ab = <B as ArchiveBuilder>::new(sess, out_filename, None);
269269

270+
let trailing_metadata = match flavor {
271+
RlibFlavor::Normal => {
272+
let (metadata, metadata_position) =
273+
create_rmeta_file(sess, codegen_results.metadata.raw_data());
274+
let metadata = emit_metadata(sess, &metadata, tmpdir);
275+
match metadata_position {
276+
MetadataPosition::First => {
277+
// Most of the time metadata in rlib files is wrapped in a "dummy" object
278+
// file for the target platform so the rlib can be processed entirely by
279+
// normal linkers for the platform. Sometimes this is not possible however.
280+
// If it is possible however, placing the metadata object first improves
281+
// performance of getting metadata from rlibs.
282+
ab.add_file(&metadata);
283+
None
284+
}
285+
MetadataPosition::Last => Some(metadata),
286+
}
287+
}
288+
289+
RlibFlavor::StaticlibBase => None,
290+
};
291+
270292
for m in &codegen_results.modules {
271293
if let Some(obj) = m.object.as_ref() {
272294
ab.add_file(obj);
@@ -277,6 +299,16 @@ fn link_rlib<'a, B: ArchiveBuilder<'a>>(
277299
}
278300
}
279301

302+
match flavor {
303+
RlibFlavor::Normal => {}
304+
RlibFlavor::StaticlibBase => {
305+
let obj = codegen_results.allocator_module.as_ref().and_then(|m| m.object.as_ref());
306+
if let Some(obj) = obj {
307+
ab.add_file(obj);
308+
}
309+
}
310+
}
311+
280312
// Note that in this loop we are ignoring the value of `lib.cfg`. That is,
281313
// we may not be configured to actually include a static library if we're
282314
// adding it here. That's because later when we consume this rlib we'll
@@ -334,42 +366,33 @@ fn link_rlib<'a, B: ArchiveBuilder<'a>>(
334366
ab.inject_dll_import_lib(&raw_dylib_name, &raw_dylib_imports, tmpdir);
335367
}
336368

337-
// Note that it is important that we add all of our non-object "magical
338-
// files" *after* all of the object files in the archive. The reason for
339-
// this is as follows:
340-
//
341-
// * When performing LTO, this archive will be modified to remove
342-
// objects from above. The reason for this is described below.
343-
//
344-
// * When the system linker looks at an archive, it will attempt to
345-
// determine the architecture of the archive in order to see whether its
346-
// linkable.
347-
//
348-
// The algorithm for this detection is: iterate over the files in the
349-
// archive. Skip magical SYMDEF names. Interpret the first file as an
350-
// object file. Read architecture from the object file.
351-
//
352-
// * As one can probably see, if "metadata" and "foo.bc" were placed
353-
// before all of the objects, then the architecture of this archive would
354-
// not be correctly inferred once 'foo.o' is removed.
355-
//
356-
// Basically, all this means is that this code should not move above the
357-
// code above.
358-
match flavor {
359-
RlibFlavor::Normal => {
360-
// metadata in rlib files is wrapped in a "dummy" object file for
361-
// the target platform so the rlib can be processed entirely by
362-
// normal linkers for the platform.
363-
let metadata = create_rmeta_file(sess, codegen_results.metadata.raw_data());
364-
ab.add_file(&emit_metadata(sess, &metadata, tmpdir));
365-
}
366-
367-
RlibFlavor::StaticlibBase => {
368-
let obj = codegen_results.allocator_module.as_ref().and_then(|m| m.object.as_ref());
369-
if let Some(obj) = obj {
370-
ab.add_file(obj);
371-
}
372-
}
369+
if let Some(trailing_metadata) = trailing_metadata {
370+
// Note that it is important that we add all of our non-object "magical
371+
// files" *after* all of the object files in the archive. The reason for
372+
// this is as follows:
373+
//
374+
// * When performing LTO, this archive will be modified to remove
375+
// objects from above. The reason for this is described below.
376+
//
377+
// * When the system linker looks at an archive, it will attempt to
378+
// determine the architecture of the archive in order to see whether its
379+
// linkable.
380+
//
381+
// The algorithm for this detection is: iterate over the files in the
382+
// archive. Skip magical SYMDEF names. Interpret the first file as an
383+
// object file. Read architecture from the object file.
384+
//
385+
// * As one can probably see, if "metadata" and "foo.bc" were placed
386+
// before all of the objects, then the architecture of this archive would
387+
// not be correctly inferred once 'foo.o' is removed.
388+
//
389+
// * Most of the time metadata in rlib files is wrapped in a "dummy" object
390+
// file for the target platform so the rlib can be processed entirely by
391+
// normal linkers for the platform. Sometimes this is not possible however.
392+
//
393+
// Basically, all this means is that this code should not move above the
394+
// code above.
395+
ab.add_file(&trailing_metadata);
373396
}
374397

375398
return Ok(ab);

compiler/rustc_codegen_ssa/src/back/metadata.rs

+8-3
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,11 @@ fn create_object_file(sess: &Session) -> Option<write::Object<'static>> {
166166
Some(file)
167167
}
168168

169+
pub enum MetadataPosition {
170+
First,
171+
Last,
172+
}
173+
169174
// For rlibs we "pack" rustc metadata into a dummy object file. When rustc
170175
// creates a dylib crate type it will pass `--whole-archive` (or the
171176
// platform equivalent) to include all object files from an rlib into the
@@ -198,7 +203,7 @@ fn create_object_file(sess: &Session) -> Option<write::Object<'static>> {
198203
// * ELF - All other targets are similar to Windows in that there's a
199204
// `SHF_EXCLUDE` flag we can set on sections in an object file to get
200205
// automatically removed from the final output.
201-
pub fn create_rmeta_file(sess: &Session, metadata: &[u8]) -> Vec<u8> {
206+
pub fn create_rmeta_file(sess: &Session, metadata: &[u8]) -> (Vec<u8>, MetadataPosition) {
202207
let Some(mut file) = create_object_file(sess) else {
203208
// This is used to handle all "other" targets. This includes targets
204209
// in two categories:
@@ -216,7 +221,7 @@ pub fn create_rmeta_file(sess: &Session, metadata: &[u8]) -> Vec<u8> {
216221
// WebAssembly and for targets not supported by the `object` crate
217222
// yet it means that work will need to be done in the `object` crate
218223
// to add a case above.
219-
return metadata.to_vec();
224+
return (metadata.to_vec(), MetadataPosition::Last);
220225
};
221226
let section = file.add_section(
222227
file.segment_name(StandardSegment::Debug).to_vec(),
@@ -235,7 +240,7 @@ pub fn create_rmeta_file(sess: &Session, metadata: &[u8]) -> Vec<u8> {
235240
_ => {}
236241
};
237242
file.append_section_data(section, metadata, 1);
238-
file.write().unwrap()
243+
(file.write().unwrap(), MetadataPosition::First)
239244
}
240245

241246
// Historical note:

0 commit comments

Comments
 (0)