From 932559cc21a2f0746012645f130956b81480cd86 Mon Sep 17 00:00:00 2001 From: bjorn3 Date: Thu, 10 Feb 2022 18:43:51 +0100 Subject: [PATCH] Put crate metadata first in the rlib when possible This should make metadata lookup faster --- compiler/rustc_codegen_ssa/src/back/link.rs | 97 ++++++++++++------- .../rustc_codegen_ssa/src/back/metadata.rs | 11 ++- 2 files changed, 68 insertions(+), 40 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index 58e0667d67898..2c35cf49578c8 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -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, @@ -267,6 +267,28 @@ fn link_rlib<'a, B: ArchiveBuilder<'a>>( let mut ab = ::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); @@ -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 @@ -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); diff --git a/compiler/rustc_codegen_ssa/src/back/metadata.rs b/compiler/rustc_codegen_ssa/src/back/metadata.rs index 9ebbcac76a28a..5ad599d1f2e1e 100644 --- a/compiler/rustc_codegen_ssa/src/back/metadata.rs +++ b/compiler/rustc_codegen_ssa/src/back/metadata.rs @@ -167,6 +167,11 @@ fn create_object_file(sess: &Session) -> Option> { 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 @@ -199,7 +204,7 @@ fn create_object_file(sess: &Session) -> Option> { // * 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 { +pub fn create_rmeta_file(sess: &Session, metadata: &[u8]) -> (Vec, MetadataPosition) { let Some(mut file) = create_object_file(sess) else { // This is used to handle all "other" targets. This includes targets // in two categories: @@ -217,7 +222,7 @@ pub fn create_rmeta_file(sess: &Session, metadata: &[u8]) -> Vec { // 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(), @@ -236,7 +241,7 @@ pub fn create_rmeta_file(sess: &Session, metadata: &[u8]) -> Vec { _ => {} }; file.append_section_data(section, metadata, 1); - file.write().unwrap() + (file.write().unwrap(), MetadataPosition::First) } // Historical note: