| //! The AOT driver uses [`cranelift_object`] to write object files suitable for linking into a | |
| //! standalone executable. | |
| use std::convert::Infallible; | |
| use std::fs::File; | |
| use std::io::BufWriter; | |
| use std::path::PathBuf; | |
| use std::sync::Arc; | |
| use std::time::Instant; | |
| use cranelift_object::{ObjectBuilder, ObjectModule}; | |
| use rustc_ast::expand::allocator::AllocatorMethod; | |
| use rustc_codegen_ssa::back::lto::ThinModule; | |
| use rustc_codegen_ssa::back::write::{ | |
| CodegenContext, FatLtoInput, ModuleConfig, SharedEmitter, TargetMachineFactoryFn, ThinLtoInput, | |
| }; | |
| use rustc_codegen_ssa::traits::{ExtraBackendMethods, WriteBackendMethods}; | |
| use rustc_codegen_ssa::{CompiledModule, ModuleCodegen, ModuleKind}; | |
| use rustc_data_structures::profiling::SelfProfilerRef; | |
| use rustc_errors::DiagCtxt; | |
| use rustc_hir::attrs::Linkage as RLinkage; | |
| use rustc_middle::dep_graph::WorkProduct; | |
| use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; | |
| use rustc_middle::mono::{MonoItem, MonoItemData, Visibility}; | |
| use rustc_session::Session; | |
| use rustc_session::config::{OptLevel, OutputFilenames, OutputType}; | |
| use rustc_span::Symbol; | |
| use crate::base::CodegenedFunction; | |
| use crate::debuginfo::TypeDebugContext; | |
| use crate::global_asm::{GlobalAsmConfig, GlobalAsmContext}; | |
| use crate::prelude::*; | |
| use crate::unwind_module::UnwindModule; | |
| pub(crate) struct AotModule { | |
| producer: String, | |
| global_asm_config: GlobalAsmConfig, | |
| module: UnwindModule<ObjectModule>, | |
| debug_context: Option<DebugContext>, | |
| codegened_functions: Vec<CodegenedFunction>, | |
| global_asm: String, | |
| } | |
| fn make_module(tcx: TyCtxt<'_>, cgu_name: &str) -> AotModule { | |
| let isa = crate::build_isa(tcx.sess, false); | |
| let mut builder = ObjectBuilder::new( | |
| isa, | |
| cgu_name.to_owned() + ".o", | |
| cranelift_module::default_libcall_names(), | |
| ) | |
| .unwrap(); | |
| // Disable function sections by default on MSVC as it causes significant slowdowns with link.exe. | |
| // Maybe link.exe has exponential behavior when there are many sections with the same name? Also | |
| // explicitly disable it on MinGW as rustc already disables it by default on MinGW and as such | |
| // isn't tested. If rustc enables it in the future on MinGW, we can re-enable it too once it has | |
| // been on MinGW. | |
| let default_function_sections = | |
| tcx.sess.target.function_sections && !tcx.sess.target.is_like_windows; | |
| builder.per_function_section( | |
| tcx.sess.opts.unstable_opts.function_sections.unwrap_or(default_function_sections), | |
| ); | |
| let module = UnwindModule::new(ObjectModule::new(builder), true); | |
| let producer = crate::debuginfo::producer(tcx.sess); | |
| let global_asm_config = GlobalAsmConfig::new(tcx.sess); | |
| let debug_context = DebugContext::new(tcx, module.isa(), false, cgu_name); | |
| let codegened_functions = vec![]; | |
| let global_asm = String::new(); | |
| AotModule { | |
| producer, | |
| global_asm_config, | |
| module, | |
| debug_context, | |
| codegened_functions, | |
| global_asm, | |
| } | |
| } | |
| fn emit_module( | |
| output_filenames: &OutputFilenames, | |
| prof: &SelfProfilerRef, | |
| module: UnwindModule<ObjectModule>, | |
| debug: Option<DebugContext>, | |
| kind: ModuleKind, | |
| name: String, | |
| global_asm_object: Option<PathBuf>, | |
| producer_str: &str, | |
| ) -> Result<CompiledModule, String> { | |
| let mut product = module.finish(); | |
| if let Some(mut debug) = debug { | |
| debug.emit(&mut product); | |
| } | |
| if product.object.format() == cranelift_object::object::BinaryFormat::Elf { | |
| let comment_section = product.object.add_section( | |
| Vec::new(), | |
| b".comment".to_vec(), | |
| cranelift_object::object::SectionKind::OtherString, | |
| ); | |
| let mut producer = vec![0]; | |
| producer.extend(producer_str.as_bytes()); | |
| producer.push(0); | |
| product.object.set_section_data(comment_section, producer, 1); | |
| } | |
| let tmp_file = output_filenames.temp_path_for_cgu(OutputType::Object, &name); | |
| let file = match File::create(&tmp_file) { | |
| Ok(file) => file, | |
| Err(err) => return Err(format!("error creating object file: {}", err)), | |
| }; | |
| let mut file = BufWriter::new(file); | |
| if let Err(err) = product.object.write_stream(&mut file) { | |
| return Err(format!("error writing object file: {}", err)); | |
| } | |
| let file = match file.into_inner() { | |
| Ok(file) => file, | |
| Err(err) => return Err(format!("error writing object file: {}", err)), | |
| }; | |
| if prof.enabled() { | |
| prof.artifact_size( | |
| "object_file", | |
| tmp_file.file_name().unwrap().to_string_lossy(), | |
| file.metadata().unwrap().len(), | |
| ); | |
| } | |
| Ok(CompiledModule { | |
| name, | |
| kind, | |
| object: Some(tmp_file), | |
| global_asm_object, | |
| dwarf_object: None, | |
| bytecode: None, | |
| assembly: None, | |
| llvm_ir: None, | |
| links_from_incr_cache: Vec::new(), | |
| }) | |
| } | |
| fn codegen_cgu(tcx: TyCtxt<'_>, cgu_name: Symbol) -> AotModule { | |
| let _timer = tcx.prof.generic_activity_with_arg("codegen cgu", cgu_name.as_str()); | |
| let cgu = tcx.codegen_unit(cgu_name); | |
| let mono_items = cgu.items_in_deterministic_order(tcx); | |
| let mut module = make_module(tcx, cgu_name.as_str()); | |
| let mut type_dbg = TypeDebugContext::default(); | |
| super::predefine_mono_items(tcx, &mut module.module, &mono_items); | |
| for (mono_item, item_data) in mono_items { | |
| match mono_item { | |
| MonoItem::Fn(instance) => { | |
| let flags = tcx.codegen_instance_attrs(instance.def).flags; | |
| if flags.contains(CodegenFnAttrFlags::NAKED) { | |
| rustc_codegen_ssa::mir::naked_asm::codegen_naked_asm( | |
| &mut GlobalAsmContext { tcx, global_asm: &mut module.global_asm }, | |
| instance, | |
| MonoItemData { | |
| linkage: RLinkage::External, | |
| visibility: if item_data.linkage == RLinkage::Internal { | |
| Visibility::Hidden | |
| } else { | |
| item_data.visibility | |
| }, | |
| ..item_data | |
| }, | |
| ); | |
| continue; | |
| } | |
| let codegened_function = crate::base::codegen_fn( | |
| tcx, | |
| cgu.name(), | |
| module.debug_context.as_mut(), | |
| &mut type_dbg, | |
| Function::new(), | |
| &mut module.module, | |
| instance, | |
| ); | |
| module.codegened_functions.push(codegened_function); | |
| } | |
| MonoItem::Static(def_id) => { | |
| let data_id = crate::constant::codegen_static(tcx, &mut module.module, def_id); | |
| if let Some(debug_context) = module.debug_context.as_mut() { | |
| debug_context.define_static(tcx, &mut type_dbg, def_id, data_id); | |
| } | |
| } | |
| MonoItem::GlobalAsm(item_id) => { | |
| rustc_codegen_ssa::base::codegen_global_asm( | |
| &mut GlobalAsmContext { tcx, global_asm: &mut module.global_asm }, | |
| item_id, | |
| ); | |
| } | |
| } | |
| } | |
| crate::main_shim::maybe_create_entry_wrapper(tcx, &mut module.module, false, cgu.is_primary()); | |
| module | |
| } | |
| fn compile_cgu( | |
| prof: &SelfProfilerRef, | |
| output_filenames: &OutputFilenames, | |
| should_write_ir: bool, | |
| mut aot_module: AotModule, | |
| cgu_name: String, | |
| kind: ModuleKind, | |
| ) -> Result<CompiledModule, String> { | |
| prof.generic_activity_with_arg("compile functions", &*cgu_name).run(|| { | |
| cranelift_codegen::timing::set_thread_profiler(Box::new(super::MeasuremeProfiler( | |
| prof.clone(), | |
| ))); | |
| let mut cached_context = Context::new(); | |
| for codegened_func in aot_module.codegened_functions { | |
| crate::base::compile_fn( | |
| &prof, | |
| &output_filenames, | |
| should_write_ir, | |
| &mut cached_context, | |
| &mut aot_module.module, | |
| aot_module.debug_context.as_mut(), | |
| &mut aot_module.global_asm, | |
| codegened_func, | |
| ); | |
| } | |
| }); | |
| let global_asm_object_file = | |
| prof.generic_activity_with_arg("compile assembly", &*cgu_name).run(|| { | |
| if aot_module.global_asm.is_empty() { | |
| return Ok::<_, String>(None); | |
| } | |
| let global_asm_object_file = output_filenames.temp_path_ext_for_cgu("asm.o", &cgu_name); | |
| crate::global_asm::compile_global_asm( | |
| &aot_module.global_asm_config, | |
| aot_module.global_asm, | |
| &global_asm_object_file, | |
| )?; | |
| Ok(Some(global_asm_object_file)) | |
| })?; | |
| prof.generic_activity_with_arg("write object file", &*cgu_name).run(|| { | |
| emit_module( | |
| output_filenames, | |
| prof, | |
| aot_module.module, | |
| aot_module.debug_context, | |
| kind, | |
| cgu_name.clone(), | |
| global_asm_object_file, | |
| &aot_module.producer, | |
| ) | |
| }) | |
| } | |
| #[derive(Copy, Clone)] | |
| pub(crate) struct AotDriver; | |
| impl ExtraBackendMethods for AotDriver { | |
| type Module = AotModule; | |
| fn codegen_allocator<'tcx>( | |
| &self, | |
| tcx: TyCtxt<'tcx>, | |
| module_name: &str, | |
| methods: &[AllocatorMethod], | |
| ) -> Self::Module { | |
| let mut allocator_module = make_module(tcx, module_name); | |
| crate::allocator::codegen(tcx, &mut allocator_module.module, methods); | |
| allocator_module | |
| } | |
| fn compile_codegen_unit( | |
| &self, | |
| tcx: TyCtxt<'_>, | |
| cgu_name: Symbol, | |
| ) -> (ModuleCodegen<Self::Module>, u64) { | |
| let start_time = Instant::now(); | |
| let dep_node = tcx.codegen_unit(cgu_name).codegen_dep_node(tcx); | |
| let (module, _) = tcx.dep_graph.with_task( | |
| dep_node, | |
| tcx, | |
| || { | |
| let aot_module = codegen_cgu(tcx, cgu_name); | |
| ModuleCodegen::new_regular(cgu_name.as_str().to_owned(), aot_module) | |
| }, | |
| Some(rustc_middle::dep_graph::hash_result), | |
| ); | |
| let time_to_codegen = start_time.elapsed(); | |
| // We assume that the cost to run LLVM on a CGU is proportional to | |
| // the time we needed for codegenning it. | |
| let cost = time_to_codegen.as_nanos() as u64; | |
| (module, cost) | |
| } | |
| } | |
| impl WriteBackendMethods for AotDriver { | |
| type Module = AotModule; | |
| type TargetMachine = (); | |
| type ModuleBuffer = Infallible; | |
| type ThinData = Infallible; | |
| fn target_machine_factory( | |
| &self, | |
| _sess: &Session, | |
| _opt_level: OptLevel, | |
| _target_features: &[String], | |
| ) -> TargetMachineFactoryFn<Self> { | |
| Arc::new(|_, _| ()) | |
| } | |
| fn optimize_and_codegen_fat_lto( | |
| _sess: &Session, | |
| _cgcx: &CodegenContext, | |
| _shared_emitter: &SharedEmitter, | |
| _tm_factory: TargetMachineFactoryFn<Self>, | |
| _exported_symbols_for_lto: &[String], | |
| _each_linked_rlib_for_lto: &[PathBuf], | |
| _modules: Vec<FatLtoInput<Self>>, | |
| ) -> CompiledModule { | |
| unreachable!() | |
| } | |
| fn run_thin_lto( | |
| _cgcx: &CodegenContext, | |
| _prof: &SelfProfilerRef, | |
| _dcx: rustc_errors::DiagCtxtHandle<'_>, | |
| _exported_symbols_for_lto: &[String], | |
| _each_linked_rlib_for_lto: &[PathBuf], | |
| _modules: Vec<ThinLtoInput<Self>>, | |
| ) -> (Vec<ThinModule<Self>>, Vec<WorkProduct>) { | |
| unreachable!() | |
| } | |
| fn optimize( | |
| _cgcx: &CodegenContext, | |
| _prof: &SelfProfilerRef, | |
| _shared_emitter: &SharedEmitter, | |
| _module: &mut ModuleCodegen<Self::Module>, | |
| _config: &ModuleConfig, | |
| ) { | |
| } | |
| fn optimize_and_codegen_thin( | |
| _cgcx: &CodegenContext, | |
| _prof: &SelfProfilerRef, | |
| _shared_emitter: &SharedEmitter, | |
| _tm_factory: TargetMachineFactoryFn<Self>, | |
| _thin: ThinModule<Self>, | |
| ) -> CompiledModule { | |
| unreachable!() | |
| } | |
| fn codegen( | |
| cgcx: &CodegenContext, | |
| prof: &SelfProfilerRef, | |
| shared_emitter: &SharedEmitter, | |
| module: ModuleCodegen<Self::Module>, | |
| config: &ModuleConfig, | |
| ) -> CompiledModule { | |
| compile_cgu( | |
| prof, | |
| &cgcx.output_filenames, | |
| config.emit_ir, | |
| module.module_llvm, | |
| module.name, | |
| module.kind, | |
| ) | |
| .unwrap_or_else(|err| { | |
| let dcx = DiagCtxt::new(Box::new(shared_emitter.clone())); | |
| dcx.handle().fatal(err) | |
| }) | |
| } | |
| fn serialize_module(_module: Self::Module, _is_thin: bool) -> Self::ModuleBuffer { | |
| unreachable!() | |
| } | |
| } |