Fix no_integrated_as option to work with new codegen architecture.
Old implementation called the assembler once per crate, but we need to call it for each object file instead, because a single crate can now have more than one object file. This patch fixes issue #45836 (Can't compile core for msp430 in release mode)
This commit is contained in:
parent
8ff449d505
commit
3f61742cc0
@ -16,6 +16,7 @@ use std::fmt;
|
||||
use std::io;
|
||||
use std::process::{self, Output, Child};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Command {
|
||||
program: OsString,
|
||||
args: Vec<OsString>,
|
||||
|
@ -11,6 +11,7 @@
|
||||
use back::bytecode::{self, RLIB_BYTECODE_EXTENSION};
|
||||
use back::lto::{self, ModuleBuffer, ThinBuffer};
|
||||
use back::link::{self, get_linker, remove};
|
||||
use back::command::Command;
|
||||
use back::linker::LinkerInfo;
|
||||
use back::symbol_export::ExportedSymbols;
|
||||
use base;
|
||||
@ -18,7 +19,7 @@ use consts;
|
||||
use rustc_incremental::{save_trans_partition, in_incr_comp_dir};
|
||||
use rustc::dep_graph::{DepGraph, WorkProductFileKind};
|
||||
use rustc::middle::cstore::{LinkMeta, EncodedMetadata};
|
||||
use rustc::session::config::{self, OutputFilenames, OutputType, OutputTypes, Passes, SomePasses,
|
||||
use rustc::session::config::{self, OutputFilenames, OutputType, Passes, SomePasses,
|
||||
AllPasses, Sanitizer};
|
||||
use rustc::session::Session;
|
||||
use rustc::util::nodemap::FxHashMap;
|
||||
@ -32,7 +33,7 @@ use CrateInfo;
|
||||
use rustc::hir::def_id::{CrateNum, LOCAL_CRATE};
|
||||
use rustc::ty::TyCtxt;
|
||||
use rustc::util::common::{time, time_depth, set_time_depth, path2cstr, print_time_passes_entry};
|
||||
use rustc::util::fs::{link_or_copy, rename_or_copy_remove};
|
||||
use rustc::util::fs::{link_or_copy};
|
||||
use errors::{self, Handler, Level, DiagnosticBuilder, FatalError, DiagnosticId};
|
||||
use errors::emitter::{Emitter};
|
||||
use syntax::attr;
|
||||
@ -258,6 +259,7 @@ pub struct ModuleConfig {
|
||||
// make the object file bitcode. Provides easy compatibility with
|
||||
// emscripten's ecc compiler, when used as the linker.
|
||||
obj_is_bitcode: bool,
|
||||
no_integrated_as: bool,
|
||||
}
|
||||
|
||||
impl ModuleConfig {
|
||||
@ -275,6 +277,7 @@ impl ModuleConfig {
|
||||
emit_asm: false,
|
||||
emit_obj: false,
|
||||
obj_is_bitcode: false,
|
||||
no_integrated_as: false,
|
||||
|
||||
no_verify: false,
|
||||
no_prepopulate_passes: false,
|
||||
@ -313,6 +316,12 @@ impl ModuleConfig {
|
||||
}
|
||||
}
|
||||
|
||||
/// Assembler name and command used by codegen when no_integrated_as is enabled
|
||||
struct AssemblerCommand {
|
||||
name: PathBuf,
|
||||
cmd: Command,
|
||||
}
|
||||
|
||||
/// Additional resources used by optimize_and_codegen (not module specific)
|
||||
#[derive(Clone)]
|
||||
pub struct CodegenContext {
|
||||
@ -356,6 +365,8 @@ pub struct CodegenContext {
|
||||
// A reference to the TimeGraph so we can register timings. None means that
|
||||
// measuring is disabled.
|
||||
time_graph: Option<TimeGraph>,
|
||||
// The assembler command if no_integrated_as option is enabled, None otherwise
|
||||
assembler_cmd: Option<Arc<AssemblerCommand>>,
|
||||
}
|
||||
|
||||
impl CodegenContext {
|
||||
@ -639,13 +650,17 @@ unsafe fn codegen(cgcx: &CodegenContext,
|
||||
!cgcx.crate_types.contains(&config::CrateTypeRlib) &&
|
||||
mtrans.kind == ModuleKind::Regular;
|
||||
|
||||
// If we don't have the integrated assembler, then we need to emit asm
|
||||
// from LLVM and use `gcc` to create the object file.
|
||||
let asm_to_obj = config.emit_obj && config.no_integrated_as;
|
||||
|
||||
// Change what we write and cleanup based on whether obj files are
|
||||
// just llvm bitcode. In that case write bitcode, and possibly
|
||||
// delete the bitcode if it wasn't requested. Don't generate the
|
||||
// machine code, instead copy the .o file from the .bc
|
||||
let write_bc = config.emit_bc || (config.obj_is_bitcode && !asm2wasm);
|
||||
let rm_bc = !config.emit_bc && config.obj_is_bitcode && !asm2wasm;
|
||||
let write_obj = config.emit_obj && !config.obj_is_bitcode && !asm2wasm;
|
||||
let write_obj = config.emit_obj && !config.obj_is_bitcode && !asm2wasm && !asm_to_obj;
|
||||
let copy_bc_to_obj = config.emit_obj && config.obj_is_bitcode && !asm2wasm;
|
||||
|
||||
let bc_out = cgcx.output_filenames.temp_path(OutputType::Bitcode, module_name);
|
||||
@ -725,7 +740,7 @@ unsafe fn codegen(cgcx: &CodegenContext,
|
||||
timeline.record("ir");
|
||||
}
|
||||
|
||||
if config.emit_asm || (asm2wasm && config.emit_obj) {
|
||||
if config.emit_asm || (asm2wasm && config.emit_obj) || asm_to_obj {
|
||||
let path = cgcx.output_filenames.temp_path(OutputType::Assembly, module_name);
|
||||
|
||||
// We can't use the same module for asm and binary output, because that triggers
|
||||
@ -760,6 +775,14 @@ unsafe fn codegen(cgcx: &CodegenContext,
|
||||
llvm::FileType::ObjectFile)
|
||||
})?;
|
||||
timeline.record("obj");
|
||||
} else if asm_to_obj {
|
||||
let assembly = cgcx.output_filenames.temp_path(OutputType::Assembly, module_name);
|
||||
run_assembler(cgcx, diag_handler, &assembly, &obj_out);
|
||||
timeline.record("asm_to_obj");
|
||||
|
||||
if !config.emit_asm && !cgcx.save_temps {
|
||||
drop(fs::remove_file(&assembly));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@ -841,7 +864,6 @@ pub fn start_async_translation(tcx: TyCtxt,
|
||||
total_cgus: usize)
|
||||
-> OngoingCrateTranslation {
|
||||
let sess = tcx.sess;
|
||||
let crate_output = tcx.output_filenames(LOCAL_CRATE);
|
||||
let crate_name = tcx.crate_name(LOCAL_CRATE);
|
||||
let no_builtins = attr::contains_name(&tcx.hir.krate().attrs, "no_builtins");
|
||||
let subsystem = attr::first_attr_value_str_by_name(&tcx.hir.krate().attrs,
|
||||
@ -855,19 +877,9 @@ pub fn start_async_translation(tcx: TyCtxt,
|
||||
subsystem.to_string()
|
||||
});
|
||||
|
||||
let no_integrated_as = tcx.sess.opts.cg.no_integrated_as ||
|
||||
(tcx.sess.target.target.options.no_integrated_as &&
|
||||
(crate_output.outputs.contains_key(&OutputType::Object) ||
|
||||
crate_output.outputs.contains_key(&OutputType::Exe)));
|
||||
let linker_info = LinkerInfo::new(tcx);
|
||||
let crate_info = CrateInfo::new(tcx);
|
||||
|
||||
let output_types_override = if no_integrated_as {
|
||||
OutputTypes::new(&[(OutputType::Assembly, None)])
|
||||
} else {
|
||||
sess.opts.output_types.clone()
|
||||
};
|
||||
|
||||
// Figure out what we actually need to build.
|
||||
let mut modules_config = ModuleConfig::new(sess.opts.cg.passes.clone());
|
||||
let mut metadata_config = ModuleConfig::new(vec![]);
|
||||
@ -913,7 +925,10 @@ pub fn start_async_translation(tcx: TyCtxt,
|
||||
allocator_config.emit_bc_compressed = true;
|
||||
}
|
||||
|
||||
for output_type in output_types_override.keys() {
|
||||
modules_config.no_integrated_as = tcx.sess.opts.cg.no_integrated_as ||
|
||||
tcx.sess.target.target.options.no_integrated_as;
|
||||
|
||||
for output_type in sess.opts.output_types.keys() {
|
||||
match *output_type {
|
||||
OutputType::Bitcode => { modules_config.emit_bc = true; }
|
||||
OutputType::LlvmAssembly => { modules_config.emit_ir = true; }
|
||||
@ -976,7 +991,6 @@ pub fn start_async_translation(tcx: TyCtxt,
|
||||
metadata,
|
||||
windows_subsystem,
|
||||
linker_info,
|
||||
no_integrated_as,
|
||||
crate_info,
|
||||
|
||||
time_graph,
|
||||
@ -1389,6 +1403,18 @@ fn start_executing_work(tcx: TyCtxt,
|
||||
let wasm_import_memory =
|
||||
attr::contains_name(&tcx.hir.krate().attrs, "wasm_import_memory");
|
||||
|
||||
let assembler_cmd = if modules_config.no_integrated_as {
|
||||
// HACK: currently we use linker (gcc) as our assembler
|
||||
let (name, mut cmd, _) = get_linker(sess);
|
||||
cmd.args(&sess.target.target.options.asm_args);
|
||||
Some(Arc::new(AssemblerCommand {
|
||||
name,
|
||||
cmd,
|
||||
}))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let cgcx = CodegenContext {
|
||||
crate_types: sess.crate_types.borrow().clone(),
|
||||
each_linked_rlib_for_lto,
|
||||
@ -1428,6 +1454,7 @@ fn start_executing_work(tcx: TyCtxt,
|
||||
binaryen_linker: tcx.sess.linker_flavor() == LinkerFlavor::Binaryen,
|
||||
debuginfo: tcx.sess.opts.debuginfo,
|
||||
wasm_import_memory: wasm_import_memory,
|
||||
assembler_cmd,
|
||||
};
|
||||
|
||||
// This is the "main loop" of parallel work happening for parallel codegen.
|
||||
@ -1936,15 +1963,14 @@ fn spawn_work(cgcx: CodegenContext, work: WorkItem) {
|
||||
});
|
||||
}
|
||||
|
||||
pub fn run_assembler(sess: &Session, outputs: &OutputFilenames) {
|
||||
let (pname, mut cmd, _) = get_linker(sess);
|
||||
pub fn run_assembler(cgcx: &CodegenContext, handler: &Handler, assembly: &Path, object: &Path) {
|
||||
let assembler = cgcx.assembler_cmd
|
||||
.as_ref()
|
||||
.expect("cgcx.assembler_cmd is missing?");
|
||||
|
||||
for arg in &sess.target.target.options.asm_args {
|
||||
cmd.arg(arg);
|
||||
}
|
||||
|
||||
cmd.arg("-c").arg("-o").arg(&outputs.path(OutputType::Object))
|
||||
.arg(&outputs.temp_path(OutputType::Assembly, None));
|
||||
let pname = &assembler.name;
|
||||
let mut cmd = assembler.cmd.clone();
|
||||
cmd.arg("-c").arg("-o").arg(object).arg(assembly);
|
||||
debug!("{:?}", cmd);
|
||||
|
||||
match cmd.output() {
|
||||
@ -1953,18 +1979,18 @@ pub fn run_assembler(sess: &Session, outputs: &OutputFilenames) {
|
||||
let mut note = prog.stderr.clone();
|
||||
note.extend_from_slice(&prog.stdout);
|
||||
|
||||
sess.struct_err(&format!("linking with `{}` failed: {}",
|
||||
pname.display(),
|
||||
prog.status))
|
||||
handler.struct_err(&format!("linking with `{}` failed: {}",
|
||||
pname.display(),
|
||||
prog.status))
|
||||
.note(&format!("{:?}", &cmd))
|
||||
.note(str::from_utf8(¬e[..]).unwrap())
|
||||
.emit();
|
||||
sess.abort_if_errors();
|
||||
handler.abort_if_errors();
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
sess.err(&format!("could not exec the linker `{}`: {}", pname.display(), e));
|
||||
sess.abort_if_errors();
|
||||
handler.err(&format!("could not exec the linker `{}`: {}", pname.display(), e));
|
||||
handler.abort_if_errors();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2138,7 +2164,6 @@ pub struct OngoingCrateTranslation {
|
||||
metadata: EncodedMetadata,
|
||||
windows_subsystem: Option<String>,
|
||||
linker_info: LinkerInfo,
|
||||
no_integrated_as: bool,
|
||||
crate_info: CrateInfo,
|
||||
time_graph: Option<TimeGraph>,
|
||||
coordinator_send: Sender<Box<Any + Send>>,
|
||||
@ -2194,26 +2219,6 @@ impl OngoingCrateTranslation {
|
||||
metadata_module: compiled_modules.metadata_module,
|
||||
};
|
||||
|
||||
if self.no_integrated_as {
|
||||
run_assembler(sess, &self.output_filenames);
|
||||
|
||||
// HACK the linker expects the object file to be named foo.0.o but
|
||||
// `run_assembler` produces an object named just foo.o. Rename it if we
|
||||
// are going to build an executable
|
||||
if sess.opts.output_types.contains_key(&OutputType::Exe) {
|
||||
let f = self.output_filenames.path(OutputType::Object);
|
||||
rename_or_copy_remove(&f,
|
||||
f.with_file_name(format!("{}.0.o",
|
||||
f.file_stem().unwrap().to_string_lossy()))).unwrap();
|
||||
}
|
||||
|
||||
// Remove assembly source, unless --save-temps was specified
|
||||
if !sess.opts.cg.save_temps {
|
||||
fs::remove_file(&self.output_filenames
|
||||
.temp_path(OutputType::Assembly, None)).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
trans
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user