Skip to content

Commit 0fef891

Browse files
committed
Store only a metadata stub into rlibs and dylibs with -Zsplit-metadata
1 parent 57c4a52 commit 0fef891

File tree

5 files changed

+101
-28
lines changed

5 files changed

+101
-28
lines changed

compiler/rustc_codegen_ssa/src/back/link.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -296,7 +296,7 @@ fn link_rlib<'a>(
296296
let (metadata, metadata_position) = create_wrapper_file(
297297
sess,
298298
".rmeta".to_string(),
299-
codegen_results.metadata.raw_data(),
299+
codegen_results.metadata.stub_or_full(),
300300
);
301301
let metadata = emit_wrapper_file(sess, &metadata, tmpdir, METADATA_FILENAME);
302302
match metadata_position {

compiler/rustc_codegen_ssa/src/back/metadata.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -570,8 +570,8 @@ pub fn create_compressed_metadata_file(
570570
symbol_name: &str,
571571
) -> Vec<u8> {
572572
let mut packed_metadata = rustc_metadata::METADATA_HEADER.to_vec();
573-
packed_metadata.write_all(&(metadata.raw_data().len() as u64).to_le_bytes()).unwrap();
574-
packed_metadata.extend(metadata.raw_data());
573+
packed_metadata.write_all(&(metadata.stub_or_full().len() as u64).to_le_bytes()).unwrap();
574+
packed_metadata.extend(metadata.stub_or_full());
575575

576576
let Some(mut file) = create_object_file(sess) else {
577577
if sess.target.is_like_wasm {

compiler/rustc_metadata/src/fs.rs

+20-6
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use std::{fs, io};
33

44
use rustc_data_structures::temp_dir::MaybeTempDir;
55
use rustc_middle::ty::TyCtxt;
6-
use rustc_session::config::{OutFileName, OutputType};
6+
use rustc_session::config::{CrateType, OutFileName, OutputType};
77
use rustc_session::output::filename_for_metadata;
88
use rustc_session::{MetadataKind, Session};
99
use tempfile::Builder as TempFileBuilder;
@@ -50,7 +50,14 @@ pub fn encode_and_write_metadata(tcx: TyCtxt<'_>) -> (EncodedMetadata, bool) {
5050
.tempdir_in(out_filename.parent().unwrap_or_else(|| Path::new("")))
5151
.unwrap_or_else(|err| tcx.dcx().emit_fatal(FailedCreateTempdir { err }));
5252
let metadata_tmpdir = MaybeTempDir::new(metadata_tmpdir, tcx.sess.opts.cg.save_temps);
53-
let metadata_filename = metadata_tmpdir.as_ref().join(METADATA_FILENAME);
53+
let metadata_filename = metadata_tmpdir.as_ref().join("full.rmeta");
54+
let metadata_stub_filename = if tcx.sess.opts.unstable_opts.split_metadata
55+
&& !tcx.crate_types().contains(&CrateType::ProcMacro)
56+
{
57+
Some(metadata_tmpdir.as_ref().join("stub.rmeta"))
58+
} else {
59+
None
60+
};
5461

5562
// Always create a file at `metadata_filename`, even if we have nothing to write to it.
5663
// This simplifies the creation of the output `out_filename` when requested.
@@ -60,9 +67,15 @@ pub fn encode_and_write_metadata(tcx: TyCtxt<'_>) -> (EncodedMetadata, bool) {
6067
std::fs::File::create(&metadata_filename).unwrap_or_else(|err| {
6168
tcx.dcx().emit_fatal(FailedCreateFile { filename: &metadata_filename, err });
6269
});
70+
if let Some(metadata_stub_filename) = &metadata_stub_filename {
71+
std::fs::File::create(metadata_stub_filename).unwrap_or_else(|err| {
72+
tcx.dcx()
73+
.emit_fatal(FailedCreateFile { filename: &metadata_stub_filename, err });
74+
});
75+
}
6376
}
6477
MetadataKind::Uncompressed | MetadataKind::Compressed => {
65-
encode_metadata(tcx, &metadata_filename);
78+
encode_metadata(tcx, &metadata_filename, metadata_stub_filename.as_deref())
6679
}
6780
};
6881

@@ -100,9 +113,10 @@ pub fn encode_and_write_metadata(tcx: TyCtxt<'_>) -> (EncodedMetadata, bool) {
100113

101114
// Load metadata back to memory: codegen may need to include it in object files.
102115
let metadata =
103-
EncodedMetadata::from_path(metadata_filename, metadata_tmpdir).unwrap_or_else(|err| {
104-
tcx.dcx().emit_fatal(FailedCreateEncodedMetadata { err });
105-
});
116+
EncodedMetadata::from_path(metadata_filename, metadata_stub_filename, metadata_tmpdir)
117+
.unwrap_or_else(|err| {
118+
tcx.dcx().emit_fatal(FailedCreateEncodedMetadata { err });
119+
});
106120

107121
let need_metadata_module = metadata_kind == MetadataKind::Compressed;
108122

compiler/rustc_metadata/src/rmeta/encoder.rs

+72-19
Original file line numberDiff line numberDiff line change
@@ -703,6 +703,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
703703
triple: tcx.sess.opts.target_triple.clone(),
704704
hash: tcx.crate_hash(LOCAL_CRATE),
705705
is_proc_macro_crate: proc_macro_data.is_some(),
706+
is_stub: false,
706707
},
707708
extra_filename: tcx.sess.opts.cg.extra_filename.clone(),
708709
stable_crate_id: tcx.def_path_hash(LOCAL_CRATE.as_def_id()).stable_crate_id(),
@@ -2235,54 +2236,75 @@ fn prefetch_mir(tcx: TyCtxt<'_>) {
22352236
// generated regardless of trailing bytes that end up in it.
22362237

22372238
pub struct EncodedMetadata {
2238-
// The declaration order matters because `mmap` should be dropped before `_temp_dir`.
2239-
mmap: Option<Mmap>,
2239+
// The declaration order matters because `full_metadata` should be dropped
2240+
// before `_temp_dir`.
2241+
full_metadata: Option<Mmap>,
2242+
// This is an optional stub metadata containing only the crate header.
2243+
// The header should be very small, so we load it directly into memory.
2244+
stub_metadata: Option<Vec<u8>>,
22402245
// We need to carry MaybeTempDir to avoid deleting the temporary
22412246
// directory while accessing the Mmap.
22422247
_temp_dir: Option<MaybeTempDir>,
22432248
}
22442249

22452250
impl EncodedMetadata {
22462251
#[inline]
2247-
pub fn from_path(path: PathBuf, temp_dir: Option<MaybeTempDir>) -> std::io::Result<Self> {
2252+
pub fn from_path(
2253+
path: PathBuf,
2254+
stub_path: Option<PathBuf>,
2255+
temp_dir: Option<MaybeTempDir>,
2256+
) -> std::io::Result<Self> {
22482257
let file = std::fs::File::open(&path)?;
22492258
let file_metadata = file.metadata()?;
22502259
if file_metadata.len() == 0 {
2251-
return Ok(Self { mmap: None, _temp_dir: None });
2260+
return Ok(Self { full_metadata: None, stub_metadata: None, _temp_dir: None });
22522261
}
2253-
let mmap = unsafe { Some(Mmap::map(file)?) };
2254-
Ok(Self { mmap, _temp_dir: temp_dir })
2262+
let full_mmap = unsafe { Some(Mmap::map(file)?) };
2263+
2264+
let stub =
2265+
if let Some(stub_path) = stub_path { Some(std::fs::read(stub_path)?) } else { None };
2266+
2267+
Ok(Self { full_metadata: full_mmap, stub_metadata: stub, _temp_dir: temp_dir })
2268+
}
2269+
2270+
#[inline]
2271+
pub fn full(&self) -> &[u8] {
2272+
&self.full_metadata.as_deref().unwrap_or_default()
22552273
}
22562274

22572275
#[inline]
2258-
pub fn raw_data(&self) -> &[u8] {
2259-
self.mmap.as_deref().unwrap_or_default()
2276+
pub fn stub_or_full(&self) -> &[u8] {
2277+
self.stub_metadata.as_deref().unwrap_or(self.full())
22602278
}
22612279
}
22622280

22632281
impl<S: Encoder> Encodable<S> for EncodedMetadata {
22642282
fn encode(&self, s: &mut S) {
2265-
let slice = self.raw_data();
2283+
self.stub_metadata.encode(s);
2284+
2285+
let slice = self.full();
22662286
slice.encode(s)
22672287
}
22682288
}
22692289

22702290
impl<D: Decoder> Decodable<D> for EncodedMetadata {
22712291
fn decode(d: &mut D) -> Self {
2292+
let stub = <Option<Vec<u8>>>::decode(d);
2293+
22722294
let len = d.read_usize();
2273-
let mmap = if len > 0 {
2295+
let full_metadata = if len > 0 {
22742296
let mut mmap = MmapMut::map_anon(len).unwrap();
22752297
mmap.copy_from_slice(d.read_raw_bytes(len));
22762298
Some(mmap.make_read_only().unwrap())
22772299
} else {
22782300
None
22792301
};
22802302

2281-
Self { mmap, _temp_dir: None }
2303+
Self { full_metadata, stub_metadata: stub, _temp_dir: None }
22822304
}
22832305
}
22842306

2285-
pub fn encode_metadata(tcx: TyCtxt<'_>, path: &Path) {
2307+
pub fn encode_metadata(tcx: TyCtxt<'_>, path: &Path, ref_path: Option<&Path>) {
22862308
let _prof_timer = tcx.prof.verbose_generic_activity("generate_crate_metadata");
22872309

22882310
// Since encoding metadata is not in a query, and nothing is cached,
@@ -2296,6 +2318,42 @@ pub fn encode_metadata(tcx: TyCtxt<'_>, path: &Path) {
22962318
join(|| prefetch_mir(tcx), || tcx.exported_symbols(LOCAL_CRATE));
22972319
}
22982320

2321+
with_encode_metadata_header(tcx, path, |ecx| {
2322+
// Encode all the entries and extra information in the crate,
2323+
// culminating in the `CrateRoot` which points to all of it.
2324+
let root = ecx.encode_crate_root();
2325+
2326+
// Flush buffer to ensure backing file has the correct size.
2327+
ecx.opaque.flush();
2328+
// Record metadata size for self-profiling
2329+
tcx.prof.artifact_size(
2330+
"crate_metadata",
2331+
"crate_metadata",
2332+
ecx.opaque.file().metadata().unwrap().len(),
2333+
);
2334+
2335+
root.position.get()
2336+
});
2337+
2338+
if let Some(ref_path) = ref_path {
2339+
with_encode_metadata_header(tcx, ref_path, |ecx| {
2340+
let header: LazyValue<CrateHeader> = ecx.lazy(CrateHeader {
2341+
name: tcx.crate_name(LOCAL_CRATE),
2342+
triple: tcx.sess.opts.target_triple.clone(),
2343+
hash: tcx.crate_hash(LOCAL_CRATE),
2344+
is_proc_macro_crate: false,
2345+
is_stub: true,
2346+
});
2347+
header.position.get()
2348+
});
2349+
}
2350+
}
2351+
2352+
fn with_encode_metadata_header(
2353+
tcx: TyCtxt<'_>,
2354+
path: &Path,
2355+
f: impl FnOnce(&mut EncodeContext<'_, '_>) -> usize,
2356+
) {
22992357
let mut encoder = opaque::FileEncoder::new(path)
23002358
.unwrap_or_else(|err| tcx.dcx().emit_fatal(FailCreateFileEncoder { err }));
23012359
encoder.emit_raw_bytes(METADATA_HEADER);
@@ -2330,9 +2388,7 @@ pub fn encode_metadata(tcx: TyCtxt<'_>, path: &Path) {
23302388
// Encode the rustc version string in a predictable location.
23312389
rustc_version(tcx.sess.cfg_version).encode(&mut ecx);
23322390

2333-
// Encode all the entries and extra information in the crate,
2334-
// culminating in the `CrateRoot` which points to all of it.
2335-
let root = ecx.encode_crate_root();
2391+
let root_position = f(&mut ecx);
23362392

23372393
// Make sure we report any errors from writing to the file.
23382394
// If we forget this, compilation can succeed with an incomplete rmeta file,
@@ -2342,12 +2398,9 @@ pub fn encode_metadata(tcx: TyCtxt<'_>, path: &Path) {
23422398
}
23432399

23442400
let file = ecx.opaque.file();
2345-
if let Err(err) = encode_root_position(file, root.position.get()) {
2401+
if let Err(err) = encode_root_position(file, root_position) {
23462402
tcx.dcx().emit_fatal(FailWriteFile { path: ecx.opaque.path(), err });
23472403
}
2348-
2349-
// Record metadata size for self-profiling
2350-
tcx.prof.artifact_size("crate_metadata", "crate_metadata", file.metadata().unwrap().len());
23512404
}
23522405

23532406
fn encode_root_position(mut file: &File, pos: usize) -> Result<(), std::io::Error> {

compiler/rustc_metadata/src/rmeta/mod.rs

+6
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,12 @@ pub(crate) struct CrateHeader {
220220
/// This is separate from [`ProcMacroData`] to avoid having to update [`METADATA_VERSION`] every
221221
/// time ProcMacroData changes.
222222
pub(crate) is_proc_macro_crate: bool,
223+
/// Whether this crate metadata section is just a stub.
224+
/// Stubs do not contain the full metadata (it will be typically stored
225+
/// in a separate rmeta file).
226+
///
227+
/// This is used inside rlibs and dylibs when using `-Zsplit-metadata`.
228+
pub(crate) is_stub: bool,
223229
}
224230

225231
/// Serialized `.rmeta` data for a crate.

0 commit comments

Comments
 (0)