Auto merge of #139453 - compiler-errors:incr, r=jieyouxu
Prepend temp files with per-invocation random string to avoid temp filename conflicts https://github.com/rust-lang/rust/issues/139407 uncovered a very subtle unsoundness with incremental codegen, failing compilation sessions (due to assembler errors), and the "prefer hard linking over copying files" strategy we use in the compiler for file management. Specifically, imagine we're building a single file 3 times, all with `-Csave-temps -Cincremental=...`. Let's call the object file we're building for the codegen unit for `main` "`XXX.o`" just for clarity since it's probably some gigantic hash name: ``` #[inline(never)] #[cfg(any(rpass1, rpass3))] fn a() -> i32 { 0 } #[cfg(any(cfail2))] fn a() -> i32 { 1 } fn main() { evil::evil(); assert_eq!(a(), 0); } mod evil { #[cfg(any(rpass1, rpass3))] pub fn evil() { unsafe { std::arch::asm!("/* */"); } } #[cfg(any(cfail2))] pub fn evil() { unsafe { std::arch::asm!("missing"); } } } ``` Session 1 (`rpass1`): * Type-check, borrow-check, etc. * Serialize the dep graph to the incremental working directory `.../s-...-working/`. * Codegen object file to a temp file `XXX.rcgu.o` which is spit out in the cwd. * Hard-link[^1] `XXX.rcgu.o` to the incremental working directory `.../s-...-working/XXX.o`. * Save-temps option means we don't delete `XXX.rgcu.o`. * Link the binary and stuff. * Finalize[^2] the working incremental session by renaming `.../s-...-working` to ` s-...-asjkdhsjakd` (some other finalized incr comp session dir name). Session 2 (`cfail2`): * Load artifacts from the previous *finalized* incremental session, namely the dep graph. * Type-check, borrow-check, etc. since the file has changed, so most dep graph nodes are red. * Serialize the dep graph to the incremental working directory `.../s-...-working/`. * Codegen object file to a temp file `XXX.rcgu.o`. **HERE IS THE PROBLEM**: The hard-link is still set up to point to the inode from `XXX.o` from the first session, so this also modifies the `XXX.o` in the previous finalized session directory. * Codegen emits an error b/c `missing` is not an instruction, so we abort before finalizing the incremental session. Specifically, this means that the *previous* session is the last finalized session. Session 3 (`rpass3`): * Load artifacts from the previous *finalized* incremental session, namely the dep graph. NOTE that this is from session 1. * All the dep graph nodes are green since we are basically replaying session 1. * codegen object file `XXX.o`, which is detected as *reused* from session 1 since dep nodes were green. That means we **reuse** `XXX.o` which had been dirtied from session 2. * Link the binary and stuff. This results in a binary which reuses some of the build artifacts from session 2, but thinks it's from session 1. At this point, I hope it's clear to see that the incremental results from session 1 were dirtied from session 2, but we reuse them as if session 1 was the previous (finalized) incremental session we ran. This is at best really buggy, and at worst **unsound**. This isn't limited to `-C save-temps`, since there are other combinations of flags that may keep around temporary files (hard linked) in the working directory (like `-C debuginfo=1 -C split-debuginfo=unpacked` on darwin, for example). --- This PR implements a fix which is to prepend temp filenames with a random string that is generated per invocation of rustc. This string is not *deterministic*, but temporary files are transient anyways, so I don't believe this is a problem. That means that temp files are now something like... `{crate-name}.{cgu}.{invocation_temp}.rcgu.o`, where `{invocation_temp}` is the new temporary string we generate per invocation of rustc. Fixes https://github.com/rust-lang/rust/issues/139407 [^1]:175dcc7773/compiler/rustc_fs_util/src/lib.rs (L60)
[^2]:175dcc7773/compiler/rustc_incremental/src/persist/fs.rs (L1-L40)
This commit is contained in:
commit
e1b06f7730
20 changed files with 285 additions and 97 deletions
|
@ -4444,6 +4444,7 @@ dependencies = [
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"getopts",
|
"getopts",
|
||||||
"libc",
|
"libc",
|
||||||
|
"rand 0.9.0",
|
||||||
"rustc_abi",
|
"rustc_abi",
|
||||||
"rustc_ast",
|
"rustc_ast",
|
||||||
"rustc_data_structures",
|
"rustc_data_structures",
|
||||||
|
|
|
@ -169,8 +169,11 @@ fn produce_final_output_artifacts(
|
||||||
if codegen_results.modules.len() == 1 {
|
if codegen_results.modules.len() == 1 {
|
||||||
// 1) Only one codegen unit. In this case it's no difficulty
|
// 1) Only one codegen unit. In this case it's no difficulty
|
||||||
// to copy `foo.0.x` to `foo.x`.
|
// to copy `foo.0.x` to `foo.x`.
|
||||||
let module_name = Some(&codegen_results.modules[0].name[..]);
|
let path = crate_output.temp_path_for_cgu(
|
||||||
let path = crate_output.temp_path(output_type, module_name);
|
output_type,
|
||||||
|
&codegen_results.modules[0].name,
|
||||||
|
sess.invocation_temp.as_deref(),
|
||||||
|
);
|
||||||
let output = crate_output.path(output_type);
|
let output = crate_output.path(output_type);
|
||||||
if !output_type.is_text_output() && output.is_tty() {
|
if !output_type.is_text_output() && output.is_tty() {
|
||||||
sess.dcx()
|
sess.dcx()
|
||||||
|
@ -183,22 +186,16 @@ fn produce_final_output_artifacts(
|
||||||
ensure_removed(sess.dcx(), &path);
|
ensure_removed(sess.dcx(), &path);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let extension = crate_output
|
|
||||||
.temp_path(output_type, None)
|
|
||||||
.extension()
|
|
||||||
.unwrap()
|
|
||||||
.to_str()
|
|
||||||
.unwrap()
|
|
||||||
.to_owned();
|
|
||||||
|
|
||||||
if crate_output.outputs.contains_explicit_name(&output_type) {
|
if crate_output.outputs.contains_explicit_name(&output_type) {
|
||||||
// 2) Multiple codegen units, with `--emit foo=some_name`. We have
|
// 2) Multiple codegen units, with `--emit foo=some_name`. We have
|
||||||
// no good solution for this case, so warn the user.
|
// no good solution for this case, so warn the user.
|
||||||
sess.dcx().emit_warn(ssa_errors::IgnoringEmitPath { extension });
|
sess.dcx()
|
||||||
|
.emit_warn(ssa_errors::IgnoringEmitPath { extension: output_type.extension() });
|
||||||
} else if crate_output.single_output_file.is_some() {
|
} else if crate_output.single_output_file.is_some() {
|
||||||
// 3) Multiple codegen units, with `-o some_name`. We have
|
// 3) Multiple codegen units, with `-o some_name`. We have
|
||||||
// no good solution for this case, so warn the user.
|
// no good solution for this case, so warn the user.
|
||||||
sess.dcx().emit_warn(ssa_errors::IgnoringOutput { extension });
|
sess.dcx()
|
||||||
|
.emit_warn(ssa_errors::IgnoringOutput { extension: output_type.extension() });
|
||||||
} else {
|
} else {
|
||||||
// 4) Multiple codegen units, but no explicit name. We
|
// 4) Multiple codegen units, but no explicit name. We
|
||||||
// just leave the `foo.0.x` files in place.
|
// just leave the `foo.0.x` files in place.
|
||||||
|
@ -351,6 +348,7 @@ fn make_module(sess: &Session, name: String) -> UnwindModule<ObjectModule> {
|
||||||
|
|
||||||
fn emit_cgu(
|
fn emit_cgu(
|
||||||
output_filenames: &OutputFilenames,
|
output_filenames: &OutputFilenames,
|
||||||
|
invocation_temp: Option<&str>,
|
||||||
prof: &SelfProfilerRef,
|
prof: &SelfProfilerRef,
|
||||||
name: String,
|
name: String,
|
||||||
module: UnwindModule<ObjectModule>,
|
module: UnwindModule<ObjectModule>,
|
||||||
|
@ -366,6 +364,7 @@ fn emit_cgu(
|
||||||
|
|
||||||
let module_regular = emit_module(
|
let module_regular = emit_module(
|
||||||
output_filenames,
|
output_filenames,
|
||||||
|
invocation_temp,
|
||||||
prof,
|
prof,
|
||||||
product.object,
|
product.object,
|
||||||
ModuleKind::Regular,
|
ModuleKind::Regular,
|
||||||
|
@ -391,6 +390,7 @@ fn emit_cgu(
|
||||||
|
|
||||||
fn emit_module(
|
fn emit_module(
|
||||||
output_filenames: &OutputFilenames,
|
output_filenames: &OutputFilenames,
|
||||||
|
invocation_temp: Option<&str>,
|
||||||
prof: &SelfProfilerRef,
|
prof: &SelfProfilerRef,
|
||||||
mut object: cranelift_object::object::write::Object<'_>,
|
mut object: cranelift_object::object::write::Object<'_>,
|
||||||
kind: ModuleKind,
|
kind: ModuleKind,
|
||||||
|
@ -409,7 +409,7 @@ fn emit_module(
|
||||||
object.set_section_data(comment_section, producer, 1);
|
object.set_section_data(comment_section, producer, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
let tmp_file = output_filenames.temp_path(OutputType::Object, Some(&name));
|
let tmp_file = output_filenames.temp_path_for_cgu(OutputType::Object, &name, invocation_temp);
|
||||||
let file = match File::create(&tmp_file) {
|
let file = match File::create(&tmp_file) {
|
||||||
Ok(file) => file,
|
Ok(file) => file,
|
||||||
Err(err) => return Err(format!("error creating object file: {}", err)),
|
Err(err) => return Err(format!("error creating object file: {}", err)),
|
||||||
|
@ -449,8 +449,11 @@ fn reuse_workproduct_for_cgu(
|
||||||
cgu: &CodegenUnit<'_>,
|
cgu: &CodegenUnit<'_>,
|
||||||
) -> Result<ModuleCodegenResult, String> {
|
) -> Result<ModuleCodegenResult, String> {
|
||||||
let work_product = cgu.previous_work_product(tcx);
|
let work_product = cgu.previous_work_product(tcx);
|
||||||
let obj_out_regular =
|
let obj_out_regular = tcx.output_filenames(()).temp_path_for_cgu(
|
||||||
tcx.output_filenames(()).temp_path(OutputType::Object, Some(cgu.name().as_str()));
|
OutputType::Object,
|
||||||
|
cgu.name().as_str(),
|
||||||
|
tcx.sess.invocation_temp.as_deref(),
|
||||||
|
);
|
||||||
let source_file_regular = rustc_incremental::in_incr_comp_dir_sess(
|
let source_file_regular = rustc_incremental::in_incr_comp_dir_sess(
|
||||||
&tcx.sess,
|
&tcx.sess,
|
||||||
&work_product.saved_files.get("o").expect("no saved object file in work product"),
|
&work_product.saved_files.get("o").expect("no saved object file in work product"),
|
||||||
|
@ -595,13 +598,19 @@ fn module_codegen(
|
||||||
|
|
||||||
let global_asm_object_file =
|
let global_asm_object_file =
|
||||||
profiler.generic_activity_with_arg("compile assembly", &*cgu_name).run(|| {
|
profiler.generic_activity_with_arg("compile assembly", &*cgu_name).run(|| {
|
||||||
crate::global_asm::compile_global_asm(&global_asm_config, &cgu_name, &cx.global_asm)
|
crate::global_asm::compile_global_asm(
|
||||||
|
&global_asm_config,
|
||||||
|
&cgu_name,
|
||||||
|
&cx.global_asm,
|
||||||
|
cx.invocation_temp.as_deref(),
|
||||||
|
)
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let codegen_result =
|
let codegen_result =
|
||||||
profiler.generic_activity_with_arg("write object file", &*cgu_name).run(|| {
|
profiler.generic_activity_with_arg("write object file", &*cgu_name).run(|| {
|
||||||
emit_cgu(
|
emit_cgu(
|
||||||
&global_asm_config.output_filenames,
|
&global_asm_config.output_filenames,
|
||||||
|
cx.invocation_temp.as_deref(),
|
||||||
&profiler,
|
&profiler,
|
||||||
cgu_name,
|
cgu_name,
|
||||||
module,
|
module,
|
||||||
|
@ -626,8 +635,11 @@ fn emit_metadata_module(tcx: TyCtxt<'_>, metadata: &EncodedMetadata) -> Compiled
|
||||||
.as_str()
|
.as_str()
|
||||||
.to_string();
|
.to_string();
|
||||||
|
|
||||||
let tmp_file =
|
let tmp_file = tcx.output_filenames(()).temp_path_for_cgu(
|
||||||
tcx.output_filenames(()).temp_path(OutputType::Metadata, Some(&metadata_cgu_name));
|
OutputType::Metadata,
|
||||||
|
&metadata_cgu_name,
|
||||||
|
tcx.sess.invocation_temp.as_deref(),
|
||||||
|
);
|
||||||
|
|
||||||
let symbol_name = rustc_middle::middle::exported_symbols::metadata_symbol_name(tcx);
|
let symbol_name = rustc_middle::middle::exported_symbols::metadata_symbol_name(tcx);
|
||||||
let obj = create_compressed_metadata_file(tcx.sess, metadata, &symbol_name);
|
let obj = create_compressed_metadata_file(tcx.sess, metadata, &symbol_name);
|
||||||
|
@ -657,6 +669,7 @@ fn emit_allocator_module(tcx: TyCtxt<'_>) -> Option<CompiledModule> {
|
||||||
|
|
||||||
match emit_module(
|
match emit_module(
|
||||||
tcx.output_filenames(()),
|
tcx.output_filenames(()),
|
||||||
|
tcx.sess.invocation_temp.as_deref(),
|
||||||
&tcx.sess.prof,
|
&tcx.sess.prof,
|
||||||
product.object,
|
product.object,
|
||||||
ModuleKind::Allocator,
|
ModuleKind::Allocator,
|
||||||
|
|
|
@ -132,6 +132,7 @@ pub(crate) fn compile_global_asm(
|
||||||
config: &GlobalAsmConfig,
|
config: &GlobalAsmConfig,
|
||||||
cgu_name: &str,
|
cgu_name: &str,
|
||||||
global_asm: &str,
|
global_asm: &str,
|
||||||
|
invocation_temp: Option<&str>,
|
||||||
) -> Result<Option<PathBuf>, String> {
|
) -> Result<Option<PathBuf>, String> {
|
||||||
if global_asm.is_empty() {
|
if global_asm.is_empty() {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
|
@ -146,7 +147,7 @@ pub(crate) fn compile_global_asm(
|
||||||
global_asm.push('\n');
|
global_asm.push('\n');
|
||||||
|
|
||||||
let global_asm_object_file = add_file_stem_postfix(
|
let global_asm_object_file = add_file_stem_postfix(
|
||||||
config.output_filenames.temp_path(OutputType::Object, Some(cgu_name)),
|
config.output_filenames.temp_path_for_cgu(OutputType::Object, cgu_name, invocation_temp),
|
||||||
".asm",
|
".asm",
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -124,6 +124,7 @@ impl<F: Fn() -> String> Drop for PrintOnPanic<F> {
|
||||||
/// inside a single codegen unit with the exception of the Cranelift [`Module`](cranelift_module::Module).
|
/// inside a single codegen unit with the exception of the Cranelift [`Module`](cranelift_module::Module).
|
||||||
struct CodegenCx {
|
struct CodegenCx {
|
||||||
output_filenames: Arc<OutputFilenames>,
|
output_filenames: Arc<OutputFilenames>,
|
||||||
|
invocation_temp: Option<String>,
|
||||||
should_write_ir: bool,
|
should_write_ir: bool,
|
||||||
global_asm: String,
|
global_asm: String,
|
||||||
inline_asm_index: usize,
|
inline_asm_index: usize,
|
||||||
|
@ -142,6 +143,7 @@ impl CodegenCx {
|
||||||
};
|
};
|
||||||
CodegenCx {
|
CodegenCx {
|
||||||
output_filenames: tcx.output_filenames(()).clone(),
|
output_filenames: tcx.output_filenames(()).clone(),
|
||||||
|
invocation_temp: tcx.sess.invocation_temp.clone(),
|
||||||
should_write_ir: crate::pretty_clif::should_write_ir(tcx),
|
should_write_ir: crate::pretty_clif::should_write_ir(tcx),
|
||||||
global_asm: String::new(),
|
global_asm: String::new(),
|
||||||
inline_asm_index: 0,
|
inline_asm_index: 0,
|
||||||
|
|
|
@ -24,19 +24,23 @@ pub(crate) unsafe fn codegen(
|
||||||
{
|
{
|
||||||
let context = &module.module_llvm.context;
|
let context = &module.module_llvm.context;
|
||||||
|
|
||||||
let module_name = module.name.clone();
|
|
||||||
|
|
||||||
let should_combine_object_files = module.module_llvm.should_combine_object_files;
|
let should_combine_object_files = module.module_llvm.should_combine_object_files;
|
||||||
|
|
||||||
let module_name = Some(&module_name[..]);
|
|
||||||
|
|
||||||
// NOTE: Only generate object files with GIMPLE when this environment variable is set for
|
// NOTE: Only generate object files with GIMPLE when this environment variable is set for
|
||||||
// now because this requires a particular setup (same gcc/lto1/lto-wrapper commit as libgccjit).
|
// now because this requires a particular setup (same gcc/lto1/lto-wrapper commit as libgccjit).
|
||||||
// TODO(antoyo): remove this environment variable.
|
// TODO(antoyo): remove this environment variable.
|
||||||
let fat_lto = env::var("EMBED_LTO_BITCODE").as_deref() == Ok("1");
|
let fat_lto = env::var("EMBED_LTO_BITCODE").as_deref() == Ok("1");
|
||||||
|
|
||||||
let bc_out = cgcx.output_filenames.temp_path(OutputType::Bitcode, module_name);
|
let bc_out = cgcx.output_filenames.temp_path_for_cgu(
|
||||||
let obj_out = cgcx.output_filenames.temp_path(OutputType::Object, module_name);
|
OutputType::Bitcode,
|
||||||
|
&module.name,
|
||||||
|
cgcx.invocation_temp.as_deref(),
|
||||||
|
);
|
||||||
|
let obj_out = cgcx.output_filenames.temp_path_for_cgu(
|
||||||
|
OutputType::Object,
|
||||||
|
&module.name,
|
||||||
|
cgcx.invocation_temp.as_deref(),
|
||||||
|
);
|
||||||
|
|
||||||
if config.bitcode_needed() {
|
if config.bitcode_needed() {
|
||||||
if fat_lto {
|
if fat_lto {
|
||||||
|
@ -117,14 +121,22 @@ pub(crate) unsafe fn codegen(
|
||||||
}
|
}
|
||||||
|
|
||||||
if config.emit_ir {
|
if config.emit_ir {
|
||||||
let out = cgcx.output_filenames.temp_path(OutputType::LlvmAssembly, module_name);
|
let out = cgcx.output_filenames.temp_path_for_cgu(
|
||||||
|
OutputType::LlvmAssembly,
|
||||||
|
&module.name,
|
||||||
|
cgcx.invocation_temp.as_deref(),
|
||||||
|
);
|
||||||
std::fs::write(out, "").expect("write file");
|
std::fs::write(out, "").expect("write file");
|
||||||
}
|
}
|
||||||
|
|
||||||
if config.emit_asm {
|
if config.emit_asm {
|
||||||
let _timer =
|
let _timer =
|
||||||
cgcx.prof.generic_activity_with_arg("GCC_module_codegen_emit_asm", &*module.name);
|
cgcx.prof.generic_activity_with_arg("GCC_module_codegen_emit_asm", &*module.name);
|
||||||
let path = cgcx.output_filenames.temp_path(OutputType::Assembly, module_name);
|
let path = cgcx.output_filenames.temp_path_for_cgu(
|
||||||
|
OutputType::Assembly,
|
||||||
|
&module.name,
|
||||||
|
cgcx.invocation_temp.as_deref(),
|
||||||
|
);
|
||||||
context.compile_to_file(OutputKind::Assembler, path.to_str().expect("path to str"));
|
context.compile_to_file(OutputKind::Assembler, path.to_str().expect("path to str"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -238,6 +250,7 @@ pub(crate) unsafe fn codegen(
|
||||||
config.emit_asm,
|
config.emit_asm,
|
||||||
config.emit_ir,
|
config.emit_ir,
|
||||||
&cgcx.output_filenames,
|
&cgcx.output_filenames,
|
||||||
|
cgcx.invocation_temp.as_deref(),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -119,14 +119,18 @@ pub(crate) fn create_target_machine(tcx: TyCtxt<'_>, mod_name: &str) -> OwnedTar
|
||||||
tcx.output_filenames(()).split_dwarf_path(
|
tcx.output_filenames(()).split_dwarf_path(
|
||||||
tcx.sess.split_debuginfo(),
|
tcx.sess.split_debuginfo(),
|
||||||
tcx.sess.opts.unstable_opts.split_dwarf_kind,
|
tcx.sess.opts.unstable_opts.split_dwarf_kind,
|
||||||
Some(mod_name),
|
mod_name,
|
||||||
|
tcx.sess.invocation_temp.as_deref(),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
let output_obj_file =
|
let output_obj_file = Some(tcx.output_filenames(()).temp_path_for_cgu(
|
||||||
Some(tcx.output_filenames(()).temp_path(OutputType::Object, Some(mod_name)));
|
OutputType::Object,
|
||||||
|
mod_name,
|
||||||
|
tcx.sess.invocation_temp.as_deref(),
|
||||||
|
));
|
||||||
let config = TargetMachineFactoryConfig { split_dwarf_file, output_obj_file };
|
let config = TargetMachineFactoryConfig { split_dwarf_file, output_obj_file };
|
||||||
|
|
||||||
target_machine_factory(
|
target_machine_factory(
|
||||||
|
@ -330,8 +334,11 @@ pub(crate) fn save_temp_bitcode(
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let ext = format!("{name}.bc");
|
let ext = format!("{name}.bc");
|
||||||
let cgu = Some(&module.name[..]);
|
let path = cgcx.output_filenames.temp_path_ext_for_cgu(
|
||||||
let path = cgcx.output_filenames.temp_path_ext(&ext, cgu);
|
&ext,
|
||||||
|
&module.name,
|
||||||
|
cgcx.invocation_temp.as_deref(),
|
||||||
|
);
|
||||||
write_bitcode_to_file(module, &path)
|
write_bitcode_to_file(module, &path)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -694,11 +701,12 @@ pub(crate) unsafe fn optimize(
|
||||||
let llcx = &*module.module_llvm.llcx;
|
let llcx = &*module.module_llvm.llcx;
|
||||||
let _handlers = DiagnosticHandlers::new(cgcx, dcx, llcx, module, CodegenDiagnosticsStage::Opt);
|
let _handlers = DiagnosticHandlers::new(cgcx, dcx, llcx, module, CodegenDiagnosticsStage::Opt);
|
||||||
|
|
||||||
let module_name = module.name.clone();
|
|
||||||
let module_name = Some(&module_name[..]);
|
|
||||||
|
|
||||||
if config.emit_no_opt_bc {
|
if config.emit_no_opt_bc {
|
||||||
let out = cgcx.output_filenames.temp_path_ext("no-opt.bc", module_name);
|
let out = cgcx.output_filenames.temp_path_ext_for_cgu(
|
||||||
|
"no-opt.bc",
|
||||||
|
&module.name,
|
||||||
|
cgcx.invocation_temp.as_deref(),
|
||||||
|
);
|
||||||
write_bitcode_to_file(module, &out)
|
write_bitcode_to_file(module, &out)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -743,8 +751,11 @@ pub(crate) unsafe fn optimize(
|
||||||
if let Some(thin_lto_buffer) = thin_lto_buffer {
|
if let Some(thin_lto_buffer) = thin_lto_buffer {
|
||||||
let thin_lto_buffer = unsafe { ThinBuffer::from_raw_ptr(thin_lto_buffer) };
|
let thin_lto_buffer = unsafe { ThinBuffer::from_raw_ptr(thin_lto_buffer) };
|
||||||
module.thin_lto_buffer = Some(thin_lto_buffer.data().to_vec());
|
module.thin_lto_buffer = Some(thin_lto_buffer.data().to_vec());
|
||||||
let bc_summary_out =
|
let bc_summary_out = cgcx.output_filenames.temp_path_for_cgu(
|
||||||
cgcx.output_filenames.temp_path(OutputType::ThinLinkBitcode, module_name);
|
OutputType::ThinLinkBitcode,
|
||||||
|
&module.name,
|
||||||
|
cgcx.invocation_temp.as_deref(),
|
||||||
|
);
|
||||||
if config.emit_thin_lto_summary
|
if config.emit_thin_lto_summary
|
||||||
&& let Some(thin_link_bitcode_filename) = bc_summary_out.file_name()
|
&& let Some(thin_link_bitcode_filename) = bc_summary_out.file_name()
|
||||||
{
|
{
|
||||||
|
@ -801,8 +812,6 @@ pub(crate) unsafe fn codegen(
|
||||||
let llmod = module.module_llvm.llmod();
|
let llmod = module.module_llvm.llmod();
|
||||||
let llcx = &*module.module_llvm.llcx;
|
let llcx = &*module.module_llvm.llcx;
|
||||||
let tm = &*module.module_llvm.tm;
|
let tm = &*module.module_llvm.tm;
|
||||||
let module_name = module.name.clone();
|
|
||||||
let module_name = Some(&module_name[..]);
|
|
||||||
let _handlers =
|
let _handlers =
|
||||||
DiagnosticHandlers::new(cgcx, dcx, llcx, &module, CodegenDiagnosticsStage::Codegen);
|
DiagnosticHandlers::new(cgcx, dcx, llcx, &module, CodegenDiagnosticsStage::Codegen);
|
||||||
|
|
||||||
|
@ -814,8 +823,16 @@ pub(crate) unsafe fn codegen(
|
||||||
// copy it to the .o file, and delete the bitcode if it wasn't
|
// copy it to the .o file, and delete the bitcode if it wasn't
|
||||||
// otherwise requested.
|
// otherwise requested.
|
||||||
|
|
||||||
let bc_out = cgcx.output_filenames.temp_path(OutputType::Bitcode, module_name);
|
let bc_out = cgcx.output_filenames.temp_path_for_cgu(
|
||||||
let obj_out = cgcx.output_filenames.temp_path(OutputType::Object, module_name);
|
OutputType::Bitcode,
|
||||||
|
&module.name,
|
||||||
|
cgcx.invocation_temp.as_deref(),
|
||||||
|
);
|
||||||
|
let obj_out = cgcx.output_filenames.temp_path_for_cgu(
|
||||||
|
OutputType::Object,
|
||||||
|
&module.name,
|
||||||
|
cgcx.invocation_temp.as_deref(),
|
||||||
|
);
|
||||||
|
|
||||||
if config.bitcode_needed() {
|
if config.bitcode_needed() {
|
||||||
if config.emit_bc || config.emit_obj == EmitObj::Bitcode {
|
if config.emit_bc || config.emit_obj == EmitObj::Bitcode {
|
||||||
|
@ -857,7 +874,11 @@ pub(crate) unsafe fn codegen(
|
||||||
if config.emit_ir {
|
if config.emit_ir {
|
||||||
let _timer =
|
let _timer =
|
||||||
cgcx.prof.generic_activity_with_arg("LLVM_module_codegen_emit_ir", &*module.name);
|
cgcx.prof.generic_activity_with_arg("LLVM_module_codegen_emit_ir", &*module.name);
|
||||||
let out = cgcx.output_filenames.temp_path(OutputType::LlvmAssembly, module_name);
|
let out = cgcx.output_filenames.temp_path_for_cgu(
|
||||||
|
OutputType::LlvmAssembly,
|
||||||
|
&module.name,
|
||||||
|
cgcx.invocation_temp.as_deref(),
|
||||||
|
);
|
||||||
let out_c = path_to_c_string(&out);
|
let out_c = path_to_c_string(&out);
|
||||||
|
|
||||||
extern "C" fn demangle_callback(
|
extern "C" fn demangle_callback(
|
||||||
|
@ -899,7 +920,11 @@ pub(crate) unsafe fn codegen(
|
||||||
if config.emit_asm {
|
if config.emit_asm {
|
||||||
let _timer =
|
let _timer =
|
||||||
cgcx.prof.generic_activity_with_arg("LLVM_module_codegen_emit_asm", &*module.name);
|
cgcx.prof.generic_activity_with_arg("LLVM_module_codegen_emit_asm", &*module.name);
|
||||||
let path = cgcx.output_filenames.temp_path(OutputType::Assembly, module_name);
|
let path = cgcx.output_filenames.temp_path_for_cgu(
|
||||||
|
OutputType::Assembly,
|
||||||
|
&module.name,
|
||||||
|
cgcx.invocation_temp.as_deref(),
|
||||||
|
);
|
||||||
|
|
||||||
// We can't use the same module for asm and object code output,
|
// We can't use the same module for asm and object code output,
|
||||||
// because that triggers various errors like invalid IR or broken
|
// because that triggers various errors like invalid IR or broken
|
||||||
|
@ -929,7 +954,9 @@ pub(crate) unsafe fn codegen(
|
||||||
.prof
|
.prof
|
||||||
.generic_activity_with_arg("LLVM_module_codegen_emit_obj", &*module.name);
|
.generic_activity_with_arg("LLVM_module_codegen_emit_obj", &*module.name);
|
||||||
|
|
||||||
let dwo_out = cgcx.output_filenames.temp_path_dwo(module_name);
|
let dwo_out = cgcx
|
||||||
|
.output_filenames
|
||||||
|
.temp_path_dwo_for_cgu(&module.name, cgcx.invocation_temp.as_deref());
|
||||||
let dwo_out = match (cgcx.split_debuginfo, cgcx.split_dwarf_kind) {
|
let dwo_out = match (cgcx.split_debuginfo, cgcx.split_dwarf_kind) {
|
||||||
// Don't change how DWARF is emitted when disabled.
|
// Don't change how DWARF is emitted when disabled.
|
||||||
(SplitDebuginfo::Off, _) => None,
|
(SplitDebuginfo::Off, _) => None,
|
||||||
|
@ -994,6 +1021,7 @@ pub(crate) unsafe fn codegen(
|
||||||
config.emit_asm,
|
config.emit_asm,
|
||||||
config.emit_ir,
|
config.emit_ir,
|
||||||
&cgcx.output_filenames,
|
&cgcx.output_filenames,
|
||||||
|
cgcx.invocation_temp.as_deref(),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -910,7 +910,8 @@ pub(crate) fn build_compile_unit_di_node<'ll, 'tcx>(
|
||||||
&& let Some(f) = output_filenames.split_dwarf_path(
|
&& let Some(f) = output_filenames.split_dwarf_path(
|
||||||
tcx.sess.split_debuginfo(),
|
tcx.sess.split_debuginfo(),
|
||||||
tcx.sess.opts.unstable_opts.split_dwarf_kind,
|
tcx.sess.opts.unstable_opts.split_dwarf_kind,
|
||||||
Some(codegen_unit_name),
|
codegen_unit_name,
|
||||||
|
tcx.sess.invocation_temp.as_deref(),
|
||||||
) {
|
) {
|
||||||
// We get a path relative to the working directory from split_dwarf_path
|
// We get a path relative to the working directory from split_dwarf_path
|
||||||
Some(tcx.sess.source_map().path_mapping().to_real_filename(f))
|
Some(tcx.sess.source_map().path_mapping().to_real_filename(f))
|
||||||
|
|
|
@ -112,8 +112,12 @@ pub fn link_binary(
|
||||||
codegen_results.crate_info.local_crate_name,
|
codegen_results.crate_info.local_crate_name,
|
||||||
);
|
);
|
||||||
let crate_name = format!("{}", codegen_results.crate_info.local_crate_name);
|
let crate_name = format!("{}", codegen_results.crate_info.local_crate_name);
|
||||||
let out_filename =
|
let out_filename = output.file_for_writing(
|
||||||
output.file_for_writing(outputs, OutputType::Exe, Some(crate_name.as_str()));
|
outputs,
|
||||||
|
OutputType::Exe,
|
||||||
|
&crate_name,
|
||||||
|
sess.invocation_temp.as_deref(),
|
||||||
|
);
|
||||||
match crate_type {
|
match crate_type {
|
||||||
CrateType::Rlib => {
|
CrateType::Rlib => {
|
||||||
let _timer = sess.timer("link_rlib");
|
let _timer = sess.timer("link_rlib");
|
||||||
|
|
|
@ -306,14 +306,18 @@ impl TargetMachineFactoryConfig {
|
||||||
cgcx.output_filenames.split_dwarf_path(
|
cgcx.output_filenames.split_dwarf_path(
|
||||||
cgcx.split_debuginfo,
|
cgcx.split_debuginfo,
|
||||||
cgcx.split_dwarf_kind,
|
cgcx.split_dwarf_kind,
|
||||||
Some(module_name),
|
module_name,
|
||||||
|
cgcx.invocation_temp.as_deref(),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
let output_obj_file =
|
let output_obj_file = Some(cgcx.output_filenames.temp_path_for_cgu(
|
||||||
Some(cgcx.output_filenames.temp_path(OutputType::Object, Some(module_name)));
|
OutputType::Object,
|
||||||
|
module_name,
|
||||||
|
cgcx.invocation_temp.as_deref(),
|
||||||
|
));
|
||||||
TargetMachineFactoryConfig { split_dwarf_file, output_obj_file }
|
TargetMachineFactoryConfig { split_dwarf_file, output_obj_file }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -344,6 +348,7 @@ pub struct CodegenContext<B: WriteBackendMethods> {
|
||||||
pub crate_types: Vec<CrateType>,
|
pub crate_types: Vec<CrateType>,
|
||||||
pub each_linked_rlib_for_lto: Vec<(CrateNum, PathBuf)>,
|
pub each_linked_rlib_for_lto: Vec<(CrateNum, PathBuf)>,
|
||||||
pub output_filenames: Arc<OutputFilenames>,
|
pub output_filenames: Arc<OutputFilenames>,
|
||||||
|
pub invocation_temp: Option<String>,
|
||||||
pub regular_module_config: Arc<ModuleConfig>,
|
pub regular_module_config: Arc<ModuleConfig>,
|
||||||
pub metadata_module_config: Arc<ModuleConfig>,
|
pub metadata_module_config: Arc<ModuleConfig>,
|
||||||
pub allocator_module_config: Arc<ModuleConfig>,
|
pub allocator_module_config: Arc<ModuleConfig>,
|
||||||
|
@ -582,8 +587,11 @@ fn produce_final_output_artifacts(
|
||||||
if let [module] = &compiled_modules.modules[..] {
|
if let [module] = &compiled_modules.modules[..] {
|
||||||
// 1) Only one codegen unit. In this case it's no difficulty
|
// 1) Only one codegen unit. In this case it's no difficulty
|
||||||
// to copy `foo.0.x` to `foo.x`.
|
// to copy `foo.0.x` to `foo.x`.
|
||||||
let module_name = Some(&module.name[..]);
|
let path = crate_output.temp_path_for_cgu(
|
||||||
let path = crate_output.temp_path(output_type, module_name);
|
output_type,
|
||||||
|
&module.name,
|
||||||
|
sess.invocation_temp.as_deref(),
|
||||||
|
);
|
||||||
let output = crate_output.path(output_type);
|
let output = crate_output.path(output_type);
|
||||||
if !output_type.is_text_output() && output.is_tty() {
|
if !output_type.is_text_output() && output.is_tty() {
|
||||||
sess.dcx()
|
sess.dcx()
|
||||||
|
@ -596,22 +604,15 @@ fn produce_final_output_artifacts(
|
||||||
ensure_removed(sess.dcx(), &path);
|
ensure_removed(sess.dcx(), &path);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let extension = crate_output
|
|
||||||
.temp_path(output_type, None)
|
|
||||||
.extension()
|
|
||||||
.unwrap()
|
|
||||||
.to_str()
|
|
||||||
.unwrap()
|
|
||||||
.to_owned();
|
|
||||||
|
|
||||||
if crate_output.outputs.contains_explicit_name(&output_type) {
|
if crate_output.outputs.contains_explicit_name(&output_type) {
|
||||||
// 2) Multiple codegen units, with `--emit foo=some_name`. We have
|
// 2) Multiple codegen units, with `--emit foo=some_name`. We have
|
||||||
// no good solution for this case, so warn the user.
|
// no good solution for this case, so warn the user.
|
||||||
sess.dcx().emit_warn(errors::IgnoringEmitPath { extension });
|
sess.dcx()
|
||||||
|
.emit_warn(errors::IgnoringEmitPath { extension: output_type.extension() });
|
||||||
} else if crate_output.single_output_file.is_some() {
|
} else if crate_output.single_output_file.is_some() {
|
||||||
// 3) Multiple codegen units, with `-o some_name`. We have
|
// 3) Multiple codegen units, with `-o some_name`. We have
|
||||||
// no good solution for this case, so warn the user.
|
// no good solution for this case, so warn the user.
|
||||||
sess.dcx().emit_warn(errors::IgnoringOutput { extension });
|
sess.dcx().emit_warn(errors::IgnoringOutput { extension: output_type.extension() });
|
||||||
} else {
|
} else {
|
||||||
// 4) Multiple codegen units, but no explicit name. We
|
// 4) Multiple codegen units, but no explicit name. We
|
||||||
// just leave the `foo.0.x` files in place.
|
// just leave the `foo.0.x` files in place.
|
||||||
|
@ -967,7 +968,12 @@ fn execute_copy_from_cache_work_item<B: ExtraBackendMethods>(
|
||||||
module.source.saved_files.get("dwo").as_ref().and_then(|saved_dwarf_object_file| {
|
module.source.saved_files.get("dwo").as_ref().and_then(|saved_dwarf_object_file| {
|
||||||
let dwarf_obj_out = cgcx
|
let dwarf_obj_out = cgcx
|
||||||
.output_filenames
|
.output_filenames
|
||||||
.split_dwarf_path(cgcx.split_debuginfo, cgcx.split_dwarf_kind, Some(&module.name))
|
.split_dwarf_path(
|
||||||
|
cgcx.split_debuginfo,
|
||||||
|
cgcx.split_dwarf_kind,
|
||||||
|
&module.name,
|
||||||
|
cgcx.invocation_temp.as_deref(),
|
||||||
|
)
|
||||||
.expect(
|
.expect(
|
||||||
"saved dwarf object in work product but `split_dwarf_path` returned `None`",
|
"saved dwarf object in work product but `split_dwarf_path` returned `None`",
|
||||||
);
|
);
|
||||||
|
@ -977,7 +983,11 @@ fn execute_copy_from_cache_work_item<B: ExtraBackendMethods>(
|
||||||
let mut load_from_incr_cache = |perform, output_type: OutputType| {
|
let mut load_from_incr_cache = |perform, output_type: OutputType| {
|
||||||
if perform {
|
if perform {
|
||||||
let saved_file = module.source.saved_files.get(output_type.extension())?;
|
let saved_file = module.source.saved_files.get(output_type.extension())?;
|
||||||
let output_path = cgcx.output_filenames.temp_path(output_type, Some(&module.name));
|
let output_path = cgcx.output_filenames.temp_path_for_cgu(
|
||||||
|
output_type,
|
||||||
|
&module.name,
|
||||||
|
cgcx.invocation_temp.as_deref(),
|
||||||
|
);
|
||||||
load_from_incr_comp_dir(output_path, &saved_file)
|
load_from_incr_comp_dir(output_path, &saved_file)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
@ -1222,6 +1232,7 @@ fn start_executing_work<B: ExtraBackendMethods>(
|
||||||
split_dwarf_kind: tcx.sess.opts.unstable_opts.split_dwarf_kind,
|
split_dwarf_kind: tcx.sess.opts.unstable_opts.split_dwarf_kind,
|
||||||
parallel: backend.supports_parallel() && !sess.opts.unstable_opts.no_parallel_backend,
|
parallel: backend.supports_parallel() && !sess.opts.unstable_opts.no_parallel_backend,
|
||||||
pointer_size: tcx.data_layout.pointer_size,
|
pointer_size: tcx.data_layout.pointer_size,
|
||||||
|
invocation_temp: sess.invocation_temp.clone(),
|
||||||
};
|
};
|
||||||
|
|
||||||
// This is the "main loop" of parallel work happening for parallel codegen.
|
// This is the "main loop" of parallel work happening for parallel codegen.
|
||||||
|
|
|
@ -640,8 +640,11 @@ pub fn codegen_crate<B: ExtraBackendMethods>(
|
||||||
let metadata_cgu_name =
|
let metadata_cgu_name =
|
||||||
cgu_name_builder.build_cgu_name(LOCAL_CRATE, &["crate"], Some("metadata")).to_string();
|
cgu_name_builder.build_cgu_name(LOCAL_CRATE, &["crate"], Some("metadata")).to_string();
|
||||||
tcx.sess.time("write_compressed_metadata", || {
|
tcx.sess.time("write_compressed_metadata", || {
|
||||||
let file_name =
|
let file_name = tcx.output_filenames(()).temp_path_for_cgu(
|
||||||
tcx.output_filenames(()).temp_path(OutputType::Metadata, Some(&metadata_cgu_name));
|
OutputType::Metadata,
|
||||||
|
&metadata_cgu_name,
|
||||||
|
tcx.sess.invocation_temp.as_deref(),
|
||||||
|
);
|
||||||
let data = create_compressed_metadata_file(
|
let data = create_compressed_metadata_file(
|
||||||
tcx.sess,
|
tcx.sess,
|
||||||
&metadata,
|
&metadata,
|
||||||
|
|
|
@ -277,13 +277,13 @@ pub struct BinaryOutputToTty {
|
||||||
#[derive(Diagnostic)]
|
#[derive(Diagnostic)]
|
||||||
#[diag(codegen_ssa_ignoring_emit_path)]
|
#[diag(codegen_ssa_ignoring_emit_path)]
|
||||||
pub struct IgnoringEmitPath {
|
pub struct IgnoringEmitPath {
|
||||||
pub extension: String,
|
pub extension: &'static str,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Diagnostic)]
|
#[derive(Diagnostic)]
|
||||||
#[diag(codegen_ssa_ignoring_output)]
|
#[diag(codegen_ssa_ignoring_output)]
|
||||||
pub struct IgnoringOutput {
|
pub struct IgnoringOutput {
|
||||||
pub extension: String,
|
pub extension: &'static str,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Diagnostic)]
|
#[derive(Diagnostic)]
|
||||||
|
|
|
@ -105,13 +105,19 @@ impl<M> ModuleCodegen<M> {
|
||||||
emit_asm: bool,
|
emit_asm: bool,
|
||||||
emit_ir: bool,
|
emit_ir: bool,
|
||||||
outputs: &OutputFilenames,
|
outputs: &OutputFilenames,
|
||||||
|
invocation_temp: Option<&str>,
|
||||||
) -> CompiledModule {
|
) -> CompiledModule {
|
||||||
let object = emit_obj.then(|| outputs.temp_path(OutputType::Object, Some(&self.name)));
|
let object = emit_obj
|
||||||
let dwarf_object = emit_dwarf_obj.then(|| outputs.temp_path_dwo(Some(&self.name)));
|
.then(|| outputs.temp_path_for_cgu(OutputType::Object, &self.name, invocation_temp));
|
||||||
let bytecode = emit_bc.then(|| outputs.temp_path(OutputType::Bitcode, Some(&self.name)));
|
let dwarf_object =
|
||||||
let assembly = emit_asm.then(|| outputs.temp_path(OutputType::Assembly, Some(&self.name)));
|
emit_dwarf_obj.then(|| outputs.temp_path_dwo_for_cgu(&self.name, invocation_temp));
|
||||||
let llvm_ir =
|
let bytecode = emit_bc
|
||||||
emit_ir.then(|| outputs.temp_path(OutputType::LlvmAssembly, Some(&self.name)));
|
.then(|| outputs.temp_path_for_cgu(OutputType::Bitcode, &self.name, invocation_temp));
|
||||||
|
let assembly = emit_asm
|
||||||
|
.then(|| outputs.temp_path_for_cgu(OutputType::Assembly, &self.name, invocation_temp));
|
||||||
|
let llvm_ir = emit_ir.then(|| {
|
||||||
|
outputs.temp_path_for_cgu(OutputType::LlvmAssembly, &self.name, invocation_temp)
|
||||||
|
});
|
||||||
|
|
||||||
CompiledModule {
|
CompiledModule {
|
||||||
name: self.name.clone(),
|
name: self.name.clone(),
|
||||||
|
|
|
@ -800,6 +800,7 @@ pub fn create_and_enter_global_ctxt<T, F: for<'tcx> FnOnce(TyCtxt<'tcx>) -> T>(
|
||||||
sess.opts.cg.metadata.clone(),
|
sess.opts.cg.metadata.clone(),
|
||||||
sess.cfg_version,
|
sess.cfg_version,
|
||||||
);
|
);
|
||||||
|
|
||||||
let outputs = util::build_output_filenames(&pre_configured_attrs, sess);
|
let outputs = util::build_output_filenames(&pre_configured_attrs, sess);
|
||||||
|
|
||||||
let dep_type = DepsType { dep_names: rustc_query_impl::dep_kind_names() };
|
let dep_type = DepsType { dep_names: rustc_query_impl::dep_kind_names() };
|
||||||
|
|
|
@ -279,7 +279,7 @@ impl<'tcx> TyCtxt<'tcx> {
|
||||||
p.hash(&mut s);
|
p.hash(&mut s);
|
||||||
let hash = s.finish();
|
let hash = s.finish();
|
||||||
*path = Some(path.take().unwrap_or_else(|| {
|
*path = Some(path.take().unwrap_or_else(|| {
|
||||||
self.output_filenames(()).temp_path_ext(&format!("long-type-{hash}.txt"), None)
|
self.output_filenames(()).temp_path_for_diagnostic(&format!("long-type-{hash}.txt"))
|
||||||
}));
|
}));
|
||||||
let Ok(mut file) =
|
let Ok(mut file) =
|
||||||
File::options().create(true).read(true).append(true).open(&path.as_ref().unwrap())
|
File::options().create(true).read(true).append(true).open(&path.as_ref().unwrap())
|
||||||
|
|
|
@ -382,7 +382,7 @@ pub fn shrunk_instance_name<'tcx>(
|
||||||
return (s, None);
|
return (s, None);
|
||||||
}
|
}
|
||||||
|
|
||||||
let path = tcx.output_filenames(()).temp_path_ext("long-type.txt", None);
|
let path = tcx.output_filenames(()).temp_path_for_diagnostic("long-type.txt");
|
||||||
let written_to_path = std::fs::write(&path, s).ok().map(|_| path);
|
let written_to_path = std::fs::write(&path, s).ok().map(|_| path);
|
||||||
|
|
||||||
(shrunk, written_to_path)
|
(shrunk, written_to_path)
|
||||||
|
|
|
@ -7,6 +7,7 @@ edition = "2024"
|
||||||
# tidy-alphabetical-start
|
# tidy-alphabetical-start
|
||||||
bitflags = "2.4.1"
|
bitflags = "2.4.1"
|
||||||
getopts = "0.2"
|
getopts = "0.2"
|
||||||
|
rand = "0.9.0"
|
||||||
rustc_abi = { path = "../rustc_abi" }
|
rustc_abi = { path = "../rustc_abi" }
|
||||||
rustc_ast = { path = "../rustc_ast" }
|
rustc_ast = { path = "../rustc_ast" }
|
||||||
rustc_data_structures = { path = "../rustc_data_structures" }
|
rustc_data_structures = { path = "../rustc_data_structures" }
|
||||||
|
|
|
@ -1015,11 +1015,14 @@ impl OutFileName {
|
||||||
&self,
|
&self,
|
||||||
outputs: &OutputFilenames,
|
outputs: &OutputFilenames,
|
||||||
flavor: OutputType,
|
flavor: OutputType,
|
||||||
codegen_unit_name: Option<&str>,
|
codegen_unit_name: &str,
|
||||||
|
invocation_temp: Option<&str>,
|
||||||
) -> PathBuf {
|
) -> PathBuf {
|
||||||
match *self {
|
match *self {
|
||||||
OutFileName::Real(ref path) => path.clone(),
|
OutFileName::Real(ref path) => path.clone(),
|
||||||
OutFileName::Stdout => outputs.temp_path(flavor, codegen_unit_name),
|
OutFileName::Stdout => {
|
||||||
|
outputs.temp_path_for_cgu(flavor, codegen_unit_name, invocation_temp)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1094,40 +1097,59 @@ impl OutputFilenames {
|
||||||
/// Gets the path where a compilation artifact of the given type for the
|
/// Gets the path where a compilation artifact of the given type for the
|
||||||
/// given codegen unit should be placed on disk. If codegen_unit_name is
|
/// given codegen unit should be placed on disk. If codegen_unit_name is
|
||||||
/// None, a path distinct from those of any codegen unit will be generated.
|
/// None, a path distinct from those of any codegen unit will be generated.
|
||||||
pub fn temp_path(&self, flavor: OutputType, codegen_unit_name: Option<&str>) -> PathBuf {
|
pub fn temp_path_for_cgu(
|
||||||
|
&self,
|
||||||
|
flavor: OutputType,
|
||||||
|
codegen_unit_name: &str,
|
||||||
|
invocation_temp: Option<&str>,
|
||||||
|
) -> PathBuf {
|
||||||
let extension = flavor.extension();
|
let extension = flavor.extension();
|
||||||
self.temp_path_ext(extension, codegen_unit_name)
|
self.temp_path_ext_for_cgu(extension, codegen_unit_name, invocation_temp)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Like `temp_path`, but specifically for dwarf objects.
|
/// Like `temp_path`, but specifically for dwarf objects.
|
||||||
pub fn temp_path_dwo(&self, codegen_unit_name: Option<&str>) -> PathBuf {
|
pub fn temp_path_dwo_for_cgu(
|
||||||
self.temp_path_ext(DWARF_OBJECT_EXT, codegen_unit_name)
|
&self,
|
||||||
|
codegen_unit_name: &str,
|
||||||
|
invocation_temp: Option<&str>,
|
||||||
|
) -> PathBuf {
|
||||||
|
self.temp_path_ext_for_cgu(DWARF_OBJECT_EXT, codegen_unit_name, invocation_temp)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Like `temp_path`, but also supports things where there is no corresponding
|
/// Like `temp_path`, but also supports things where there is no corresponding
|
||||||
/// OutputType, like noopt-bitcode or lto-bitcode.
|
/// OutputType, like noopt-bitcode or lto-bitcode.
|
||||||
pub fn temp_path_ext(&self, ext: &str, codegen_unit_name: Option<&str>) -> PathBuf {
|
pub fn temp_path_ext_for_cgu(
|
||||||
let mut extension = String::new();
|
&self,
|
||||||
|
ext: &str,
|
||||||
|
codegen_unit_name: &str,
|
||||||
|
invocation_temp: Option<&str>,
|
||||||
|
) -> PathBuf {
|
||||||
|
let mut extension = codegen_unit_name.to_string();
|
||||||
|
|
||||||
if let Some(codegen_unit_name) = codegen_unit_name {
|
// Append `.{invocation_temp}` to ensure temporary files are unique.
|
||||||
extension.push_str(codegen_unit_name);
|
if let Some(rng) = invocation_temp {
|
||||||
|
extension.push('.');
|
||||||
|
extension.push_str(rng);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME: This is sketchy that we're not appending `.rcgu` when the ext is empty.
|
||||||
|
// Append `.rcgu.{ext}`.
|
||||||
if !ext.is_empty() {
|
if !ext.is_empty() {
|
||||||
if !extension.is_empty() {
|
extension.push('.');
|
||||||
extension.push('.');
|
extension.push_str(RUST_CGU_EXT);
|
||||||
extension.push_str(RUST_CGU_EXT);
|
extension.push('.');
|
||||||
extension.push('.');
|
|
||||||
}
|
|
||||||
|
|
||||||
extension.push_str(ext);
|
extension.push_str(ext);
|
||||||
}
|
}
|
||||||
|
|
||||||
let temps_directory = self.temps_directory.as_ref().unwrap_or(&self.out_directory);
|
let temps_directory = self.temps_directory.as_ref().unwrap_or(&self.out_directory);
|
||||||
|
|
||||||
self.with_directory_and_extension(temps_directory, &extension)
|
self.with_directory_and_extension(temps_directory, &extension)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn temp_path_for_diagnostic(&self, ext: &str) -> PathBuf {
|
||||||
|
let temps_directory = self.temps_directory.as_ref().unwrap_or(&self.out_directory);
|
||||||
|
self.with_directory_and_extension(temps_directory, &ext)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn with_extension(&self, extension: &str) -> PathBuf {
|
pub fn with_extension(&self, extension: &str) -> PathBuf {
|
||||||
self.with_directory_and_extension(&self.out_directory, extension)
|
self.with_directory_and_extension(&self.out_directory, extension)
|
||||||
}
|
}
|
||||||
|
@ -1144,10 +1166,11 @@ impl OutputFilenames {
|
||||||
&self,
|
&self,
|
||||||
split_debuginfo_kind: SplitDebuginfo,
|
split_debuginfo_kind: SplitDebuginfo,
|
||||||
split_dwarf_kind: SplitDwarfKind,
|
split_dwarf_kind: SplitDwarfKind,
|
||||||
cgu_name: Option<&str>,
|
cgu_name: &str,
|
||||||
|
invocation_temp: Option<&str>,
|
||||||
) -> Option<PathBuf> {
|
) -> Option<PathBuf> {
|
||||||
let obj_out = self.temp_path(OutputType::Object, cgu_name);
|
let obj_out = self.temp_path_for_cgu(OutputType::Object, cgu_name, invocation_temp);
|
||||||
let dwo_out = self.temp_path_dwo(cgu_name);
|
let dwo_out = self.temp_path_dwo_for_cgu(cgu_name, invocation_temp);
|
||||||
match (split_debuginfo_kind, split_dwarf_kind) {
|
match (split_debuginfo_kind, split_dwarf_kind) {
|
||||||
(SplitDebuginfo::Off, SplitDwarfKind::Single | SplitDwarfKind::Split) => None,
|
(SplitDebuginfo::Off, SplitDwarfKind::Single | SplitDwarfKind::Split) => None,
|
||||||
// Single mode doesn't change how DWARF is emitted, but does add Split DWARF attributes
|
// Single mode doesn't change how DWARF is emitted, but does add Split DWARF attributes
|
||||||
|
|
|
@ -6,6 +6,8 @@ use std::sync::Arc;
|
||||||
use std::sync::atomic::AtomicBool;
|
use std::sync::atomic::AtomicBool;
|
||||||
use std::{env, fmt, io};
|
use std::{env, fmt, io};
|
||||||
|
|
||||||
|
use rand::{RngCore, rng};
|
||||||
|
use rustc_data_structures::base_n::{CASE_INSENSITIVE, ToBaseN};
|
||||||
use rustc_data_structures::flock;
|
use rustc_data_structures::flock;
|
||||||
use rustc_data_structures::fx::{FxHashMap, FxIndexSet};
|
use rustc_data_structures::fx::{FxHashMap, FxIndexSet};
|
||||||
use rustc_data_structures::profiling::{SelfProfiler, SelfProfilerRef};
|
use rustc_data_structures::profiling::{SelfProfiler, SelfProfilerRef};
|
||||||
|
@ -203,6 +205,14 @@ pub struct Session {
|
||||||
|
|
||||||
target_filesearch: FileSearch,
|
target_filesearch: FileSearch,
|
||||||
host_filesearch: FileSearch,
|
host_filesearch: FileSearch,
|
||||||
|
|
||||||
|
/// A random string generated per invocation of rustc.
|
||||||
|
///
|
||||||
|
/// This is prepended to all temporary files so that they do not collide
|
||||||
|
/// during concurrent invocations of rustc, or past invocations that were
|
||||||
|
/// preserved with a flag like `-C save-temps`, since these files may be
|
||||||
|
/// hard linked.
|
||||||
|
pub invocation_temp: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, PartialOrd, Ord)]
|
#[derive(PartialEq, Eq, PartialOrd, Ord)]
|
||||||
|
@ -1117,6 +1127,12 @@ pub fn build_session(
|
||||||
let target_filesearch =
|
let target_filesearch =
|
||||||
filesearch::FileSearch::new(&sopts.search_paths, &target_tlib_path, &target);
|
filesearch::FileSearch::new(&sopts.search_paths, &target_tlib_path, &target);
|
||||||
let host_filesearch = filesearch::FileSearch::new(&sopts.search_paths, &host_tlib_path, &host);
|
let host_filesearch = filesearch::FileSearch::new(&sopts.search_paths, &host_tlib_path, &host);
|
||||||
|
|
||||||
|
let invocation_temp = sopts
|
||||||
|
.incremental
|
||||||
|
.as_ref()
|
||||||
|
.map(|_| rng().next_u32().to_base_fixed_len(CASE_INSENSITIVE).to_string());
|
||||||
|
|
||||||
let sess = Session {
|
let sess = Session {
|
||||||
target,
|
target,
|
||||||
host,
|
host,
|
||||||
|
@ -1140,6 +1156,7 @@ pub fn build_session(
|
||||||
expanded_args,
|
expanded_args,
|
||||||
target_filesearch,
|
target_filesearch,
|
||||||
host_filesearch,
|
host_filesearch,
|
||||||
|
invocation_temp,
|
||||||
};
|
};
|
||||||
|
|
||||||
validate_commandline_args_with_session_available(&sess);
|
validate_commandline_args_with_session_available(&sess);
|
||||||
|
|
32
tests/run-make/dirty-incr-due-to-hard-link/rmake.rs
Normal file
32
tests/run-make/dirty-incr-due-to-hard-link/rmake.rs
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
//@ only-x86_64-unknown-linux-gnu
|
||||||
|
|
||||||
|
// Regression test for the incremental bug in <https://github.com/rust-lang/rust/issues/139407>.
|
||||||
|
//
|
||||||
|
// A detailed explanation is described in <https://github.com/rust-lang/rust/pull/139453>,
|
||||||
|
// however the gist of the issue is that hard-linking temporary files can interact strangely
|
||||||
|
// across incremental sessions that are not finalized due to errors originating from the
|
||||||
|
// codegen backend.
|
||||||
|
|
||||||
|
use run_make_support::{run, rustc};
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mk_rustc = || {
|
||||||
|
let mut rustc = rustc();
|
||||||
|
rustc.input("test.rs").incremental("incr").arg("-Csave-temps").output("test");
|
||||||
|
rustc
|
||||||
|
};
|
||||||
|
|
||||||
|
// Revision 1
|
||||||
|
mk_rustc().cfg("rpass1").run();
|
||||||
|
|
||||||
|
run("test");
|
||||||
|
|
||||||
|
// Revision 2
|
||||||
|
mk_rustc().cfg("cfail2").run_fail();
|
||||||
|
// Expected to fail.
|
||||||
|
|
||||||
|
// Revision 3
|
||||||
|
mk_rustc().cfg("rpass3").run();
|
||||||
|
|
||||||
|
run("test");
|
||||||
|
}
|
31
tests/run-make/dirty-incr-due-to-hard-link/test.rs
Normal file
31
tests/run-make/dirty-incr-due-to-hard-link/test.rs
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
#[inline(never)]
|
||||||
|
#[cfg(any(rpass1, rpass3))]
|
||||||
|
fn a() -> i32 {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(any(cfail2))]
|
||||||
|
fn a() -> i32 {
|
||||||
|
1
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
evil::evil();
|
||||||
|
assert_eq!(a(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
mod evil {
|
||||||
|
#[cfg(any(rpass1, rpass3))]
|
||||||
|
pub fn evil() {
|
||||||
|
unsafe {
|
||||||
|
std::arch::asm!("/* */");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(any(cfail2))]
|
||||||
|
pub fn evil() {
|
||||||
|
unsafe {
|
||||||
|
std::arch::asm!("missing");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue