Skip to content

Commit

Permalink
Auto merge of #93816 - bjorn3:rlib_metadata_first, r=nagisa
Browse files Browse the repository at this point in the history
Put crate metadata first in the rlib

This should make metadata lookup faster

Fixes #93806
  • Loading branch information
bors committed Feb 20, 2022
2 parents 6d7aa47 + 932559c commit 3b18651
Show file tree
Hide file tree
Showing 2 changed files with 68 additions and 40 deletions.
97 changes: 60 additions & 37 deletions compiler/rustc_codegen_ssa/src/back/link.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ use rustc_target::spec::{PanicStrategy, RelocModel, RelroLevel, SanitizerSet, Ta
use super::archive::{find_library, ArchiveBuilder};
use super::command::Command;
use super::linker::{self, Linker};
use super::metadata::create_rmeta_file;
use super::metadata::{create_rmeta_file, MetadataPosition};
use super::rpath::{self, RPathConfig};
use crate::{
looks_like_rust_object_file, CodegenResults, CompiledModule, CrateInfo, NativeLib,
Expand Down Expand Up @@ -267,6 +267,28 @@ fn link_rlib<'a, B: ArchiveBuilder<'a>>(

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

let trailing_metadata = match flavor {
RlibFlavor::Normal => {
let (metadata, metadata_position) =
create_rmeta_file(sess, codegen_results.metadata.raw_data());
let metadata = emit_metadata(sess, &metadata, tmpdir);
match metadata_position {
MetadataPosition::First => {
// Most of the time metadata in rlib files is wrapped in a "dummy" object
// file for the target platform so the rlib can be processed entirely by
// normal linkers for the platform. Sometimes this is not possible however.
// If it is possible however, placing the metadata object first improves
// performance of getting metadata from rlibs.
ab.add_file(&metadata);
None
}
MetadataPosition::Last => Some(metadata),
}
}

RlibFlavor::StaticlibBase => None,
};

for m in &codegen_results.modules {
if let Some(obj) = m.object.as_ref() {
ab.add_file(obj);
Expand All @@ -277,6 +299,16 @@ fn link_rlib<'a, B: ArchiveBuilder<'a>>(
}
}

match flavor {
RlibFlavor::Normal => {}
RlibFlavor::StaticlibBase => {
let obj = codegen_results.allocator_module.as_ref().and_then(|m| m.object.as_ref());
if let Some(obj) = obj {
ab.add_file(obj);
}
}
}

// Note that in this loop we are ignoring the value of `lib.cfg`. That is,
// we may not be configured to actually include a static library if we're
// adding it here. That's because later when we consume this rlib we'll
Expand Down Expand Up @@ -334,42 +366,33 @@ fn link_rlib<'a, B: ArchiveBuilder<'a>>(
ab.inject_dll_import_lib(&raw_dylib_name, &raw_dylib_imports, tmpdir);
}

// Note that it is important that we add all of our non-object "magical
// files" *after* all of the object files in the archive. The reason for
// this is as follows:
//
// * When performing LTO, this archive will be modified to remove
// objects from above. The reason for this is described below.
//
// * When the system linker looks at an archive, it will attempt to
// determine the architecture of the archive in order to see whether its
// linkable.
//
// The algorithm for this detection is: iterate over the files in the
// archive. Skip magical SYMDEF names. Interpret the first file as an
// object file. Read architecture from the object file.
//
// * As one can probably see, if "metadata" and "foo.bc" were placed
// before all of the objects, then the architecture of this archive would
// not be correctly inferred once 'foo.o' is removed.
//
// Basically, all this means is that this code should not move above the
// code above.
match flavor {
RlibFlavor::Normal => {
// metadata in rlib files is wrapped in a "dummy" object file for
// the target platform so the rlib can be processed entirely by
// normal linkers for the platform.
let metadata = create_rmeta_file(sess, codegen_results.metadata.raw_data());
ab.add_file(&emit_metadata(sess, &metadata, tmpdir));
}

RlibFlavor::StaticlibBase => {
let obj = codegen_results.allocator_module.as_ref().and_then(|m| m.object.as_ref());
if let Some(obj) = obj {
ab.add_file(obj);
}
}
if let Some(trailing_metadata) = trailing_metadata {
// Note that it is important that we add all of our non-object "magical
// files" *after* all of the object files in the archive. The reason for
// this is as follows:
//
// * When performing LTO, this archive will be modified to remove
// objects from above. The reason for this is described below.
//
// * When the system linker looks at an archive, it will attempt to
// determine the architecture of the archive in order to see whether its
// linkable.
//
// The algorithm for this detection is: iterate over the files in the
// archive. Skip magical SYMDEF names. Interpret the first file as an
// object file. Read architecture from the object file.
//
// * As one can probably see, if "metadata" and "foo.bc" were placed
// before all of the objects, then the architecture of this archive would
// not be correctly inferred once 'foo.o' is removed.
//
// * Most of the time metadata in rlib files is wrapped in a "dummy" object
// file for the target platform so the rlib can be processed entirely by
// normal linkers for the platform. Sometimes this is not possible however.
//
// Basically, all this means is that this code should not move above the
// code above.
ab.add_file(&trailing_metadata);
}

return Ok(ab);
Expand Down
11 changes: 8 additions & 3 deletions compiler/rustc_codegen_ssa/src/back/metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,11 @@ fn create_object_file(sess: &Session) -> Option<write::Object<'static>> {
Some(file)
}

pub enum MetadataPosition {
First,
Last,
}

// For rlibs we "pack" rustc metadata into a dummy object file. When rustc
// creates a dylib crate type it will pass `--whole-archive` (or the
// platform equivalent) to include all object files from an rlib into the
Expand Down Expand Up @@ -198,7 +203,7 @@ fn create_object_file(sess: &Session) -> Option<write::Object<'static>> {
// * ELF - All other targets are similar to Windows in that there's a
// `SHF_EXCLUDE` flag we can set on sections in an object file to get
// automatically removed from the final output.
pub fn create_rmeta_file(sess: &Session, metadata: &[u8]) -> Vec<u8> {
pub fn create_rmeta_file(sess: &Session, metadata: &[u8]) -> (Vec<u8>, MetadataPosition) {
let Some(mut file) = create_object_file(sess) else {
// This is used to handle all "other" targets. This includes targets
// in two categories:
Expand All @@ -216,7 +221,7 @@ pub fn create_rmeta_file(sess: &Session, metadata: &[u8]) -> Vec<u8> {
// WebAssembly and for targets not supported by the `object` crate
// yet it means that work will need to be done in the `object` crate
// to add a case above.
return metadata.to_vec();
return (metadata.to_vec(), MetadataPosition::Last);
};
let section = file.add_section(
file.segment_name(StandardSegment::Debug).to_vec(),
Expand All @@ -235,7 +240,7 @@ pub fn create_rmeta_file(sess: &Session, metadata: &[u8]) -> Vec<u8> {
_ => {}
};
file.append_section_data(section, metadata, 1);
file.write().unwrap()
(file.write().unwrap(), MetadataPosition::First)
}

// Historical note:
Expand Down

0 comments on commit 3b18651

Please sign in to comment.