]> git.lizzy.rs Git - rust.git/blob - src/driver/aot.rs
Add a feature flag for the JIT
[rust.git] / src / driver / aot.rs
1 use rustc_middle::dep_graph::{WorkProduct, WorkProductId};
2 use rustc_middle::middle::cstore::EncodedMetadata;
3 use rustc_middle::mir::mono::CodegenUnit;
4 use rustc_session::config::{DebugInfo, OutputType};
5 use rustc_session::cgu_reuse_tracker::CguReuse;
6 use rustc_codegen_ssa::back::linker::LinkerInfo;
7 use rustc_codegen_ssa::{CrateInfo, CodegenResults, CompiledModule, ModuleKind};
8 use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
9
10 use crate::prelude::*;
11
12 use crate::backend::{Emit, WriteDebugInfo};
13
14 fn new_module(tcx: TyCtxt<'_>, name: String) -> Module<crate::backend::Backend> {
15     let module = crate::backend::make_module(tcx.sess, name);
16     assert_eq!(pointer_ty(tcx), module.target_config().pointer_type());
17     module
18 }
19
20 struct ModuleCodegenResult(CompiledModule, Option<(WorkProductId, WorkProduct)>);
21
22
23 impl<HCX> HashStable<HCX> for ModuleCodegenResult {
24     fn hash_stable(&self, _: &mut HCX, _: &mut StableHasher) {
25         // do nothing
26     }
27 }
28
29 fn emit_module<B: Backend>(
30     tcx: TyCtxt<'_>,
31     name: String,
32     kind: ModuleKind,
33     mut module: Module<B>,
34     debug: Option<DebugContext<'_>>,
35     unwind_context: UnwindContext<'_>,
36 ) -> ModuleCodegenResult
37     where B::Product: Emit + WriteDebugInfo,
38 {
39     module.finalize_definitions();
40     let mut product = module.finish();
41
42     if let Some(mut debug) = debug {
43         debug.emit(&mut product);
44     }
45
46     unwind_context.emit(&mut product);
47
48     let tmp_file = tcx
49         .output_filenames(LOCAL_CRATE)
50         .temp_path(OutputType::Object, Some(&name));
51     let obj = product.emit();
52     std::fs::write(&tmp_file, obj).unwrap();
53
54     let work_product = if std::env::var("CG_CLIF_INCR_CACHE_DISABLED").is_ok() {
55         None
56     } else {
57         rustc_incremental::copy_cgu_workproduct_to_incr_comp_cache_dir(
58             tcx.sess,
59             &name,
60             &Some(tmp_file.clone()),
61         )
62     };
63
64     ModuleCodegenResult(
65         CompiledModule {
66             name,
67             kind,
68             object: Some(tmp_file),
69             bytecode: None,
70         },
71         work_product,
72     )
73 }
74
75 fn reuse_workproduct_for_cgu(
76     tcx: TyCtxt<'_>,
77     cgu: &CodegenUnit<'_>,
78     work_products: &mut FxHashMap<WorkProductId, WorkProduct>,
79 ) -> CompiledModule {
80     let incr_comp_session_dir = tcx.sess.incr_comp_session_dir();
81     let mut object = None;
82     let work_product = cgu.work_product(tcx);
83     if let Some(saved_file) = &work_product.saved_file {
84         let obj_out = tcx.output_filenames(LOCAL_CRATE).temp_path(OutputType::Object, Some(&cgu.name().as_str()));
85         object = Some(obj_out.clone());
86         let source_file = rustc_incremental::in_incr_comp_dir(&incr_comp_session_dir, &saved_file);
87         if let Err(err) = rustc_fs_util::link_or_copy(&source_file, &obj_out) {
88             tcx.sess.err(&format!(
89                 "unable to copy {} to {}: {}",
90                 source_file.display(),
91                 obj_out.display(),
92                 err
93             ));
94         }
95     }
96
97     work_products.insert(cgu.work_product_id(), work_product);
98
99     CompiledModule {
100         name: cgu.name().to_string(),
101         kind: ModuleKind::Regular,
102         object,
103         bytecode: None,
104     }
105 }
106
107 fn module_codegen(tcx: TyCtxt<'_>, cgu_name: rustc_span::Symbol) -> ModuleCodegenResult {
108     let cgu = tcx.codegen_unit(cgu_name);
109     let mono_items = cgu.items_in_deterministic_order(tcx);
110
111     let module = new_module(tcx, cgu_name.as_str().to_string());
112
113     let mut cx = crate::CodegenCx::new(tcx, module, tcx.sess.opts.debuginfo != DebugInfo::None);
114     super::codegen_mono_items(&mut cx, mono_items);
115     let (mut module, debug, mut unwind_context) = tcx.sess.time("finalize CodegenCx", || cx.finalize());
116     crate::main_shim::maybe_create_entry_wrapper(tcx, &mut module, &mut unwind_context);
117
118     emit_module(
119         tcx,
120         cgu.name().as_str().to_string(),
121         ModuleKind::Regular,
122         module,
123         debug,
124         unwind_context,
125     )
126 }
127
128 pub(super) fn run_aot(
129     tcx: TyCtxt<'_>,
130     metadata: EncodedMetadata,
131     need_metadata_module: bool,
132 ) -> Box<(CodegenResults, FxHashMap<WorkProductId, WorkProduct>)> {
133     let mut work_products = FxHashMap::default();
134
135     let cgus = if tcx.sess.opts.output_types.should_codegen() {
136         tcx.collect_and_partition_mono_items(LOCAL_CRATE).1
137     } else {
138         // If only `--emit metadata` is used, we shouldn't perform any codegen.
139         // Also `tcx.collect_and_partition_mono_items` may panic in that case.
140         &[]
141     };
142
143     if tcx.dep_graph.is_fully_enabled() {
144         for cgu in &*cgus {
145             tcx.ensure().codegen_unit(cgu.name());
146         }
147     }
148
149     let modules = super::time(tcx, "codegen mono items", || {
150         cgus.iter().map(|cgu| {
151             let cgu_reuse = determine_cgu_reuse(tcx, cgu);
152             tcx.sess.cgu_reuse_tracker.set_actual_reuse(&cgu.name().as_str(), cgu_reuse);
153
154             match cgu_reuse {
155                 _ if std::env::var("CG_CLIF_INCR_CACHE_DISABLED").is_ok() => {}
156                 CguReuse::No => {}
157                 CguReuse::PreLto => {
158                     return reuse_workproduct_for_cgu(tcx, &*cgu, &mut work_products);
159                 }
160                 CguReuse::PostLto => unreachable!(),
161             }
162
163             let dep_node = cgu.codegen_dep_node(tcx);
164             let (ModuleCodegenResult(module, work_product), _) =
165                 tcx.dep_graph.with_task(dep_node, tcx, cgu.name(), module_codegen, rustc_middle::dep_graph::hash_result);
166
167             if let Some((id, product)) = work_product {
168                 work_products.insert(id, product);
169             }
170
171             module
172         }).collect::<Vec<_>>()
173     });
174
175     tcx.sess.abort_if_errors();
176
177     let mut allocator_module = new_module(tcx, "allocator_shim".to_string());
178     let mut allocator_unwind_context = UnwindContext::new(tcx, allocator_module.isa());
179     let created_alloc_shim = crate::allocator::codegen(
180         tcx,
181         &mut allocator_module,
182         &mut allocator_unwind_context,
183     );
184
185     let allocator_module = if created_alloc_shim {
186         let ModuleCodegenResult(module, work_product) = emit_module(
187             tcx,
188             "allocator_shim".to_string(),
189             ModuleKind::Allocator,
190             allocator_module,
191             None,
192             allocator_unwind_context,
193         );
194         if let Some((id, product)) = work_product {
195             work_products.insert(id, product);
196         }
197         Some(module)
198     } else {
199         None
200     };
201
202     rustc_incremental::assert_dep_graph(tcx);
203     rustc_incremental::save_dep_graph(tcx);
204
205     let metadata_module = if need_metadata_module {
206         let _timer = tcx.prof.generic_activity("codegen crate metadata");
207         let (metadata_cgu_name, tmp_file) = tcx.sess.time("write compressed metadata", || {
208             use rustc_middle::mir::mono::CodegenUnitNameBuilder;
209
210             let cgu_name_builder = &mut CodegenUnitNameBuilder::new(tcx);
211             let metadata_cgu_name = cgu_name_builder
212                 .build_cgu_name(LOCAL_CRATE, &["crate"], Some("metadata"))
213                 .as_str()
214                 .to_string();
215
216             let tmp_file = tcx
217                 .output_filenames(LOCAL_CRATE)
218                 .temp_path(OutputType::Metadata, Some(&metadata_cgu_name));
219
220             let obj = crate::backend::with_object(tcx.sess, &metadata_cgu_name, |object| {
221                 crate::metadata::write_metadata(tcx, object);
222             });
223
224             std::fs::write(&tmp_file, obj).unwrap();
225
226             (metadata_cgu_name, tmp_file)
227         });
228
229         Some(CompiledModule {
230             name: metadata_cgu_name,
231             kind: ModuleKind::Metadata,
232             object: Some(tmp_file),
233             bytecode: None,
234         })
235     } else {
236         None
237     };
238
239     if tcx.sess.opts.output_types.should_codegen() {
240         rustc_incremental::assert_module_sources::assert_module_sources(tcx);
241     }
242
243     Box::new((CodegenResults {
244         crate_name: tcx.crate_name(LOCAL_CRATE),
245         modules,
246         allocator_module,
247         metadata_module,
248         crate_hash: tcx.crate_hash(LOCAL_CRATE),
249         metadata,
250         windows_subsystem: None, // Windows is not yet supported
251         linker_info: LinkerInfo::new(tcx),
252         crate_info: CrateInfo::new(tcx),
253     }, work_products))
254 }
255
256 // Adapted from https://github.com/rust-lang/rust/blob/303d8aff6092709edd4dbd35b1c88e9aa40bf6d8/src/librustc_codegen_ssa/base.rs#L922-L953
257 fn determine_cgu_reuse<'tcx>(tcx: TyCtxt<'tcx>, cgu: &CodegenUnit<'tcx>) -> CguReuse {
258     if !tcx.dep_graph.is_fully_enabled() {
259         return CguReuse::No;
260     }
261
262     let work_product_id = &cgu.work_product_id();
263     if tcx.dep_graph.previous_work_product(work_product_id).is_none() {
264         // We don't have anything cached for this CGU. This can happen
265         // if the CGU did not exist in the previous session.
266         return CguReuse::No;
267     }
268
269     // Try to mark the CGU as green. If it we can do so, it means that nothing
270     // affecting the LLVM module has changed and we can re-use a cached version.
271     // If we compile with any kind of LTO, this means we can re-use the bitcode
272     // of the Pre-LTO stage (possibly also the Post-LTO version but we'll only
273     // know that later). If we are not doing LTO, there is only one optimized
274     // version of each module, so we re-use that.
275     let dep_node = cgu.codegen_dep_node(tcx);
276     assert!(
277         !tcx.dep_graph.dep_node_exists(&dep_node),
278         "CompileCodegenUnit dep-node for CGU `{}` already exists before marking.",
279         cgu.name()
280     );
281
282     if tcx.dep_graph.try_mark_green(tcx, &dep_node).is_some() {
283         CguReuse::PreLto
284     } else {
285         CguReuse::No
286     }
287 }