-use super::command::Command;
-use super::link::{self, get_linker, remove};
+use super::link::{self, remove};
use super::linker::LinkerInfo;
use super::lto::{self, SerializedModule};
use super::symbol_export::symbol_name_for_instance_in_crate;
use crate::traits::*;
use jobserver::{Acquired, Client};
-use rustc::dep_graph::{WorkProduct, WorkProductFileKind, WorkProductId};
-use rustc::middle::cstore::EncodedMetadata;
-use rustc::middle::exported_symbols::SymbolExportLevel;
-use rustc::session::config::{
- self, Lto, OutputFilenames, OutputType, Passes, Sanitizer, SwitchWithOptPath,
-};
-use rustc::session::Session;
-use rustc::ty::TyCtxt;
use rustc_ast::attr;
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::profiling::SelfProfilerRef;
use rustc_incremental::{
copy_cgu_workproducts_to_incr_comp_cache_dir, in_incr_comp_dir, in_incr_comp_dir_sess,
};
+use rustc_middle::dep_graph::{WorkProduct, WorkProductFileKind, WorkProductId};
+use rustc_middle::middle::cstore::EncodedMetadata;
+use rustc_middle::middle::exported_symbols::SymbolExportLevel;
+use rustc_middle::ty::TyCtxt;
use rustc_session::cgu_reuse_tracker::CguReuseTracker;
+use rustc_session::config::{
+ self, Lto, OutputFilenames, OutputType, Passes, Sanitizer, SwitchWithOptPath,
+};
+use rustc_session::Session;
use rustc_span::hygiene::ExpnId;
use rustc_span::source_map::SourceMap;
use rustc_span::symbol::{sym, Symbol};
const PRE_LTO_BC_EXT: &str = "pre-lto.bc";
+/// What kind of object file to emit.
+#[derive(Clone, Copy, PartialEq)]
+pub enum EmitObj {
+ // No object file.
+ None,
+
+ // Just uncompressed llvm bitcode. Provides easy compatibility with
+ // emscripten's ecc compiler, when used as the linker.
+ Bitcode,
+
+ // Object code, possibly augmented with a bitcode section.
+ ObjectCode(BitcodeSection),
+}
+
+/// What kind of llvm bitcode section to embed in an object file.
+#[derive(Clone, Copy, PartialEq)]
+pub enum BitcodeSection {
+ // No bitcode section.
+ None,
+
+ // An empty bitcode section (to placate tools such as the iOS linker that
+ // require this section even if they don't use it).
+ Marker,
+
+ // A full, uncompressed bitcode section.
+ Full,
+}
+
/// Module-specific configuration for `optimize_and_codegen`.
pub struct ModuleConfig {
/// Names of additional optimization passes to run.
pub emit_no_opt_bc: bool,
pub emit_bc: bool,
pub emit_bc_compressed: bool,
- pub emit_lto_bc: bool,
pub emit_ir: bool,
pub emit_asm: bool,
- pub emit_obj: bool,
+ pub emit_obj: EmitObj,
+
// Miscellaneous flags. These are mostly copied from command-line
// options.
pub verify_llvm_ir: bool,
pub vectorize_slp: bool,
pub merge_functions: bool,
pub inline_threshold: Option<usize>,
- pub new_llvm_pass_manager: Option<bool>,
- // Instead of creating an object file by doing LLVM codegen, just
- // make the object file bitcode. Provides easy compatibility with
- // emscripten's ecc compiler, when used as the linker.
- pub obj_is_bitcode: bool,
- pub no_integrated_as: bool,
- pub embed_bitcode: bool,
- pub embed_bitcode_marker: bool,
+ pub new_llvm_pass_manager: bool,
}
impl ModuleConfig {
- fn new(passes: Vec<String>) -> ModuleConfig {
- ModuleConfig {
- passes,
- opt_level: None,
- opt_size: None,
-
- pgo_gen: SwitchWithOptPath::Disabled,
- pgo_use: None,
-
- sanitizer: None,
- sanitizer_recover: Default::default(),
- sanitizer_memory_track_origins: 0,
-
- emit_no_opt_bc: false,
- emit_pre_lto_bc: false,
- emit_bc: false,
- emit_bc_compressed: false,
- emit_lto_bc: false,
- emit_ir: false,
- emit_asm: false,
- emit_obj: false,
- obj_is_bitcode: false,
- embed_bitcode: false,
- embed_bitcode_marker: false,
- no_integrated_as: false,
-
- verify_llvm_ir: false,
- no_prepopulate_passes: false,
- no_builtins: false,
- time_module: true,
- vectorize_loop: false,
- vectorize_slp: false,
- merge_functions: false,
- inline_threshold: None,
- new_llvm_pass_manager: None,
+ fn new(kind: ModuleKind, sess: &Session, no_builtins: bool) -> ModuleConfig {
+ // If it's a regular module, use `$regular`, otherwise use `$other`.
+ // `$regular` and `$other` are evaluated lazily.
+ macro_rules! if_regular {
+ ($regular: expr, $other: expr) => {
+ if let ModuleKind::Regular = kind { $regular } else { $other }
+ };
}
- }
- fn set_flags(&mut self, sess: &Session, no_builtins: bool) {
- self.verify_llvm_ir = sess.verify_llvm_ir();
- self.no_prepopulate_passes = sess.opts.cg.no_prepopulate_passes;
- self.no_builtins = no_builtins || sess.target.target.options.no_builtins;
- self.inline_threshold = sess.opts.cg.inline_threshold;
- self.new_llvm_pass_manager = sess.opts.debugging_opts.new_llvm_pass_manager;
- self.obj_is_bitcode =
- sess.target.target.options.obj_is_bitcode || sess.opts.cg.linker_plugin_lto.enabled();
- let embed_bitcode =
- sess.target.target.options.embed_bitcode || sess.opts.debugging_opts.embed_bitcode;
- if embed_bitcode {
+ let opt_level_and_size = if_regular!(Some(sess.opts.optimize), None);
+
+ let save_temps = sess.opts.cg.save_temps;
+
+ let should_emit_obj = sess.opts.output_types.contains_key(&OutputType::Exe)
+ || match kind {
+ ModuleKind::Regular => sess.opts.output_types.contains_key(&OutputType::Object),
+ ModuleKind::Allocator => false,
+ ModuleKind::Metadata => sess.opts.output_types.contains_key(&OutputType::Metadata),
+ };
+
+ let emit_obj = if !should_emit_obj {
+ EmitObj::None
+ } else if sess.target.target.options.obj_is_bitcode
+ || sess.opts.cg.linker_plugin_lto.enabled()
+ {
+ EmitObj::Bitcode
+ } else if sess.opts.debugging_opts.embed_bitcode {
match sess.opts.optimize {
config::OptLevel::No | config::OptLevel::Less => {
- self.embed_bitcode_marker = embed_bitcode;
+ EmitObj::ObjectCode(BitcodeSection::Marker)
}
- _ => self.embed_bitcode = embed_bitcode,
- }
- }
-
- // Copy what clang does by turning on loop vectorization at O2 and
- // slp vectorization at O3. Otherwise configure other optimization aspects
- // of this pass manager builder.
- self.vectorize_loop = !sess.opts.cg.no_vectorize_loops
- && (sess.opts.optimize == config::OptLevel::Default
- || sess.opts.optimize == config::OptLevel::Aggressive);
-
- self.vectorize_slp =
- !sess.opts.cg.no_vectorize_slp && sess.opts.optimize == config::OptLevel::Aggressive;
-
- // Some targets (namely, NVPTX) interact badly with the MergeFunctions
- // pass. This is because MergeFunctions can generate new function calls
- // which may interfere with the target calling convention; e.g. for the
- // NVPTX target, PTX kernels should not call other PTX kernels.
- // MergeFunctions can also be configured to generate aliases instead,
- // but aliases are not supported by some backends (again, NVPTX).
- // Therefore, allow targets to opt out of the MergeFunctions pass,
- // but otherwise keep the pass enabled (at O2 and O3) since it can be
- // useful for reducing code size.
- self.merge_functions = match sess
- .opts
- .debugging_opts
- .merge_functions
- .unwrap_or(sess.target.target.options.merge_functions)
- {
- MergeFunctions::Disabled => false,
- MergeFunctions::Trampolines | MergeFunctions::Aliases => {
- sess.opts.optimize == config::OptLevel::Default
- || sess.opts.optimize == config::OptLevel::Aggressive
+ _ => EmitObj::ObjectCode(BitcodeSection::Full),
}
+ } else {
+ EmitObj::ObjectCode(BitcodeSection::None)
};
+
+ ModuleConfig {
+ passes: if_regular!(
+ {
+ let mut passes = sess.opts.cg.passes.clone();
+ if sess.opts.debugging_opts.profile {
+ passes.push("insert-gcov-profiling".to_owned());
+ }
+ passes
+ },
+ vec![]
+ ),
+
+ opt_level: opt_level_and_size,
+ opt_size: opt_level_and_size,
+
+ pgo_gen: if_regular!(
+ sess.opts.cg.profile_generate.clone(),
+ SwitchWithOptPath::Disabled
+ ),
+ pgo_use: if_regular!(sess.opts.cg.profile_use.clone(), None),
+
+ sanitizer: if_regular!(sess.opts.debugging_opts.sanitizer.clone(), None),
+ sanitizer_recover: if_regular!(
+ sess.opts.debugging_opts.sanitizer_recover.clone(),
+ vec![]
+ ),
+ sanitizer_memory_track_origins: if_regular!(
+ sess.opts.debugging_opts.sanitizer_memory_track_origins,
+ 0
+ ),
+
+ emit_pre_lto_bc: if_regular!(
+ save_temps || need_pre_lto_bitcode_for_incr_comp(sess),
+ false
+ ),
+ emit_no_opt_bc: if_regular!(save_temps, false),
+ emit_bc: if_regular!(
+ save_temps || sess.opts.output_types.contains_key(&OutputType::Bitcode),
+ save_temps
+ ),
+ emit_bc_compressed: match kind {
+ ModuleKind::Regular | ModuleKind::Allocator => {
+ // Emit compressed bitcode files for the crate if we're
+ // emitting an rlib. Whenever an rlib is created, the
+ // bitcode is inserted into the archive in order to allow
+ // LTO against it.
+ need_crate_bitcode_for_rlib(sess)
+ }
+ ModuleKind::Metadata => false,
+ },
+ emit_ir: if_regular!(
+ sess.opts.output_types.contains_key(&OutputType::LlvmAssembly),
+ false
+ ),
+ emit_asm: if_regular!(
+ sess.opts.output_types.contains_key(&OutputType::Assembly),
+ false
+ ),
+ emit_obj,
+
+ verify_llvm_ir: sess.verify_llvm_ir(),
+ no_prepopulate_passes: sess.opts.cg.no_prepopulate_passes,
+ no_builtins: no_builtins || sess.target.target.options.no_builtins,
+
+ // Exclude metadata and allocator modules from time_passes output,
+ // since they throw off the "LLVM passes" measurement.
+ time_module: if_regular!(true, false),
+
+ // Copy what clang does by turning on loop vectorization at O2 and
+ // slp vectorization at O3.
+ vectorize_loop: !sess.opts.cg.no_vectorize_loops
+ && (sess.opts.optimize == config::OptLevel::Default
+ || sess.opts.optimize == config::OptLevel::Aggressive),
+ vectorize_slp: !sess.opts.cg.no_vectorize_slp
+ && sess.opts.optimize == config::OptLevel::Aggressive,
+
+ // Some targets (namely, NVPTX) interact badly with the
+ // MergeFunctions pass. This is because MergeFunctions can generate
+ // new function calls which may interfere with the target calling
+ // convention; e.g. for the NVPTX target, PTX kernels should not
+ // call other PTX kernels. MergeFunctions can also be configured to
+ // generate aliases instead, but aliases are not supported by some
+ // backends (again, NVPTX). Therefore, allow targets to opt out of
+ // the MergeFunctions pass, but otherwise keep the pass enabled (at
+ // O2 and O3) since it can be useful for reducing code size.
+ merge_functions: match sess
+ .opts
+ .debugging_opts
+ .merge_functions
+ .unwrap_or(sess.target.target.options.merge_functions)
+ {
+ MergeFunctions::Disabled => false,
+ MergeFunctions::Trampolines | MergeFunctions::Aliases => {
+ sess.opts.optimize == config::OptLevel::Default
+ || sess.opts.optimize == config::OptLevel::Aggressive
+ }
+ },
+
+ inline_threshold: sess.opts.cg.inline_threshold,
+ new_llvm_pass_manager: sess.opts.debugging_opts.new_llvm_pass_manager,
+ }
}
pub fn bitcode_needed(&self) -> bool {
- self.emit_bc || self.obj_is_bitcode || self.emit_bc_compressed || self.embed_bitcode
+ self.emit_bc
+ || self.emit_bc_compressed
+ || self.emit_obj == EmitObj::Bitcode
+ || self.emit_obj == EmitObj::ObjectCode(BitcodeSection::Full)
}
}
-/// Assembler name and command used by codegen when no_integrated_as is enabled
-pub struct AssemblerCommand {
- name: PathBuf,
- cmd: Command,
-}
-
// HACK(eddyb) work around `#[derive]` producing wrong bounds for `Clone`.
pub struct TargetMachineFactory<B: WriteBackendMethods>(
pub Arc<dyn Fn() -> Result<B::TargetMachine, String> + Send + Sync>,
pub cgu_reuse_tracker: CguReuseTracker,
// Channel back to the main control thread to send messages to
pub coordinator_send: Sender<Box<dyn Any + Send>>,
- // The assembler command if no_integrated_as option is enabled, None otherwise
- pub assembler_cmd: Option<Arc<AssemblerCommand>>,
}
impl<B: WriteBackendMethods> CodegenContext<B> {
B::run_thin_lto(cgcx, needs_thin_lto, import_only_modules).unwrap_or_else(|e| e.raise())
};
- let result = lto_modules
+ lto_modules
.into_iter()
.map(|module| {
let cost = module.cost();
0,
)
}))
- .collect();
-
- result
+ .collect()
}
pub struct CompiledModules {
let crate_name = tcx.crate_name(LOCAL_CRATE);
let crate_hash = tcx.crate_hash(LOCAL_CRATE);
- let no_builtins = attr::contains_name(&tcx.hir().krate().attrs, sym::no_builtins);
+ let no_builtins = attr::contains_name(&tcx.hir().krate().item.attrs, sym::no_builtins);
let subsystem =
- attr::first_attr_value_str_by_name(&tcx.hir().krate().attrs, sym::windows_subsystem);
+ attr::first_attr_value_str_by_name(&tcx.hir().krate().item.attrs, sym::windows_subsystem);
let windows_subsystem = subsystem.map(|subsystem| {
if subsystem != sym::windows && subsystem != sym::console {
tcx.sess.fatal(&format!(
let linker_info = LinkerInfo::new(tcx);
let crate_info = CrateInfo::new(tcx);
- // 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![]);
- let mut allocator_config = ModuleConfig::new(vec![]);
-
- if sess.opts.debugging_opts.profile {
- modules_config.passes.push("insert-gcov-profiling".to_owned())
- }
-
- modules_config.pgo_gen = sess.opts.cg.profile_generate.clone();
- modules_config.pgo_use = sess.opts.cg.profile_use.clone();
- modules_config.sanitizer = sess.opts.debugging_opts.sanitizer.clone();
- modules_config.sanitizer_recover = sess.opts.debugging_opts.sanitizer_recover.clone();
- modules_config.sanitizer_memory_track_origins =
- sess.opts.debugging_opts.sanitizer_memory_track_origins;
- modules_config.opt_level = Some(sess.opts.optimize);
- modules_config.opt_size = Some(sess.opts.optimize);
-
- // Save all versions of the bytecode if we're saving our temporaries.
- if sess.opts.cg.save_temps {
- modules_config.emit_no_opt_bc = true;
- modules_config.emit_pre_lto_bc = true;
- modules_config.emit_bc = true;
- modules_config.emit_lto_bc = true;
- metadata_config.emit_bc = true;
- allocator_config.emit_bc = true;
- }
-
- // Emit compressed bitcode files for the crate if we're emitting an rlib.
- // Whenever an rlib is created, the bitcode is inserted into the archive in
- // order to allow LTO against it.
- if need_crate_bitcode_for_rlib(sess) {
- modules_config.emit_bc_compressed = true;
- allocator_config.emit_bc_compressed = true;
- }
-
- modules_config.emit_pre_lto_bc = need_pre_lto_bitcode_for_incr_comp(sess);
-
- 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;
- }
- OutputType::Assembly => {
- modules_config.emit_asm = true;
- // If we're not using the LLVM assembler, this function
- // could be invoked specially with output_type_assembly, so
- // in this case we still want the metadata object file.
- if !sess.opts.output_types.contains_key(&OutputType::Assembly) {
- metadata_config.emit_obj = true;
- allocator_config.emit_obj = true;
- }
- }
- OutputType::Object => {
- modules_config.emit_obj = true;
- }
- OutputType::Metadata => {
- metadata_config.emit_obj = true;
- }
- OutputType::Exe => {
- modules_config.emit_obj = true;
- metadata_config.emit_obj = true;
- allocator_config.emit_obj = true;
- }
- OutputType::Mir => {}
- OutputType::DepInfo => {}
- }
- }
-
- modules_config.set_flags(sess, no_builtins);
- metadata_config.set_flags(sess, no_builtins);
- allocator_config.set_flags(sess, no_builtins);
-
- // Exclude metadata and allocator modules from time_passes output, since
- // they throw off the "LLVM passes" measurement.
- metadata_config.time_module = false;
- allocator_config.time_module = false;
+ let regular_config = ModuleConfig::new(ModuleKind::Regular, sess, no_builtins);
+ let metadata_config = ModuleConfig::new(ModuleKind::Metadata, sess, no_builtins);
+ let allocator_config = ModuleConfig::new(ModuleKind::Allocator, sess, no_builtins);
let (shared_emitter, shared_emitter_main) = SharedEmitter::new();
let (codegen_worker_send, codegen_worker_receive) = channel();
coordinator_receive,
total_cgus,
sess.jobserver.clone(),
- Arc::new(modules_config),
+ Arc::new(regular_config),
Arc::new(metadata_config),
Arc::new(allocator_config),
coordinator_send.clone(),
}
}
- assert_eq!(object.is_some(), module_config.emit_obj);
+ assert_eq!(object.is_some(), module_config.emit_obj != EmitObj::None);
assert_eq!(bytecode.is_some(), module_config.emit_bc);
assert_eq!(bytecode_compressed.is_some(), module_config.emit_bc_compressed);
coordinator_receive: Receiver<Box<dyn Any + Send>>,
total_cgus: usize,
jobserver: Client,
- modules_config: Arc<ModuleConfig>,
+ regular_config: Arc<ModuleConfig>,
metadata_config: Arc<ModuleConfig>,
allocator_config: Arc<ModuleConfig>,
tx_to_llvm_workers: Sender<Box<dyn Any + Send>>,
each_linked_rlib_for_lto.push((cnum, path.to_path_buf()));
}));
- let assembler_cmd = if modules_config.no_integrated_as {
- // HACK: currently we use linker (gcc) as our assembler
- let (linker, flavor) = link::linker_and_flavor(sess);
-
- let (name, mut cmd) = get_linker(sess, &linker, flavor);
- cmd.args(&sess.target.target.options.asm_args);
- Some(Arc::new(AssemblerCommand { name, cmd }))
- } else {
- None
- };
-
let ol = if tcx.sess.opts.debugging_opts.no_codegen
|| !tcx.sess.opts.output_types.should_codegen()
{
coordinator_send,
diag_emitter: shared_emitter.clone(),
output_filenames: tcx.output_filenames(LOCAL_CRATE),
- regular_module_config: modules_config,
+ regular_module_config: regular_config,
metadata_module_config: metadata_config,
allocator_module_config: allocator_config,
tm_factory: TargetMachineFactory(backend.target_machine_factory(tcx.sess, ol, false)),
target_pointer_width: tcx.sess.target.target.target_pointer_width.clone(),
target_arch: tcx.sess.target.target.arch.clone(),
debuginfo: tcx.sess.opts.debuginfo,
- assembler_cmd,
};
// This is the "main loop" of parallel work happening for parallel codegen.
}
}
-pub const CODEGEN_WORKER_ID: usize = ::std::usize::MAX;
+pub const CODEGEN_WORKER_ID: usize = usize::MAX;
/// `FatalError` is explicitly not `Send`.
#[must_use]
});
}
-pub fn run_assembler<B: ExtraBackendMethods>(
- cgcx: &CodegenContext<B>,
- handler: &Handler,
- assembly: &Path,
- object: &Path,
-) {
- let assembler = cgcx.assembler_cmd.as_ref().expect("cgcx.assembler_cmd is missing?");
-
- let pname = &assembler.name;
- let mut cmd = assembler.cmd.clone();
- cmd.arg("-c").arg("-o").arg(object).arg(assembly);
- debug!("{:?}", cmd);
-
- match cmd.output() {
- Ok(prog) => {
- if !prog.status.success() {
- let mut note = prog.stderr.clone();
- note.extend_from_slice(&prog.stdout);
-
- handler
- .struct_err(&format!(
- "linking with `{}` failed: {}",
- pname.display(),
- prog.status
- ))
- .note(&format!("{:?}", &cmd))
- .note(str::from_utf8(¬e[..]).unwrap())
- .emit();
- handler.abort_if_errors();
- }
- }
- Err(e) => {
- handler.err(&format!("could not exec the linker `{}`: {}", pname.display(), e));
- handler.abort_if_errors();
- }
- }
-}
-
enum SharedEmitterMessage {
Diagnostic(Diagnostic),
InlineAsmError(u32, String),