1
Fork 0

incr: cache dwarf objects in work products

Cache DWARF objects alongside object files in work products when those
exist so that DWARF object files are available for thorin in packed mode
in incremental scenarios.

Signed-off-by: David Wood <david.wood@huawei.com>
This commit is contained in:
David Wood 2022-07-04 14:38:42 +01:00
parent 5b8cf49c51
commit 8371a036ea
7 changed files with 128 additions and 66 deletions

View file

@ -66,7 +66,11 @@ fn emit_module(
let work_product = if backend_config.disable_incr_cache { let work_product = if backend_config.disable_incr_cache {
None None
} else { } else {
rustc_incremental::copy_cgu_workproduct_to_incr_comp_cache_dir(tcx.sess, &name, &tmp_file) rustc_incremental::copy_cgu_workproduct_to_incr_comp_cache_dir(
tcx.sess,
&name,
&[("o", &tmp_file)],
)
}; };
ModuleCodegenResult( ModuleCodegenResult(
@ -82,7 +86,10 @@ fn reuse_workproduct_for_cgu(
) -> CompiledModule { ) -> CompiledModule {
let work_product = cgu.previous_work_product(tcx); let work_product = cgu.previous_work_product(tcx);
let obj_out = tcx.output_filenames(()).temp_path(OutputType::Object, Some(cgu.name().as_str())); let obj_out = tcx.output_filenames(()).temp_path(OutputType::Object, Some(cgu.name().as_str()));
let source_file = rustc_incremental::in_incr_comp_dir_sess(&tcx.sess, &work_product.saved_file); let source_file = rustc_incremental::in_incr_comp_dir_sess(
&tcx.sess,
&work_product.saved_files.get("o").expect("no saved object file in work product"),
);
if let Err(err) = rustc_fs_util::link_or_copy(&source_file, &obj_out) { if let Err(err) = rustc_fs_util::link_or_copy(&source_file, &obj_out) {
tcx.sess.err(&format!( tcx.sess.err(&format!(
"unable to copy {} to {}: {}", "unable to copy {} to {}: {}",

View file

@ -494,14 +494,20 @@ fn copy_all_cgu_workproducts_to_incr_comp_cache_dir(
let _timer = sess.timer("copy_all_cgu_workproducts_to_incr_comp_cache_dir"); let _timer = sess.timer("copy_all_cgu_workproducts_to_incr_comp_cache_dir");
for module in compiled_modules.modules.iter().filter(|m| m.kind == ModuleKind::Regular) { for module in compiled_modules.modules.iter().filter(|m| m.kind == ModuleKind::Regular) {
if let Some(path) = &module.object { let mut files = Vec::new();
if let Some(object_file_path) = &module.object {
files.push(("o", object_file_path.as_path()));
}
if let Some(dwarf_object_file_path) = &module.dwarf_object {
files.push(("dwo", dwarf_object_file_path.as_path()));
}
if let Some((id, product)) = if let Some((id, product)) =
copy_cgu_workproduct_to_incr_comp_cache_dir(sess, &module.name, path) copy_cgu_workproduct_to_incr_comp_cache_dir(sess, &module.name, files.as_slice())
{ {
work_products.insert(id, product); work_products.insert(id, product);
} }
} }
}
work_products work_products
} }
@ -856,29 +862,50 @@ fn execute_copy_from_cache_work_item<B: ExtraBackendMethods>(
assert!(module_config.emit_obj != EmitObj::None); assert!(module_config.emit_obj != EmitObj::None);
let incr_comp_session_dir = cgcx.incr_comp_session_dir.as_ref().unwrap(); let incr_comp_session_dir = cgcx.incr_comp_session_dir.as_ref().unwrap();
let obj_out = cgcx.output_filenames.temp_path(OutputType::Object, Some(&module.name));
let source_file = in_incr_comp_dir(&incr_comp_session_dir, &module.source.saved_file); let load_from_incr_comp_dir = |output_path: PathBuf, saved_path: &str| {
let source_file = in_incr_comp_dir(&incr_comp_session_dir, saved_path);
debug!( debug!(
"copying pre-existing module `{}` from {:?} to {}", "copying pre-existing module `{}` from {:?} to {}",
module.name, module.name,
source_file, source_file,
obj_out.display() output_path.display()
); );
if let Err(err) = link_or_copy(&source_file, &obj_out) { match link_or_copy(&source_file, &output_path) {
Ok(_) => Some(output_path),
Err(err) => {
let diag_handler = cgcx.create_diag_handler(); let diag_handler = cgcx.create_diag_handler();
diag_handler.err(&format!( diag_handler.err(&format!(
"unable to copy {} to {}: {}", "unable to copy {} to {}: {}",
source_file.display(), source_file.display(),
obj_out.display(), output_path.display(),
err err
)); ));
None
} }
}
};
let object = load_from_incr_comp_dir(
cgcx.output_filenames.temp_path(OutputType::Object, Some(&module.name)),
&module.source.saved_files.get("o").expect("no saved object file in work product"),
);
let dwarf_object =
module.source.saved_files.get("dwo").as_ref().and_then(|saved_dwarf_object_file| {
let dwarf_obj_out = cgcx
.output_filenames
.split_dwarf_path(cgcx.split_debuginfo, cgcx.split_dwarf_kind, Some(&module.name))
.expect(
"saved dwarf object in work product but `split_dwarf_path` returned `None`",
);
load_from_incr_comp_dir(dwarf_obj_out, &saved_dwarf_object_file)
});
WorkItemResult::Compiled(CompiledModule { WorkItemResult::Compiled(CompiledModule {
name: module.name, name: module.name,
kind: ModuleKind::Regular, kind: ModuleKind::Regular,
object: Some(obj_out), object,
dwarf_object: None, dwarf_object,
bytecode: None, bytecode: None,
}) })
} }

View file

@ -161,19 +161,13 @@ pub fn load_dep_graph(sess: &Session) -> DepGraphFuture {
Decodable::decode(&mut work_product_decoder); Decodable::decode(&mut work_product_decoder);
for swp in work_products { for swp in work_products {
let mut all_files_exist = true; let all_files_exist = swp.work_product.saved_files.iter().all(|(_, path)| {
let path = in_incr_comp_dir_sess(sess, &swp.work_product.saved_file); let exists = in_incr_comp_dir_sess(sess, path).exists();
if !path.exists() { if !exists && sess.opts.debugging_opts.incremental_info {
all_files_exist = false; eprintln!("incremental: could not find file for work product: {path}",);
if sess.opts.debugging_opts.incremental_info {
eprintln!(
"incremental: could not find file for work \
product: {}",
path.display()
);
}
} }
exists
});
if all_files_exist { if all_files_exist {
debug!("reconcile_work_products: all files for {:?} exist", swp); debug!("reconcile_work_products: all files for {:?} exist", swp);

View file

@ -108,16 +108,17 @@ pub fn save_work_product_index(
for (id, wp) in previous_work_products.iter() { for (id, wp) in previous_work_products.iter() {
if !new_work_products.contains_key(id) { if !new_work_products.contains_key(id) {
work_product::delete_workproduct_files(sess, wp); work_product::delete_workproduct_files(sess, wp);
debug_assert!(!in_incr_comp_dir_sess(sess, &wp.saved_file).exists()); debug_assert!(
!wp.saved_files.iter().all(|(_, path)| in_incr_comp_dir_sess(sess, path).exists())
);
} }
} }
// Check that we did not delete one of the current work-products: // Check that we did not delete one of the current work-products:
debug_assert!({ debug_assert!({
new_work_products new_work_products.iter().all(|(_, wp)| {
.iter() wp.saved_files.iter().all(|(_, path)| in_incr_comp_dir_sess(sess, path).exists())
.map(|(_, wp)| in_incr_comp_dir_sess(sess, &wp.saved_file)) })
.all(|path| path.exists())
}); });
} }

View file

@ -3,6 +3,7 @@
//! [work products]: WorkProduct //! [work products]: WorkProduct
use crate::persist::fs::*; use crate::persist::fs::*;
use rustc_data_structures::stable_map::FxHashMap;
use rustc_fs_util::link_or_copy; use rustc_fs_util::link_or_copy;
use rustc_middle::dep_graph::{WorkProduct, WorkProductId}; use rustc_middle::dep_graph::{WorkProduct, WorkProductId};
use rustc_session::Session; use rustc_session::Session;
@ -13,15 +14,19 @@ use std::path::Path;
pub fn copy_cgu_workproduct_to_incr_comp_cache_dir( pub fn copy_cgu_workproduct_to_incr_comp_cache_dir(
sess: &Session, sess: &Session,
cgu_name: &str, cgu_name: &str,
path: &Path, files: &[(&'static str, &Path)],
) -> Option<(WorkProductId, WorkProduct)> { ) -> Option<(WorkProductId, WorkProduct)> {
debug!("copy_cgu_workproduct_to_incr_comp_cache_dir({:?},{:?})", cgu_name, path); debug!(?cgu_name, ?files);
sess.opts.incremental.as_ref()?; sess.opts.incremental.as_ref()?;
let file_name = format!("{}.o", cgu_name); let mut saved_files = FxHashMap::default();
for (ext, path) in files {
let file_name = format!("{cgu_name}.{ext}");
let path_in_incr_dir = in_incr_comp_dir_sess(sess, &file_name); let path_in_incr_dir = in_incr_comp_dir_sess(sess, &file_name);
let saved_file = match link_or_copy(path, &path_in_incr_dir) { match link_or_copy(path, &path_in_incr_dir) {
Ok(_) => file_name, Ok(_) => {
let _ = saved_files.insert(ext.to_string(), file_name);
}
Err(err) => { Err(err) => {
sess.warn(&format!( sess.warn(&format!(
"error copying object file `{}` to incremental directory as `{}`: {}", "error copying object file `{}` to incremental directory as `{}`: {}",
@ -29,22 +34,21 @@ pub fn copy_cgu_workproduct_to_incr_comp_cache_dir(
path_in_incr_dir.display(), path_in_incr_dir.display(),
err err
)); ));
return None;
} }
}; }
}
let work_product = WorkProduct { cgu_name: cgu_name.to_string(), saved_file };
let work_product = WorkProduct { cgu_name: cgu_name.to_string(), saved_files };
debug!(?work_product);
let work_product_id = WorkProductId::from_cgu_name(cgu_name); let work_product_id = WorkProductId::from_cgu_name(cgu_name);
Some((work_product_id, work_product)) Some((work_product_id, work_product))
} }
/// Removes files for a given work product. /// Removes files for a given work product.
pub fn delete_workproduct_files(sess: &Session, work_product: &WorkProduct) { pub fn delete_workproduct_files(sess: &Session, work_product: &WorkProduct) {
let path = in_incr_comp_dir_sess(sess, &work_product.saved_file); for (_, path) in &work_product.saved_files {
match std_fs::remove_file(&path) { let path = in_incr_comp_dir_sess(sess, path);
Ok(()) => {} if let Err(err) = std_fs::remove_file(&path) {
Err(err) => {
sess.warn(&format!( sess.warn(&format!(
"file-system error deleting outdated file `{}`: {}", "file-system error deleting outdated file `{}`: {}",
path.display(), path.display(),

View file

@ -886,8 +886,12 @@ impl<K: DepKind> DepGraph<K> {
#[derive(Clone, Debug, Encodable, Decodable)] #[derive(Clone, Debug, Encodable, Decodable)]
pub struct WorkProduct { pub struct WorkProduct {
pub cgu_name: String, pub cgu_name: String,
/// Saved file associated with this CGU. /// Saved files associated with this CGU. In each key/value pair, the value is the path to the
pub saved_file: String, /// saved file and the key is some identifier for the type of file being saved.
///
/// By convention, file extensions are currently used as identifiers, i.e. the key "o" maps to
/// the object file's path, and "dwo" to the dwarf object file's path.
pub saved_files: FxHashMap<String, String>,
} }
// Index type for `DepNodeData`'s edges. // Index type for `DepNodeData`'s edges.

View file

@ -0,0 +1,25 @@
// Check that compiling with packed Split DWARF twice succeeds. This should confirm that DWARF
// objects are cached as work products and available to the incremental compilation for `thorin` to
// pack into a DWARF package.
// ignore-tidy-linelength
// only-x86_64-unknown-linux-gnu
// revisions:rpass1 rpass2
// [rpass1]compile-flags: -g -Zquery-dep-graph -Zunstable-options -Csplit-debuginfo=packed -Zsplit-dwarf-kind=split
// [rpass2]compile-flags: -g -Zquery-dep-graph -Zunstable-options -Csplit-debuginfo=packed -Zsplit-dwarf-kind=split
#![feature(rustc_attrs)]
// For `rpass2`, nothing has changed so everything should re-used.
#![rustc_partition_reused(module = "split_debuginfo_cached", cfg = "rpass2")]
#![rustc_partition_reused(module = "split_debuginfo_cached-another_module", cfg = "rpass2")]
mod another_module {
pub fn foo() -> &'static str {
"hello world"
}
}
pub fn main() {
println!("{}", another_module::foo());
}