]> git.lizzy.rs Git - rust.git/blob - src/driver/aot.rs
Rustfmt
[rust.git] / src / driver / aot.rs
1 //! The AOT driver uses [`cranelift_object`] to write object files suitable for linking into a
2 //! standalone executable.
3
4 use std::path::PathBuf;
5
6 use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece};
7 use rustc_codegen_ssa::back::metadata::create_compressed_metadata_file;
8 use rustc_codegen_ssa::{CodegenResults, CompiledModule, CrateInfo, ModuleKind};
9 use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
10 use rustc_metadata::EncodedMetadata;
11 use rustc_middle::dep_graph::{WorkProduct, WorkProductId};
12 use rustc_middle::mir::mono::{CodegenUnit, MonoItem};
13 use rustc_session::cgu_reuse_tracker::CguReuse;
14 use rustc_session::config::{DebugInfo, OutputType};
15 use rustc_session::Session;
16
17 use cranelift_codegen::isa::TargetIsa;
18 use cranelift_object::{ObjectBuilder, ObjectModule};
19
20 use crate::{prelude::*, BackendConfig};
21
22 struct ModuleCodegenResult(CompiledModule, Option<(WorkProductId, WorkProduct)>);
23
24 impl<HCX> HashStable<HCX> for ModuleCodegenResult {
25     fn hash_stable(&self, _: &mut HCX, _: &mut StableHasher) {
26         // do nothing
27     }
28 }
29
30 fn make_module(sess: &Session, isa: Box<dyn TargetIsa>, name: String) -> ObjectModule {
31     let mut builder =
32         ObjectBuilder::new(isa, name + ".o", cranelift_module::default_libcall_names()).unwrap();
33     // Unlike cg_llvm, cg_clif defaults to disabling -Zfunction-sections. For cg_llvm binary size
34     // is important, while cg_clif cares more about compilation times. Enabling -Zfunction-sections
35     // can easily double the amount of time necessary to perform linking.
36     builder.per_function_section(sess.opts.debugging_opts.function_sections.unwrap_or(false));
37     ObjectModule::new(builder)
38 }
39
40 fn emit_module(
41     tcx: TyCtxt<'_>,
42     backend_config: &BackendConfig,
43     name: String,
44     kind: ModuleKind,
45     module: ObjectModule,
46     debug: Option<DebugContext<'_>>,
47     unwind_context: UnwindContext,
48 ) -> ModuleCodegenResult {
49     let mut product = module.finish();
50
51     if let Some(mut debug) = debug {
52         debug.emit(&mut product);
53     }
54
55     unwind_context.emit(&mut product);
56
57     let tmp_file = tcx.output_filenames(()).temp_path(OutputType::Object, Some(&name));
58     let obj = product.object.write().unwrap();
59
60     tcx.sess.prof.artifact_size("object_file", name.clone(), obj.len().try_into().unwrap());
61
62     if let Err(err) = std::fs::write(&tmp_file, obj) {
63         tcx.sess.fatal(&format!("error writing object file: {}", err));
64     }
65
66     let work_product = if backend_config.disable_incr_cache {
67         None
68     } else {
69         rustc_incremental::copy_cgu_workproduct_to_incr_comp_cache_dir(
70             tcx.sess,
71             &name,
72             &Some(tmp_file.clone()),
73         )
74     };
75
76     ModuleCodegenResult(
77         CompiledModule { name, kind, object: Some(tmp_file), dwarf_object: None, bytecode: None },
78         work_product,
79     )
80 }
81
82 fn reuse_workproduct_for_cgu(
83     tcx: TyCtxt<'_>,
84     cgu: &CodegenUnit<'_>,
85     work_products: &mut FxHashMap<WorkProductId, WorkProduct>,
86 ) -> CompiledModule {
87     let mut object = None;
88     let work_product = cgu.work_product(tcx);
89     if let Some(saved_file) = &work_product.saved_file {
90         let obj_out =
91             tcx.output_filenames(()).temp_path(OutputType::Object, Some(cgu.name().as_str()));
92         object = Some(obj_out.clone());
93         let source_file = rustc_incremental::in_incr_comp_dir_sess(&tcx.sess, &saved_file);
94         if let Err(err) = rustc_fs_util::link_or_copy(&source_file, &obj_out) {
95             tcx.sess.err(&format!(
96                 "unable to copy {} to {}: {}",
97                 source_file.display(),
98                 obj_out.display(),
99                 err
100             ));
101         }
102     }
103
104     work_products.insert(cgu.work_product_id(), work_product);
105
106     CompiledModule {
107         name: cgu.name().to_string(),
108         kind: ModuleKind::Regular,
109         object,
110         dwarf_object: None,
111         bytecode: None,
112     }
113 }
114
115 fn module_codegen(
116     tcx: TyCtxt<'_>,
117     (backend_config, cgu_name): (BackendConfig, rustc_span::Symbol),
118 ) -> ModuleCodegenResult {
119     let cgu = tcx.codegen_unit(cgu_name);
120     let mono_items = cgu.items_in_deterministic_order(tcx);
121
122     let isa = crate::build_isa(tcx.sess, &backend_config);
123     let mut module = make_module(tcx.sess, isa, cgu_name.as_str().to_string());
124
125     let mut cx = crate::CodegenCx::new(
126         tcx,
127         backend_config.clone(),
128         module.isa(),
129         tcx.sess.opts.debuginfo != DebugInfo::None,
130         cgu_name,
131     );
132     super::predefine_mono_items(tcx, &mut module, &mono_items);
133     for (mono_item, _) in mono_items {
134         match mono_item {
135             MonoItem::Fn(inst) => {
136                 cx.tcx
137                     .sess
138                     .time("codegen fn", || crate::base::codegen_fn(&mut cx, &mut module, inst));
139             }
140             MonoItem::Static(def_id) => crate::constant::codegen_static(tcx, &mut module, def_id),
141             MonoItem::GlobalAsm(item_id) => {
142                 let item = cx.tcx.hir().item(item_id);
143                 if let rustc_hir::ItemKind::GlobalAsm(asm) = item.kind {
144                     if !asm.options.contains(InlineAsmOptions::ATT_SYNTAX) {
145                         cx.global_asm.push_str("\n.intel_syntax noprefix\n");
146                     } else {
147                         cx.global_asm.push_str("\n.att_syntax\n");
148                     }
149                     for piece in asm.template {
150                         match *piece {
151                             InlineAsmTemplatePiece::String(ref s) => cx.global_asm.push_str(s),
152                             InlineAsmTemplatePiece::Placeholder { .. } => todo!(),
153                         }
154                     }
155                     cx.global_asm.push_str("\n.att_syntax\n\n");
156                 } else {
157                     bug!("Expected GlobalAsm found {:?}", item);
158                 }
159             }
160         }
161     }
162     crate::main_shim::maybe_create_entry_wrapper(
163         tcx,
164         &mut module,
165         &mut cx.unwind_context,
166         false,
167         cgu.is_primary(),
168     );
169
170     let debug_context = cx.debug_context;
171     let unwind_context = cx.unwind_context;
172     let codegen_result = tcx.sess.time("write object file", || {
173         emit_module(
174             tcx,
175             &backend_config,
176             cgu.name().as_str().to_string(),
177             ModuleKind::Regular,
178             module,
179             debug_context,
180             unwind_context,
181         )
182     });
183
184     codegen_global_asm(tcx, cgu.name().as_str(), &cx.global_asm);
185
186     codegen_result
187 }
188
189 pub(crate) fn run_aot(
190     tcx: TyCtxt<'_>,
191     backend_config: BackendConfig,
192     metadata: EncodedMetadata,
193     need_metadata_module: bool,
194 ) -> Box<(CodegenResults, FxHashMap<WorkProductId, WorkProduct>)> {
195     let mut work_products = FxHashMap::default();
196
197     let cgus = if tcx.sess.opts.output_types.should_codegen() {
198         tcx.collect_and_partition_mono_items(()).1
199     } else {
200         // If only `--emit metadata` is used, we shouldn't perform any codegen.
201         // Also `tcx.collect_and_partition_mono_items` may panic in that case.
202         &[]
203     };
204
205     if tcx.dep_graph.is_fully_enabled() {
206         for cgu in &*cgus {
207             tcx.ensure().codegen_unit(cgu.name());
208         }
209     }
210
211     let modules = super::time(tcx, backend_config.display_cg_time, "codegen mono items", || {
212         cgus.iter()
213             .map(|cgu| {
214                 let cgu_reuse = determine_cgu_reuse(tcx, cgu);
215                 tcx.sess.cgu_reuse_tracker.set_actual_reuse(cgu.name().as_str(), cgu_reuse);
216
217                 match cgu_reuse {
218                     _ if backend_config.disable_incr_cache => {}
219                     CguReuse::No => {}
220                     CguReuse::PreLto => {
221                         return reuse_workproduct_for_cgu(tcx, &*cgu, &mut work_products);
222                     }
223                     CguReuse::PostLto => unreachable!(),
224                 }
225
226                 let dep_node = cgu.codegen_dep_node(tcx);
227                 let (ModuleCodegenResult(module, work_product), _) = tcx.dep_graph.with_task(
228                     dep_node,
229                     tcx,
230                     (backend_config.clone(), cgu.name()),
231                     module_codegen,
232                     Some(rustc_middle::dep_graph::hash_result),
233                 );
234
235                 if let Some((id, product)) = work_product {
236                     work_products.insert(id, product);
237                 }
238
239                 module
240             })
241             .collect::<Vec<_>>()
242     });
243
244     tcx.sess.abort_if_errors();
245
246     let isa = crate::build_isa(tcx.sess, &backend_config);
247     let mut allocator_module = make_module(tcx.sess, isa, "allocator_shim".to_string());
248     assert_eq!(pointer_ty(tcx), allocator_module.target_config().pointer_type());
249     let mut allocator_unwind_context = UnwindContext::new(allocator_module.isa(), true);
250     let created_alloc_shim =
251         crate::allocator::codegen(tcx, &mut allocator_module, &mut allocator_unwind_context);
252
253     let allocator_module = if created_alloc_shim {
254         let ModuleCodegenResult(module, work_product) = emit_module(
255             tcx,
256             &backend_config,
257             "allocator_shim".to_string(),
258             ModuleKind::Allocator,
259             allocator_module,
260             None,
261             allocator_unwind_context,
262         );
263         if let Some((id, product)) = work_product {
264             work_products.insert(id, product);
265         }
266         Some(module)
267     } else {
268         None
269     };
270
271     let metadata_module = if need_metadata_module {
272         let _timer = tcx.prof.generic_activity("codegen crate metadata");
273         let (metadata_cgu_name, tmp_file) = tcx.sess.time("write compressed metadata", || {
274             use rustc_middle::mir::mono::CodegenUnitNameBuilder;
275
276             let cgu_name_builder = &mut CodegenUnitNameBuilder::new(tcx);
277             let metadata_cgu_name = cgu_name_builder
278                 .build_cgu_name(LOCAL_CRATE, &["crate"], Some("metadata"))
279                 .as_str()
280                 .to_string();
281
282             let tmp_file =
283                 tcx.output_filenames(()).temp_path(OutputType::Metadata, Some(&metadata_cgu_name));
284
285             let symbol_name = rustc_middle::middle::exported_symbols::metadata_symbol_name(tcx);
286             let obj = create_compressed_metadata_file(tcx.sess, &metadata, &symbol_name);
287
288             if let Err(err) = std::fs::write(&tmp_file, obj) {
289                 tcx.sess.fatal(&format!("error writing metadata object file: {}", err));
290             }
291
292             (metadata_cgu_name, tmp_file)
293         });
294
295         Some(CompiledModule {
296             name: metadata_cgu_name,
297             kind: ModuleKind::Metadata,
298             object: Some(tmp_file),
299             dwarf_object: None,
300             bytecode: None,
301         })
302     } else {
303         None
304     };
305
306     // FIXME handle `-Ctarget-cpu=native`
307     let target_cpu = match tcx.sess.opts.cg.target_cpu {
308         Some(ref name) => name,
309         None => tcx.sess.target.cpu.as_ref(),
310     }
311     .to_owned();
312
313     Box::new((
314         CodegenResults {
315             modules,
316             allocator_module,
317             metadata_module,
318             metadata,
319             crate_info: CrateInfo::new(tcx, target_cpu),
320         },
321         work_products,
322     ))
323 }
324
325 fn codegen_global_asm(tcx: TyCtxt<'_>, cgu_name: &str, global_asm: &str) {
326     use std::io::Write;
327     use std::process::{Command, Stdio};
328
329     if global_asm.is_empty() {
330         return;
331     }
332
333     if cfg!(not(feature = "inline_asm"))
334         || tcx.sess.target.is_like_osx
335         || tcx.sess.target.is_like_windows
336     {
337         if global_asm.contains("__rust_probestack") {
338             return;
339         }
340
341         // FIXME fix linker error on macOS
342         if cfg!(not(feature = "inline_asm")) {
343             tcx.sess.fatal(
344                 "asm! and global_asm! support is disabled while compiling rustc_codegen_cranelift",
345             );
346         } else {
347             tcx.sess.fatal("asm! and global_asm! are not yet supported on macOS and Windows");
348         }
349     }
350
351     let assembler = crate::toolchain::get_toolchain_binary(tcx.sess, "as");
352     let linker = crate::toolchain::get_toolchain_binary(tcx.sess, "ld");
353
354     // Remove all LLVM style comments
355     let global_asm = global_asm
356         .lines()
357         .map(|line| if let Some(index) = line.find("//") { &line[0..index] } else { line })
358         .collect::<Vec<_>>()
359         .join("\n");
360
361     let output_object_file = tcx.output_filenames(()).temp_path(OutputType::Object, Some(cgu_name));
362
363     // Assemble `global_asm`
364     let global_asm_object_file = add_file_stem_postfix(output_object_file.clone(), ".asm");
365     let mut child = Command::new(assembler)
366         .arg("-o")
367         .arg(&global_asm_object_file)
368         .stdin(Stdio::piped())
369         .spawn()
370         .expect("Failed to spawn `as`.");
371     child.stdin.take().unwrap().write_all(global_asm.as_bytes()).unwrap();
372     let status = child.wait().expect("Failed to wait for `as`.");
373     if !status.success() {
374         tcx.sess.fatal(&format!("Failed to assemble `{}`", global_asm));
375     }
376
377     // Link the global asm and main object file together
378     let main_object_file = add_file_stem_postfix(output_object_file.clone(), ".main");
379     std::fs::rename(&output_object_file, &main_object_file).unwrap();
380     let status = Command::new(linker)
381         .arg("-r") // Create a new object file
382         .arg("-o")
383         .arg(output_object_file)
384         .arg(&main_object_file)
385         .arg(&global_asm_object_file)
386         .status()
387         .unwrap();
388     if !status.success() {
389         tcx.sess.fatal(&format!(
390             "Failed to link `{}` and `{}` together",
391             main_object_file.display(),
392             global_asm_object_file.display(),
393         ));
394     }
395
396     std::fs::remove_file(global_asm_object_file).unwrap();
397     std::fs::remove_file(main_object_file).unwrap();
398 }
399
400 fn add_file_stem_postfix(mut path: PathBuf, postfix: &str) -> PathBuf {
401     let mut new_filename = path.file_stem().unwrap().to_owned();
402     new_filename.push(postfix);
403     if let Some(extension) = path.extension() {
404         new_filename.push(".");
405         new_filename.push(extension);
406     }
407     path.set_file_name(new_filename);
408     path
409 }
410
411 // Adapted from https://github.com/rust-lang/rust/blob/303d8aff6092709edd4dbd35b1c88e9aa40bf6d8/src/librustc_codegen_ssa/base.rs#L922-L953
412 fn determine_cgu_reuse<'tcx>(tcx: TyCtxt<'tcx>, cgu: &CodegenUnit<'tcx>) -> CguReuse {
413     if !tcx.dep_graph.is_fully_enabled() {
414         return CguReuse::No;
415     }
416
417     let work_product_id = &cgu.work_product_id();
418     if tcx.dep_graph.previous_work_product(work_product_id).is_none() {
419         // We don't have anything cached for this CGU. This can happen
420         // if the CGU did not exist in the previous session.
421         return CguReuse::No;
422     }
423
424     // Try to mark the CGU as green. If it we can do so, it means that nothing
425     // affecting the LLVM module has changed and we can re-use a cached version.
426     // If we compile with any kind of LTO, this means we can re-use the bitcode
427     // of the Pre-LTO stage (possibly also the Post-LTO version but we'll only
428     // know that later). If we are not doing LTO, there is only one optimized
429     // version of each module, so we re-use that.
430     let dep_node = cgu.codegen_dep_node(tcx);
431     assert!(
432         !tcx.dep_graph.dep_node_exists(&dep_node),
433         "CompileCodegenUnit dep-node for CGU `{}` already exists before marking.",
434         cgu.name()
435     );
436
437     if tcx.try_mark_green(&dep_node) { CguReuse::PreLto } else { CguReuse::No }
438 }