1 //! The AOT driver uses [`cranelift_object`] to write object files suitable for linking into a
2 //! standalone executable.
5 use std
::path
::PathBuf
;
7 use std
::thread
::JoinHandle
;
9 use rustc_codegen_ssa
::back
::metadata
::create_compressed_metadata_file
;
10 use rustc_codegen_ssa
::{CodegenResults, CompiledModule, CrateInfo, ModuleKind}
;
11 use rustc_data_structures
::profiling
::SelfProfilerRef
;
12 use rustc_data_structures
::stable_hasher
::{HashStable, StableHasher}
;
13 use rustc_metadata
::EncodedMetadata
;
14 use rustc_middle
::dep_graph
::{WorkProduct, WorkProductId}
;
15 use rustc_middle
::mir
::mono
::{CodegenUnit, MonoItem}
;
16 use rustc_session
::cgu_reuse_tracker
::CguReuse
;
17 use rustc_session
::config
::{DebugInfo, OutputFilenames, OutputType}
;
18 use rustc_session
::Session
;
20 use cranelift_object
::{ObjectBuilder, ObjectModule}
;
22 use crate::concurrency_limiter
::{ConcurrencyLimiter, ConcurrencyLimiterToken}
;
23 use crate::global_asm
::GlobalAsmConfig
;
24 use crate::{prelude::*, BackendConfig}
;
26 struct ModuleCodegenResult
{
27 module_regular
: CompiledModule
,
28 module_global_asm
: Option
<CompiledModule
>,
29 existing_work_product
: Option
<(WorkProductId
, WorkProduct
)>,
32 enum OngoingModuleCodegen
{
33 Sync(Result
<ModuleCodegenResult
, String
>),
34 Async(JoinHandle
<Result
<ModuleCodegenResult
, String
>>),
37 impl<HCX
> HashStable
<HCX
> for OngoingModuleCodegen
{
38 fn hash_stable(&self, _
: &mut HCX
, _
: &mut StableHasher
) {
43 pub(crate) struct OngoingCodegen
{
44 modules
: Vec
<OngoingModuleCodegen
>,
45 allocator_module
: Option
<CompiledModule
>,
46 metadata_module
: Option
<CompiledModule
>,
47 metadata
: EncodedMetadata
,
48 crate_info
: CrateInfo
,
49 concurrency_limiter
: ConcurrencyLimiter
,
56 backend_config
: &BackendConfig
,
57 ) -> (CodegenResults
, FxHashMap
<WorkProductId
, WorkProduct
>) {
58 let mut work_products
= FxHashMap
::default();
59 let mut modules
= vec
![];
61 for module_codegen
in self.modules
{
62 let module_codegen_result
= match module_codegen
{
63 OngoingModuleCodegen
::Sync(module_codegen_result
) => module_codegen_result
,
64 OngoingModuleCodegen
::Async(join_handle
) => match join_handle
.join() {
65 Ok(module_codegen_result
) => module_codegen_result
,
66 Err(panic
) => std
::panic
::resume_unwind(panic
),
70 let module_codegen_result
= match module_codegen_result
{
71 Ok(module_codegen_result
) => module_codegen_result
,
72 Err(err
) => sess
.fatal(&err
),
74 let ModuleCodegenResult { module_regular, module_global_asm, existing_work_product }
=
75 module_codegen_result
;
77 if let Some((work_product_id
, work_product
)) = existing_work_product
{
78 work_products
.insert(work_product_id
, work_product
);
80 let work_product
= if backend_config
.disable_incr_cache
{
82 } else if let Some(module_global_asm
) = &module_global_asm
{
83 rustc_incremental
::copy_cgu_workproduct_to_incr_comp_cache_dir(
87 ("o", &module_regular
.object
.as_ref().unwrap()),
88 ("asm.o", &module_global_asm
.object
.as_ref().unwrap()),
92 rustc_incremental
::copy_cgu_workproduct_to_incr_comp_cache_dir(
95 &[("o", &module_regular
.object
.as_ref().unwrap())],
98 if let Some((work_product_id
, work_product
)) = work_product
{
99 work_products
.insert(work_product_id
, work_product
);
103 modules
.push(module_regular
);
104 if let Some(module_global_asm
) = module_global_asm
{
105 modules
.push(module_global_asm
);
109 drop(self.concurrency_limiter
);
114 allocator_module
: self.allocator_module
,
115 metadata_module
: self.metadata_module
,
116 metadata
: self.metadata
,
117 crate_info
: self.crate_info
,
124 fn make_module(sess
: &Session
, backend_config
: &BackendConfig
, name
: String
) -> ObjectModule
{
125 let isa
= crate::build_isa(sess
, backend_config
);
128 ObjectBuilder
::new(isa
, name
+ ".o", cranelift_module
::default_libcall_names()).unwrap();
129 // Unlike cg_llvm, cg_clif defaults to disabling -Zfunction-sections. For cg_llvm binary size
130 // is important, while cg_clif cares more about compilation times. Enabling -Zfunction-sections
131 // can easily double the amount of time necessary to perform linking.
132 builder
.per_function_section(sess
.opts
.unstable_opts
.function_sections
.unwrap_or(false));
133 ObjectModule
::new(builder
)
137 output_filenames
: &OutputFilenames
,
138 prof
: &SelfProfilerRef
,
140 module
: ObjectModule
,
141 debug
: Option
<DebugContext
>,
142 unwind_context
: UnwindContext
,
143 global_asm_object_file
: Option
<PathBuf
>,
144 ) -> Result
<ModuleCodegenResult
, String
> {
145 let mut product
= module
.finish();
147 if let Some(mut debug
) = debug
{
148 debug
.emit(&mut product
);
151 unwind_context
.emit(&mut product
);
154 emit_module(output_filenames
, prof
, product
.object
, ModuleKind
::Regular
, name
.clone())?
;
156 Ok(ModuleCodegenResult
{
158 module_global_asm
: global_asm_object_file
.map(|global_asm_object_file
| CompiledModule
{
159 name
: format
!("{name}.asm"),
160 kind
: ModuleKind
::Regular
,
161 object
: Some(global_asm_object_file
),
165 existing_work_product
: None
,
170 output_filenames
: &OutputFilenames
,
171 prof
: &SelfProfilerRef
,
172 object
: cranelift_object
::object
::write
::Object
<'_
>,
175 ) -> Result
<CompiledModule
, String
> {
176 let tmp_file
= output_filenames
.temp_path(OutputType
::Object
, Some(&name
));
177 let mut file
= match File
::create(&tmp_file
) {
179 Err(err
) => return Err(format
!("error creating object file: {}", err
)),
182 if let Err(err
) = object
.write_stream(&mut file
) {
183 return Err(format
!("error writing object file: {}", err
));
186 prof
.artifact_size("object_file", &*name
, file
.metadata().unwrap().len());
188 Ok(CompiledModule { name, kind, object: Some(tmp_file), dwarf_object: None, bytecode: None }
)
191 fn reuse_workproduct_for_cgu(
193 cgu
: &CodegenUnit
<'_
>,
194 ) -> Result
<ModuleCodegenResult
, String
> {
195 let work_product
= cgu
.previous_work_product(tcx
);
196 let obj_out_regular
=
197 tcx
.output_filenames(()).temp_path(OutputType
::Object
, Some(cgu
.name().as_str()));
198 let source_file_regular
= rustc_incremental
::in_incr_comp_dir_sess(
200 &work_product
.saved_files
.get("o").expect("no saved object file in work product"),
203 if let Err(err
) = rustc_fs_util
::link_or_copy(&source_file_regular
, &obj_out_regular
) {
205 "unable to copy {} to {}: {}",
206 source_file_regular
.display(),
207 obj_out_regular
.display(),
211 let obj_out_global_asm
=
212 crate::global_asm
::add_file_stem_postfix(obj_out_regular
.clone(), ".asm");
213 let has_global_asm
= if let Some(asm_o
) = work_product
.saved_files
.get("asm.o") {
214 let source_file_global_asm
= rustc_incremental
::in_incr_comp_dir_sess(&tcx
.sess
, asm_o
);
215 if let Err(err
) = rustc_fs_util
::link_or_copy(&source_file_global_asm
, &obj_out_global_asm
)
218 "unable to copy {} to {}: {}",
219 source_file_regular
.display(),
220 obj_out_regular
.display(),
229 Ok(ModuleCodegenResult
{
230 module_regular
: CompiledModule
{
231 name
: cgu
.name().to_string(),
232 kind
: ModuleKind
::Regular
,
233 object
: Some(obj_out_regular
),
237 module_global_asm
: if has_global_asm
{
238 Some(CompiledModule
{
239 name
: cgu
.name().to_string(),
240 kind
: ModuleKind
::Regular
,
241 object
: Some(obj_out_global_asm
),
248 existing_work_product
: Some((cgu
.work_product_id(), work_product
)),
254 (backend_config
, global_asm_config
, cgu_name
, token
): (
256 Arc
<GlobalAsmConfig
>,
258 ConcurrencyLimiterToken
,
260 ) -> OngoingModuleCodegen
{
261 let (cgu_name
, mut cx
, mut module
, codegened_functions
) = tcx
.sess
.time("codegen cgu", || {
262 let cgu
= tcx
.codegen_unit(cgu_name
);
263 let mono_items
= cgu
.items_in_deterministic_order(tcx
);
265 let mut module
= make_module(tcx
.sess
, &backend_config
, cgu_name
.as_str().to_string());
267 let mut cx
= crate::CodegenCx
::new(
269 backend_config
.clone(),
271 tcx
.sess
.opts
.debuginfo
!= DebugInfo
::None
,
274 super::predefine_mono_items(tcx
, &mut module
, &mono_items
);
275 let mut codegened_functions
= vec
![];
276 for (mono_item
, _
) in mono_items
{
278 MonoItem
::Fn(inst
) => {
279 tcx
.sess
.time("codegen fn", || {
280 let codegened_function
= crate::base
::codegen_fn(
287 codegened_functions
.push(codegened_function
);
290 MonoItem
::Static(def_id
) => {
291 crate::constant
::codegen_static(tcx
, &mut module
, def_id
)
293 MonoItem
::GlobalAsm(item_id
) => {
294 crate::global_asm
::codegen_global_asm_item(tcx
, &mut cx
.global_asm
, item_id
);
298 crate::main_shim
::maybe_create_entry_wrapper(
301 &mut cx
.unwind_context
,
306 let cgu_name
= cgu
.name().as_str().to_owned();
308 (cgu_name
, cx
, module
, codegened_functions
)
311 OngoingModuleCodegen
::Async(std
::thread
::spawn(move || {
312 cx
.profiler
.clone().verbose_generic_activity("compile functions").run(|| {
313 let mut cached_context
= Context
::new();
314 for codegened_func
in codegened_functions
{
315 crate::base
::compile_fn(&mut cx
, &mut cached_context
, &mut module
, codegened_func
);
319 let global_asm_object_file
=
320 cx
.profiler
.verbose_generic_activity("compile assembly").run(|| {
321 crate::global_asm
::compile_global_asm(&global_asm_config
, &cgu_name
, &cx
.global_asm
)
324 let codegen_result
= cx
.profiler
.verbose_generic_activity("write object file").run(|| {
326 &global_asm_config
.output_filenames
,
332 global_asm_object_file
,
335 std
::mem
::drop(token
);
340 pub(crate) fn run_aot(
342 backend_config
: BackendConfig
,
343 metadata
: EncodedMetadata
,
344 need_metadata_module
: bool
,
345 ) -> Box
<OngoingCodegen
> {
346 let cgus
= if tcx
.sess
.opts
.output_types
.should_codegen() {
347 tcx
.collect_and_partition_mono_items(()).1
349 // If only `--emit metadata` is used, we shouldn't perform any codegen.
350 // Also `tcx.collect_and_partition_mono_items` may panic in that case.
354 if tcx
.dep_graph
.is_fully_enabled() {
356 tcx
.ensure().codegen_unit(cgu
.name());
360 let global_asm_config
= Arc
::new(crate::global_asm
::GlobalAsmConfig
::new(tcx
));
362 let mut concurrency_limiter
= ConcurrencyLimiter
::new(tcx
.sess
, cgus
.len());
364 let modules
= super::time(tcx
, backend_config
.display_cg_time
, "codegen mono items", || {
367 let cgu_reuse
= if backend_config
.disable_incr_cache
{
370 determine_cgu_reuse(tcx
, cgu
)
372 tcx
.sess
.cgu_reuse_tracker
.set_actual_reuse(cgu
.name().as_str(), cgu_reuse
);
376 let dep_node
= cgu
.codegen_dep_node(tcx
);
382 backend_config
.clone(),
383 global_asm_config
.clone(),
385 concurrency_limiter
.acquire(),
388 Some(rustc_middle
::dep_graph
::hash_result
),
392 CguReuse
::PreLto
=> unreachable
!(),
393 CguReuse
::PostLto
=> {
394 concurrency_limiter
.job_already_done();
395 OngoingModuleCodegen
::Sync(reuse_workproduct_for_cgu(tcx
, &*cgu
))
402 tcx
.sess
.abort_if_errors();
404 let mut allocator_module
= make_module(tcx
.sess
, &backend_config
, "allocator_shim".to_string());
405 let mut allocator_unwind_context
= UnwindContext
::new(allocator_module
.isa(), true);
406 let created_alloc_shim
=
407 crate::allocator
::codegen(tcx
, &mut allocator_module
, &mut allocator_unwind_context
);
409 let allocator_module
= if created_alloc_shim
{
410 let mut product
= allocator_module
.finish();
411 allocator_unwind_context
.emit(&mut product
);
414 tcx
.output_filenames(()),
417 ModuleKind
::Allocator
,
418 "allocator_shim".to_owned(),
420 Ok(allocator_module
) => Some(allocator_module
),
421 Err(err
) => tcx
.sess
.fatal(err
),
427 let metadata_module
= if need_metadata_module
{
428 let _timer
= tcx
.prof
.generic_activity("codegen crate metadata");
429 let (metadata_cgu_name
, tmp_file
) = tcx
.sess
.time("write compressed metadata", || {
430 use rustc_middle
::mir
::mono
::CodegenUnitNameBuilder
;
432 let cgu_name_builder
= &mut CodegenUnitNameBuilder
::new(tcx
);
433 let metadata_cgu_name
= cgu_name_builder
434 .build_cgu_name(LOCAL_CRATE
, &["crate"], Some("metadata"))
439 tcx
.output_filenames(()).temp_path(OutputType
::Metadata
, Some(&metadata_cgu_name
));
441 let symbol_name
= rustc_middle
::middle
::exported_symbols
::metadata_symbol_name(tcx
);
442 let obj
= create_compressed_metadata_file(tcx
.sess
, &metadata
, &symbol_name
);
444 if let Err(err
) = std
::fs
::write(&tmp_file
, obj
) {
445 tcx
.sess
.fatal(&format
!("error writing metadata object file: {}", err
));
448 (metadata_cgu_name
, tmp_file
)
451 Some(CompiledModule
{
452 name
: metadata_cgu_name
,
453 kind
: ModuleKind
::Metadata
,
454 object
: Some(tmp_file
),
462 // FIXME handle `-Ctarget-cpu=native`
463 let target_cpu
= match tcx
.sess
.opts
.cg
.target_cpu
{
464 Some(ref name
) => name
,
465 None
=> tcx
.sess
.target
.cpu
.as_ref(),
469 Box
::new(OngoingCodegen
{
474 crate_info
: CrateInfo
::new(tcx
, target_cpu
),
479 // Adapted from https://github.com/rust-lang/rust/blob/303d8aff6092709edd4dbd35b1c88e9aa40bf6d8/src/librustc_codegen_ssa/base.rs#L922-L953
480 fn determine_cgu_reuse
<'tcx
>(tcx
: TyCtxt
<'tcx
>, cgu
: &CodegenUnit
<'tcx
>) -> CguReuse
{
481 if !tcx
.dep_graph
.is_fully_enabled() {
485 let work_product_id
= &cgu
.work_product_id();
486 if tcx
.dep_graph
.previous_work_product(work_product_id
).is_none() {
487 // We don't have anything cached for this CGU. This can happen
488 // if the CGU did not exist in the previous session.
492 // Try to mark the CGU as green. If it we can do so, it means that nothing
493 // affecting the LLVM module has changed and we can re-use a cached version.
494 // If we compile with any kind of LTO, this means we can re-use the bitcode
495 // of the Pre-LTO stage (possibly also the Post-LTO version but we'll only
496 // know that later). If we are not doing LTO, there is only one optimized
497 // version of each module, so we re-use that.
498 let dep_node
= cgu
.codegen_dep_node(tcx
);
500 !tcx
.dep_graph
.dep_node_exists(&dep_node
),
501 "CompileCodegenUnit dep-node for CGU `{}` already exists before marking.",
505 if tcx
.try_mark_green(&dep_node
) { CguReuse::PostLto }
else { CguReuse::No }