Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Stop keeping metadata in memory before writing it to disk #96544

Merged
merged 32 commits into from
Jul 15, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
709a782
move emit_metadata to rustc_metadata::fs
m-ysk Apr 24, 2022
5d9ba49
move encode_and_write_metadata to rustc_metadata::fs
m-ysk Apr 24, 2022
e7f95ac
use rustc_serialize::opaque::FileEncoder
m-ysk Apr 25, 2022
bb75c4b
call emit_metadata only when metadata_kind is None
m-ysk Apr 28, 2022
c26c461
construct EncodedMetadata in encode_and_write_metadata
m-ysk Apr 28, 2022
8cfa7ca
hold Mmap in EncodedMetadata
m-ysk Apr 29, 2022
336af60
write to a temporary file in Decodable for EncodedMetadata
m-ysk Apr 29, 2022
34f8889
seek and write the root position to the metadata file
m-ysk Apr 29, 2022
1be5805
use BufReader for counting zero bytes
m-ysk Apr 29, 2022
aa8e064
add Send to the argument type of encode_metadata
m-ysk Apr 29, 2022
1c2cc68
fix a typo
m-ysk Apr 29, 2022
5aac75a
import all necessary parts of rustc_metadata::fs
m-ysk May 2, 2022
b28b7c9
remove non_durable_rename in rustc_interface::util
m-ysk May 2, 2022
c8e49e4
write the root position at the end
m-ysk May 2, 2022
1ead92d
call EncodedMetadata::empty in case of MetadataKind::None
m-ysk May 2, 2022
e11dd80
seek before counting zero bytes
m-ysk May 2, 2022
8d35ff1
insert dummy 4 bytes to avoid the breaking change
m-ysk May 2, 2022
dcb599f
create tmp directory if there is no parent directory
m-ysk May 5, 2022
47c3689
use &Path instead of AsRef<Path>
m-ysk May 8, 2022
c57d778
define MmapMut and use it in Decodable impl
m-ysk May 8, 2022
7eb64b4
flush and assert when counting zero bytes
m-ysk May 8, 2022
03de5c4
create an empty file even in case of MetadataKind::None
m-ysk May 29, 2022
ec64b4c
add a comment about the drop order for EncodedMetadata
m-ysk May 29, 2022
34e44e5
Revert "write the root position at the end"
m-ysk May 29, 2022
0928061
refactor encode_and_write_metadata
m-ysk May 31, 2022
63dec94
fix an incorrect filename for an error message
m-ysk Jun 5, 2022
ad55481
add some comments for encode_and_write_metadata
m-ysk Jun 5, 2022
3e30935
add a comment about counting zero bytes
m-ysk Jun 5, 2022
ea0e0f4
avoid holding the temp_dir for empty metadata file
m-ysk Jun 6, 2022
a206121
seek instead of opening a new file handle
m-ysk Jun 6, 2022
7a5e773
fall back on the blank path if out_filename is blank
m-ysk Jun 24, 2022
1147d50
ensure rustc does not panic by the test for issue-26092
m-ysk Jul 3, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3982,7 +3982,6 @@ dependencies = [
"rustc_ty_utils",
"rustc_typeck",
"smallvec",
"tempfile",
"tracing",
"winapi",
]
Expand Down Expand Up @@ -4092,6 +4091,7 @@ dependencies = [
"rustc_type_ir",
"smallvec",
"snap",
"tempfile",
"tracing",
]

Expand Down
22 changes: 2 additions & 20 deletions compiler/rustc_codegen_ssa/src/back/link.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use rustc_data_structures::temp_dir::MaybeTempDir;
use rustc_errors::{ErrorGuaranteed, Handler};
use rustc_fs_util::fix_windows_verbatim_for_gcc;
use rustc_hir::def_id::CrateNum;
use rustc_metadata::fs::{emit_metadata, METADATA_FILENAME};
use rustc_middle::middle::dependency_format::Linkage;
use rustc_middle::middle::exported_symbols::SymbolExportKind;
use rustc_session::config::{self, CFGuard, CrateType, DebugInfo, LdImpl, Strip};
Expand All @@ -28,10 +29,7 @@ use super::command::Command;
use super::linker::{self, Linker};
use super::metadata::{create_rmeta_file, MetadataPosition};
use super::rpath::{self, RPathConfig};
use crate::{
looks_like_rust_object_file, CodegenResults, CompiledModule, CrateInfo, NativeLib,
METADATA_FILENAME,
};
use crate::{looks_like_rust_object_file, CodegenResults, CompiledModule, CrateInfo, NativeLib};

use cc::windows_registry;
use regex::Regex;
Expand Down Expand Up @@ -237,22 +235,6 @@ pub fn each_linked_rlib(
Ok(())
}

/// We use a temp directory here to avoid races between concurrent rustc processes,
/// such as builds in the same directory using the same filename for metadata while
/// building an `.rlib` (stomping over one another), or writing an `.rmeta` into a
/// directory being searched for `extern crate` (observing an incomplete file).
/// The returned path is the temporary file containing the complete metadata.
pub fn emit_metadata(sess: &Session, metadata: &[u8], tmpdir: &MaybeTempDir) -> PathBuf {
let out_filename = tmpdir.as_ref().join(METADATA_FILENAME);
let result = fs::write(&out_filename, metadata);

if let Err(e) = result {
sess.fatal(&format!("failed to write {}: {}", out_filename.display(), e));
}

out_filename
}

/// Create an 'rlib'.
///
/// An rlib in its current incarnation is essentially a renamed .a file. The rlib primarily contains
Expand Down
3 changes: 1 addition & 2 deletions compiler/rustc_codegen_ssa/src/back/metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,13 @@ use rustc_data_structures::memmap::Mmap;
use rustc_data_structures::owning_ref::OwningRef;
use rustc_data_structures::rustc_erase_owner;
use rustc_data_structures::sync::MetadataRef;
use rustc_metadata::fs::METADATA_FILENAME;
use rustc_metadata::EncodedMetadata;
use rustc_session::cstore::MetadataLoader;
use rustc_session::Session;
use rustc_target::abi::Endian;
use rustc_target::spec::{RelocModel, Target};

use crate::METADATA_FILENAME;

/// The default metadata loader. This is used by cg_llvm and cg_clif.
///
/// # Metadata location
Expand Down
3 changes: 0 additions & 3 deletions compiler/rustc_codegen_ssa/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,6 @@ pub struct ModuleCodegen<M> {
pub kind: ModuleKind,
}

// FIXME(eddyb) maybe include the crate name in this?
pub const METADATA_FILENAME: &str = "lib.rmeta";

impl<M> ModuleCodegen<M> {
pub fn into_compiled_module(
self,
Expand Down
63 changes: 62 additions & 1 deletion compiler/rustc_data_structures/src/memmap.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::fs::File;
use std::io;
use std::ops::Deref;
use std::ops::{Deref, DerefMut};

use crate::owning_ref::StableAddress;

Expand Down Expand Up @@ -45,3 +45,64 @@ impl Deref for Mmap {
// export any function that can cause the `Vec` to be re-allocated. As such the address of the
// bytes inside this `Vec` is stable.
unsafe impl StableAddress for Mmap {}

#[cfg(not(target_arch = "wasm32"))]
pub struct MmapMut(memmap2::MmapMut);

#[cfg(target_arch = "wasm32")]
pub struct MmapMut(Vec<u8>);

#[cfg(not(target_arch = "wasm32"))]
impl MmapMut {
#[inline]
pub fn map_anon(len: usize) -> io::Result<Self> {
let mmap = memmap2::MmapMut::map_anon(len)?;
Ok(MmapMut(mmap))
}

#[inline]
pub fn flush(&mut self) -> io::Result<()> {
self.0.flush()
}

#[inline]
pub fn make_read_only(self) -> std::io::Result<Mmap> {
let mmap = self.0.make_read_only()?;
Ok(Mmap(mmap))
}
}

#[cfg(target_arch = "wasm32")]
impl MmapMut {
#[inline]
pub fn map_anon(len: usize) -> io::Result<Self> {
let data = Vec::with_capacity(len);
Ok(MmapMut(data))
}

#[inline]
pub fn flush(&mut self) -> io::Result<()> {
Ok(())
}

#[inline]
pub fn make_read_only(self) -> std::io::Result<Mmap> {
Ok(Mmap(self.0))
}
}

impl Deref for MmapMut {
type Target = [u8];

#[inline]
fn deref(&self) -> &[u8] {
&*self.0
}
}

impl DerefMut for MmapMut {
#[inline]
fn deref_mut(&mut self) -> &mut [u8] {
&mut *self.0
}
}
1 change: 0 additions & 1 deletion compiler/rustc_interface/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ rustc_query_impl = { path = "../rustc_query_impl" }
rustc_resolve = { path = "../rustc_resolve" }
rustc_trait_selection = { path = "../rustc_trait_selection" }
rustc_ty_utils = { path = "../rustc_ty_utils" }
tempfile = "3.2"

[target.'cfg(unix)'.dependencies]
libc = "0.2"
Expand Down
74 changes: 4 additions & 70 deletions compiler/rustc_interface/src/passes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,16 @@ use crate::util;
use ast::CRATE_NODE_ID;
use rustc_ast::{self as ast, visit};
use rustc_borrowck as mir_borrowck;
use rustc_codegen_ssa::back::link::emit_metadata;
use rustc_codegen_ssa::traits::CodegenBackend;
use rustc_data_structures::parallel;
use rustc_data_structures::sync::{Lrc, OnceCell, WorkerLocal};
use rustc_data_structures::temp_dir::MaybeTempDir;
use rustc_errors::{Applicability, ErrorGuaranteed, MultiSpan, PResult};
use rustc_expand::base::{ExtCtxt, LintStoreExpand, ResolverExpand};
use rustc_hir::def_id::{StableCrateId, LOCAL_CRATE};
use rustc_hir::def_id::StableCrateId;
use rustc_hir::definitions::Definitions;
use rustc_hir::Crate;
use rustc_lint::{EarlyCheckNode, LintStore};
use rustc_metadata::creader::CStore;
use rustc_metadata::{encode_metadata, EncodedMetadata};
use rustc_middle::arena::Arena;
use rustc_middle::dep_graph::DepGraph;
use rustc_middle::ty::query::{ExternProviders, Providers};
Expand All @@ -30,14 +27,13 @@ use rustc_query_impl::{OnDiskCache, Queries as TcxQueries};
use rustc_resolve::{Resolver, ResolverArenas};
use rustc_session::config::{CrateType, Input, OutputFilenames, OutputType};
use rustc_session::cstore::{CrateStoreDyn, MetadataLoader, MetadataLoaderDyn};
use rustc_session::output::{filename_for_input, filename_for_metadata};
use rustc_session::output::filename_for_input;
use rustc_session::search_paths::PathKind;
use rustc_session::{Limit, Session};
use rustc_span::symbol::{sym, Symbol};
use rustc_span::FileName;
use rustc_trait_selection::traits;
use rustc_typeck as typeck;
use tempfile::Builder as TempFileBuilder;
use tracing::{info, warn};

use std::any::Any;
Expand Down Expand Up @@ -1030,69 +1026,6 @@ fn analysis(tcx: TyCtxt<'_>, (): ()) -> Result<()> {
Ok(())
}

fn encode_and_write_metadata(
tcx: TyCtxt<'_>,
outputs: &OutputFilenames,
) -> (EncodedMetadata, bool) {
#[derive(PartialEq, Eq, PartialOrd, Ord)]
enum MetadataKind {
None,
Uncompressed,
Compressed,
}

let metadata_kind = tcx
.sess
.crate_types()
.iter()
.map(|ty| match *ty {
CrateType::Executable | CrateType::Staticlib | CrateType::Cdylib => MetadataKind::None,

CrateType::Rlib => MetadataKind::Uncompressed,

CrateType::Dylib | CrateType::ProcMacro => MetadataKind::Compressed,
})
.max()
.unwrap_or(MetadataKind::None);

let metadata = match metadata_kind {
MetadataKind::None => EncodedMetadata::new(),
MetadataKind::Uncompressed | MetadataKind::Compressed => encode_metadata(tcx),
};

let _prof_timer = tcx.sess.prof.generic_activity("write_crate_metadata");

let need_metadata_file = tcx.sess.opts.output_types.contains_key(&OutputType::Metadata);
if need_metadata_file {
let crate_name = tcx.crate_name(LOCAL_CRATE);
let out_filename = filename_for_metadata(tcx.sess, crate_name.as_str(), outputs);
// To avoid races with another rustc process scanning the output directory,
// we need to write the file somewhere else and atomically move it to its
// final destination, with an `fs::rename` call. In order for the rename to
// always succeed, the temporary file needs to be on the same filesystem,
// which is why we create it inside the output directory specifically.
let metadata_tmpdir = TempFileBuilder::new()
.prefix("rmeta")
.tempdir_in(out_filename.parent().unwrap())
.unwrap_or_else(|err| tcx.sess.fatal(&format!("couldn't create a temp dir: {}", err)));
let metadata_tmpdir = MaybeTempDir::new(metadata_tmpdir, tcx.sess.opts.cg.save_temps);
let metadata_filename = emit_metadata(tcx.sess, metadata.raw_data(), &metadata_tmpdir);
if let Err(e) = util::non_durable_rename(&metadata_filename, &out_filename) {
tcx.sess.fatal(&format!("failed to write {}: {}", out_filename.display(), e));
}
if tcx.sess.opts.json_artifact_notifications {
tcx.sess
.parse_sess
.span_diagnostic
.emit_artifact_notification(&out_filename, "metadata");
}
}

let need_metadata_module = metadata_kind == MetadataKind::Compressed;

(metadata, need_metadata_module)
}

/// Runs the codegen backend, after which the AST and analysis can
/// be discarded.
pub fn start_codegen<'tcx>(
Expand All @@ -1102,7 +1035,8 @@ pub fn start_codegen<'tcx>(
) -> Box<dyn Any> {
info!("Pre-codegen\n{:?}", tcx.debug_stats());

let (metadata, need_metadata_module) = encode_and_write_metadata(tcx, outputs);
let (metadata, need_metadata_module) =
rustc_metadata::fs::encode_and_write_metadata(tcx, outputs);

let codegen = tcx.sess.time("codegen_crate", move || {
codegen_backend.codegen_crate(tcx, metadata, need_metadata_module)
Expand Down
18 changes: 0 additions & 18 deletions compiler/rustc_interface/src/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -655,24 +655,6 @@ pub fn build_output_filenames(
}
}

#[cfg(not(target_os = "linux"))]
pub fn non_durable_rename(src: &Path, dst: &Path) -> std::io::Result<()> {
std::fs::rename(src, dst)
}

/// This function attempts to bypass the auto_da_alloc heuristic implemented by some filesystems
/// such as btrfs and ext4. When renaming over a file that already exists then they will "helpfully"
/// write back the source file before committing the rename in case a developer forgot some of
/// the fsyncs in the open/write/fsync(file)/rename/fsync(dir) dance for atomic file updates.
///
/// To avoid triggering this heuristic we delete the destination first, if it exists.
/// The cost of an extra syscall is much lower than getting descheduled for the sync IO.
#[cfg(target_os = "linux")]
pub fn non_durable_rename(src: &Path, dst: &Path) -> std::io::Result<()> {
let _ = std::fs::remove_file(dst);
std::fs::rename(src, dst)
}

/// Returns a version string such as "1.46.0 (04488afe3 2020-08-24)"
pub fn version_str() -> Option<&'static str> {
option_env!("CFG_VERSION")
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_metadata/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ odht = { version = "0.3.1", features = ["nightly"] }
snap = "1"
tracing = "0.1"
smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
tempfile = "3.2"
rustc_middle = { path = "../rustc_middle" }
rustc_attr = { path = "../rustc_attr" }
rustc_data_structures = { path = "../rustc_data_structures" }
Expand Down