Call FileEncoder::finish in rmeta encoding

This commit is contained in:
Ben Kimock 2023-10-27 21:26:43 -04:00
parent c387f012b1
commit fbaa24ee35
14 changed files with 82 additions and 41 deletions

View File

@ -4041,6 +4041,7 @@ dependencies = [
"rustc_query_impl", "rustc_query_impl",
"rustc_query_system", "rustc_query_system",
"rustc_resolve", "rustc_resolve",
"rustc_serialize",
"rustc_session", "rustc_session",
"rustc_span", "rustc_span",
"rustc_symbol_mangling", "rustc_symbol_mangling",

View File

@ -226,7 +226,7 @@ impl CodegenResults {
encoder.emit_raw_bytes(&RLINK_VERSION.to_be_bytes()); encoder.emit_raw_bytes(&RLINK_VERSION.to_be_bytes());
encoder.emit_str(sess.cfg_version); encoder.emit_str(sess.cfg_version);
Encodable::encode(codegen_results, &mut encoder); Encodable::encode(codegen_results, &mut encoder);
encoder.finish() encoder.finish().map_err(|(_path, err)| err)
} }
pub fn deserialize_rlink(sess: &Session, data: Vec<u8>) -> Result<Self, CodegenErrors> { pub fn deserialize_rlink(sess: &Session, data: Vec<u8>) -> Result<Self, CodegenErrors> {

View File

@ -80,8 +80,8 @@ where
); );
debug!("save: data written to disk successfully"); debug!("save: data written to disk successfully");
} }
Err(err) => { Err((path, err)) => {
sess.emit_err(errors::WriteNew { name, path: path_buf, err }); sess.emit_err(errors::WriteNew { name, path, err });
} }
} }
} }

View File

@ -50,9 +50,6 @@ pub fn save_dep_graph(tcx: TyCtxt<'_>) {
join( join(
move || { move || {
sess.time("incr_comp_persist_dep_graph", || { sess.time("incr_comp_persist_dep_graph", || {
if let Err(err) = tcx.dep_graph.encode(&tcx.sess.prof) {
sess.emit_err(errors::WriteDepGraph { path: &staging_dep_graph_path, err });
}
if let Err(err) = fs::rename(&staging_dep_graph_path, &dep_graph_path) { if let Err(err) = fs::rename(&staging_dep_graph_path, &dep_graph_path) {
sess.emit_err(errors::MoveDepGraph { sess.emit_err(errors::MoveDepGraph {
from: &staging_dep_graph_path, from: &staging_dep_graph_path,

View File

@ -40,6 +40,7 @@ rustc_privacy = { path = "../rustc_privacy" }
rustc_query_impl = { path = "../rustc_query_impl" } rustc_query_impl = { path = "../rustc_query_impl" }
rustc_query_system = { path = "../rustc_query_system" } rustc_query_system = { path = "../rustc_query_system" }
rustc_resolve = { path = "../rustc_resolve" } rustc_resolve = { path = "../rustc_resolve" }
rustc_serialize = { path = "../rustc_serialize" }
rustc_session = { path = "../rustc_session" } rustc_session = { path = "../rustc_session" }
rustc_span = { path = "../rustc_span" } rustc_span = { path = "../rustc_span" }
rustc_symbol_mangling = { path = "../rustc_symbol_mangling" } rustc_symbol_mangling = { path = "../rustc_symbol_mangling" }

View File

@ -1,6 +1,6 @@
use crate::errors::{FailedWritingFile, RustcErrorFatal, RustcErrorUnexpectedAnnotation}; use crate::errors::{FailedWritingFile, RustcErrorFatal, RustcErrorUnexpectedAnnotation};
use crate::interface::{Compiler, Result}; use crate::interface::{Compiler, Result};
use crate::{passes, util}; use crate::{errors, passes, util};
use rustc_ast as ast; use rustc_ast as ast;
use rustc_codegen_ssa::traits::CodegenBackend; use rustc_codegen_ssa::traits::CodegenBackend;
@ -15,6 +15,7 @@ use rustc_metadata::creader::CStore;
use rustc_middle::arena::Arena; use rustc_middle::arena::Arena;
use rustc_middle::dep_graph::DepGraph; use rustc_middle::dep_graph::DepGraph;
use rustc_middle::ty::{GlobalCtxt, TyCtxt}; use rustc_middle::ty::{GlobalCtxt, TyCtxt};
use rustc_serialize::opaque::FileEncodeResult;
use rustc_session::config::{self, CrateType, OutputFilenames, OutputType}; use rustc_session::config::{self, CrateType, OutputFilenames, OutputType};
use rustc_session::cstore::Untracked; use rustc_session::cstore::Untracked;
use rustc_session::output::find_crate_name; use rustc_session::output::find_crate_name;
@ -102,6 +103,10 @@ impl<'tcx> Queries<'tcx> {
} }
} }
pub fn finish(&self) -> FileEncodeResult {
if let Some(gcx) = self.gcx_cell.get() { gcx.finish() } else { Ok(0) }
}
pub fn parse(&self) -> Result<QueryResult<'_, ast::Crate>> { pub fn parse(&self) -> Result<QueryResult<'_, ast::Crate>> {
self.parse.compute(|| { self.parse.compute(|| {
passes::parse(&self.compiler.sess).map_err(|mut parse_error| parse_error.emit()) passes::parse(&self.compiler.sess).map_err(|mut parse_error| parse_error.emit())
@ -317,6 +322,9 @@ impl Compiler {
// The timer's lifetime spans the dropping of `queries`, which contains // The timer's lifetime spans the dropping of `queries`, which contains
// the global context. // the global context.
_timer = Some(self.sess.timer("free_global_ctxt")); _timer = Some(self.sess.timer("free_global_ctxt"));
if let Err((path, error)) = queries.finish() {
self.sess.emit_err(errors::FailedWritingFile { path: &path, error });
}
ret ret
} }

View File

@ -63,11 +63,8 @@ metadata_extern_location_not_file =
metadata_fail_create_file_encoder = metadata_fail_create_file_encoder =
failed to create file encoder: {$err} failed to create file encoder: {$err}
metadata_fail_seek_file =
failed to seek the file: {$err}
metadata_fail_write_file = metadata_fail_write_file =
failed to write to the file: {$err} failed to write to `{$path}`: {$err}
metadata_failed_copy_to_stdout = metadata_failed_copy_to_stdout =
failed to copy {$filename} to stdout: {$err} failed to copy {$filename} to stdout: {$err}

View File

@ -307,15 +307,10 @@ pub struct FailCreateFileEncoder {
pub err: Error, pub err: Error,
} }
#[derive(Diagnostic)]
#[diag(metadata_fail_seek_file)]
pub struct FailSeekFile {
pub err: Error,
}
#[derive(Diagnostic)] #[derive(Diagnostic)]
#[diag(metadata_fail_write_file)] #[diag(metadata_fail_write_file)]
pub struct FailWriteFile { pub struct FailWriteFile<'a> {
pub path: &'a Path,
pub err: Error, pub err: Error,
} }

View File

@ -1,4 +1,4 @@
use crate::errors::{FailCreateFileEncoder, FailSeekFile, FailWriteFile}; use crate::errors::{FailCreateFileEncoder, FailWriteFile};
use crate::rmeta::def_path_hash_map::DefPathHashMapRef; use crate::rmeta::def_path_hash_map::DefPathHashMapRef;
use crate::rmeta::table::TableBuilder; use crate::rmeta::table::TableBuilder;
use crate::rmeta::*; use crate::rmeta::*;
@ -42,6 +42,7 @@ use rustc_span::symbol::{sym, Symbol};
use rustc_span::{self, ExternalSource, FileName, SourceFile, Span, SpanData, SyntaxContext}; use rustc_span::{self, ExternalSource, FileName, SourceFile, Span, SpanData, SyntaxContext};
use std::borrow::Borrow; use std::borrow::Borrow;
use std::collections::hash_map::Entry; use std::collections::hash_map::Entry;
use std::fs::File;
use std::hash::Hash; use std::hash::Hash;
use std::io::{Read, Seek, Write}; use std::io::{Read, Seek, Write};
use std::num::NonZeroUsize; use std::num::NonZeroUsize;
@ -2250,25 +2251,34 @@ fn encode_metadata_impl(tcx: TyCtxt<'_>, path: &Path) {
// culminating in the `CrateRoot` which points to all of it. // culminating in the `CrateRoot` which points to all of it.
let root = ecx.encode_crate_root(); let root = ecx.encode_crate_root();
ecx.opaque.flush(); // Make sure we report any errors from writing to the file.
// If we forget this, compilation can succeed with an incomplete rmeta file,
// causing an ICE when the rmeta file is read by another compilation.
if let Err((path, err)) = ecx.opaque.finish() {
tcx.sess.emit_err(FailWriteFile { path: &path, err });
}
let mut file = ecx.opaque.file(); let file = ecx.opaque.file();
if let Err(err) = encode_root_position(file, root.position.get()) {
tcx.sess.emit_err(FailWriteFile { path: ecx.opaque.path(), err });
}
// Record metadata size for self-profiling
tcx.prof.artifact_size("crate_metadata", "crate_metadata", file.metadata().unwrap().len());
}
fn encode_root_position(mut file: &File, pos: usize) -> Result<(), std::io::Error> {
// We will return to this position after writing the root position. // We will return to this position after writing the root position.
let pos_before_seek = file.stream_position().unwrap(); let pos_before_seek = file.stream_position().unwrap();
// Encode the root position. // Encode the root position.
let header = METADATA_HEADER.len(); let header = METADATA_HEADER.len();
file.seek(std::io::SeekFrom::Start(header as u64)) file.seek(std::io::SeekFrom::Start(header as u64))?;
.unwrap_or_else(|err| tcx.sess.emit_fatal(FailSeekFile { err })); file.write_all(&[(pos >> 24) as u8, (pos >> 16) as u8, (pos >> 8) as u8, (pos >> 0) as u8])?;
let pos = root.position.get();
file.write_all(&[(pos >> 24) as u8, (pos >> 16) as u8, (pos >> 8) as u8, (pos >> 0) as u8])
.unwrap_or_else(|err| tcx.sess.emit_fatal(FailWriteFile { err }));
// Return to the position where we are before writing the root position. // Return to the position where we are before writing the root position.
file.seek(std::io::SeekFrom::Start(pos_before_seek)).unwrap(); file.seek(std::io::SeekFrom::Start(pos_before_seek))?;
Ok(())
// Record metadata size for self-profiling
tcx.prof.artifact_size("crate_metadata", "crate_metadata", file.metadata().unwrap().len());
} }
pub fn provide(providers: &mut Providers) { pub fn provide(providers: &mut Providers) {

View File

@ -25,7 +25,6 @@ use rustc_span::source_map::{SourceMap, StableSourceFileId};
use rustc_span::{BytePos, ExpnData, ExpnHash, Pos, RelativeBytePos, SourceFile, Span}; use rustc_span::{BytePos, ExpnData, ExpnHash, Pos, RelativeBytePos, SourceFile, Span};
use rustc_span::{CachingSourceMapView, Symbol}; use rustc_span::{CachingSourceMapView, Symbol};
use std::collections::hash_map::Entry; use std::collections::hash_map::Entry;
use std::io;
use std::mem; use std::mem;
const TAG_FILE_FOOTER: u128 = 0xC0FFEE_C0FFEE_C0FFEE_C0FFEE_C0FFEE; const TAG_FILE_FOOTER: u128 = 0xC0FFEE_C0FFEE_C0FFEE_C0FFEE_C0FFEE;
@ -862,7 +861,7 @@ impl<'a, 'tcx> CacheEncoder<'a, 'tcx> {
} }
#[inline] #[inline]
fn finish(self) -> Result<usize, io::Error> { fn finish(mut self) -> FileEncodeResult {
self.encoder.finish() self.encoder.finish()
} }
} }

View File

@ -597,6 +597,10 @@ impl<'tcx> GlobalCtxt<'tcx> {
let icx = tls::ImplicitCtxt::new(self); let icx = tls::ImplicitCtxt::new(self);
tls::enter_context(&icx, || f(icx.tcx)) tls::enter_context(&icx, || f(icx.tcx))
} }
pub fn finish(&self) -> FileEncodeResult {
self.dep_graph.finish_encoding(&self.sess.prof)
}
} }
impl<'tcx> TyCtxt<'tcx> { impl<'tcx> TyCtxt<'tcx> {

View File

@ -982,7 +982,7 @@ impl<D: Deps> DepGraph<D> {
} }
} }
pub fn encode(&self, profiler: &SelfProfilerRef) -> FileEncodeResult { pub fn finish_encoding(&self, profiler: &SelfProfilerRef) -> FileEncodeResult {
if let Some(data) = &self.data { if let Some(data) = &self.data {
data.current.encoder.steal().finish(profiler) data.current.encoder.steal().finish(profiler)
} else { } else {

View File

@ -5,12 +5,13 @@ use std::io::{self, Write};
use std::marker::PhantomData; use std::marker::PhantomData;
use std::ops::Range; use std::ops::Range;
use std::path::Path; use std::path::Path;
use std::path::PathBuf;
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// Encoder // Encoder
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
pub type FileEncodeResult = Result<usize, io::Error>; pub type FileEncodeResult = Result<usize, (PathBuf, io::Error)>;
/// The size of the buffer in `FileEncoder`. /// The size of the buffer in `FileEncoder`.
const BUF_SIZE: usize = 8192; const BUF_SIZE: usize = 8192;
@ -34,6 +35,9 @@ pub struct FileEncoder {
// This is used to implement delayed error handling, as described in the // This is used to implement delayed error handling, as described in the
// comment on `trait Encoder`. // comment on `trait Encoder`.
res: Result<(), io::Error>, res: Result<(), io::Error>,
path: PathBuf,
#[cfg(debug_assertions)]
finished: bool,
} }
impl FileEncoder { impl FileEncoder {
@ -41,14 +45,18 @@ impl FileEncoder {
// File::create opens the file for writing only. When -Zmeta-stats is enabled, the metadata // File::create opens the file for writing only. When -Zmeta-stats is enabled, the metadata
// encoder rewinds the file to inspect what was written. So we need to always open the file // encoder rewinds the file to inspect what was written. So we need to always open the file
// for reading and writing. // for reading and writing.
let file = File::options().read(true).write(true).create(true).truncate(true).open(path)?; let file =
File::options().read(true).write(true).create(true).truncate(true).open(&path)?;
Ok(FileEncoder { Ok(FileEncoder {
buf: vec![0u8; BUF_SIZE].into_boxed_slice().try_into().unwrap(), buf: vec![0u8; BUF_SIZE].into_boxed_slice().try_into().unwrap(),
path: path.as_ref().into(),
buffered: 0, buffered: 0,
flushed: 0, flushed: 0,
file, file,
res: Ok(()), res: Ok(()),
#[cfg(debug_assertions)]
finished: false,
}) })
} }
@ -63,6 +71,10 @@ impl FileEncoder {
#[cold] #[cold]
#[inline(never)] #[inline(never)]
pub fn flush(&mut self) { pub fn flush(&mut self) {
#[cfg(debug_assertions)]
{
self.finished = false;
}
if self.res.is_ok() { if self.res.is_ok() {
self.res = self.file.write_all(&self.buf[..self.buffered]); self.res = self.file.write_all(&self.buf[..self.buffered]);
} }
@ -74,6 +86,10 @@ impl FileEncoder {
&self.file &self.file
} }
pub fn path(&self) -> &Path {
&self.path
}
#[inline] #[inline]
fn buffer_empty(&mut self) -> &mut [u8] { fn buffer_empty(&mut self) -> &mut [u8] {
// SAFETY: self.buffered is inbounds as an invariant of the type // SAFETY: self.buffered is inbounds as an invariant of the type
@ -97,6 +113,10 @@ impl FileEncoder {
#[inline] #[inline]
fn write_all(&mut self, buf: &[u8]) { fn write_all(&mut self, buf: &[u8]) {
#[cfg(debug_assertions)]
{
self.finished = false;
}
if let Some(dest) = self.buffer_empty().get_mut(..buf.len()) { if let Some(dest) = self.buffer_empty().get_mut(..buf.len()) {
dest.copy_from_slice(buf); dest.copy_from_slice(buf);
self.buffered += buf.len(); self.buffered += buf.len();
@ -121,6 +141,10 @@ impl FileEncoder {
/// with one instruction, so while this does in some sense do wasted work, we come out ahead. /// with one instruction, so while this does in some sense do wasted work, we come out ahead.
#[inline] #[inline]
pub fn write_with<const N: usize>(&mut self, visitor: impl FnOnce(&mut [u8; N]) -> usize) { pub fn write_with<const N: usize>(&mut self, visitor: impl FnOnce(&mut [u8; N]) -> usize) {
#[cfg(debug_assertions)]
{
self.finished = false;
}
let flush_threshold = const { BUF_SIZE.checked_sub(N).unwrap() }; let flush_threshold = const { BUF_SIZE.checked_sub(N).unwrap() };
if std::intrinsics::unlikely(self.buffered > flush_threshold) { if std::intrinsics::unlikely(self.buffered > flush_threshold) {
self.flush(); self.flush();
@ -152,20 +176,25 @@ impl FileEncoder {
}) })
} }
pub fn finish(mut self) -> Result<usize, io::Error> { pub fn finish(&mut self) -> FileEncodeResult {
self.flush(); self.flush();
#[cfg(debug_assertions)]
{
self.finished = true;
}
match std::mem::replace(&mut self.res, Ok(())) { match std::mem::replace(&mut self.res, Ok(())) {
Ok(()) => Ok(self.position()), Ok(()) => Ok(self.position()),
Err(e) => Err(e), Err(e) => Err((self.path.clone(), e)),
} }
} }
} }
#[cfg(debug_assertions)]
impl Drop for FileEncoder { impl Drop for FileEncoder {
fn drop(&mut self) { fn drop(&mut self) {
// Likely to be a no-op, because `finish` should have been called and if !std::thread::panicking() {
// it also flushes. But do it just in case. assert!(self.finished);
self.flush(); }
} }
} }

View File

@ -325,7 +325,7 @@ pub(crate) fn run(
// Save output to provided path // Save output to provided path
let mut encoder = FileEncoder::new(options.output_path).map_err(|e| e.to_string())?; let mut encoder = FileEncoder::new(options.output_path).map_err(|e| e.to_string())?;
calls.encode(&mut encoder); calls.encode(&mut encoder);
encoder.finish().map_err(|e| e.to_string())?; encoder.finish().map_err(|(_path, e)| e.to_string())?;
Ok(()) Ok(())
}; };