1 //! Cranelift compilation context and main entry point.
2 //!
3 //! When compiling many small functions, it is important to avoid repeatedly allocating and
4 //! deallocating the data structures needed for compilation. The `Context` struct is used to hold
5 //! on to memory allocations between function compilations.
6 //!
7 //! The context does not hold a `TargetIsa` instance which has to be provided as an argument
8 //! instead. This is because an ISA instance is immutable and can be used by multiple compilation
9 //! contexts concurrently. Typically, you would have one context per compilation thread and only a
10 //! single ISA instance.
11 
12 use crate::alias_analysis::AliasAnalysis;
13 use crate::dce::do_dce;
14 use crate::dominator_tree::DominatorTree;
15 use crate::flowgraph::ControlFlowGraph;
16 use crate::ir::Function;
17 use crate::isa::TargetIsa;
18 use crate::legalizer::simple_legalize;
19 use crate::licm::do_licm;
20 use crate::loop_analysis::LoopAnalysis;
21 use crate::machinst::{CompiledCode, CompiledCodeStencil};
22 use crate::nan_canonicalization::do_nan_canonicalization;
23 use crate::remove_constant_phis::do_remove_constant_phis;
24 use crate::result::{CodegenResult, CompileResult};
25 use crate::settings::{FlagsOrIsa, OptLevel};
26 use crate::simple_gvn::do_simple_gvn;
27 use crate::simple_preopt::do_preopt;
28 use crate::unreachable_code::eliminate_unreachable_code;
29 use crate::verifier::{verify_context, VerifierErrors, VerifierResult};
30 use crate::{timing, CompileError};
31 #[cfg(feature = "souper-harvest")]
32 use alloc::string::String;
33 use alloc::vec::Vec;
34 
35 #[cfg(feature = "souper-harvest")]
36 use crate::souper_harvest::do_souper_harvest;
37 
38 /// Persistent data structures and compilation pipeline.
39 pub struct Context {
40     /// The function we're compiling.
41     pub func: Function,
42 
43     /// The control flow graph of `func`.
44     pub cfg: ControlFlowGraph,
45 
46     /// Dominator tree for `func`.
47     pub domtree: DominatorTree,
48 
49     /// Loop analysis of `func`.
50     pub loop_analysis: LoopAnalysis,
51 
52     /// Result of MachBackend compilation, if computed.
53     pub(crate) compiled_code: Option<CompiledCode>,
54 
55     /// Flag: do we want a disassembly with the CompiledCode?
56     pub want_disasm: bool,
57 }
58 
59 impl Context {
60     /// Allocate a new compilation context.
61     ///
62     /// The returned instance should be reused for compiling multiple functions in order to avoid
63     /// needless allocator thrashing.
64     pub fn new() -> Self {
65         Self::for_function(Function::new())
66     }
67 
68     /// Allocate a new compilation context with an existing Function.
69     ///
70     /// The returned instance should be reused for compiling multiple functions in order to avoid
71     /// needless allocator thrashing.
72     pub fn for_function(func: Function) -> Self {
73         Self {
74             func,
75             cfg: ControlFlowGraph::new(),
76             domtree: DominatorTree::new(),
77             loop_analysis: LoopAnalysis::new(),
78             compiled_code: None,
79             want_disasm: false,
80         }
81     }
82 
83     /// Clear all data structures in this context.
84     pub fn clear(&mut self) {
85         self.func.clear();
86         self.cfg.clear();
87         self.domtree.clear();
88         self.loop_analysis.clear();
89         self.compiled_code = None;
90         self.want_disasm = false;
91     }
92 
93     /// Returns the compilation result for this function, available after any `compile` function
94     /// has been called.
95     pub fn compiled_code(&self) -> Option<&CompiledCode> {
96         self.compiled_code.as_ref()
97     }
98 
99     /// Set the flag to request a disassembly when compiling with a
100     /// `MachBackend` backend.
101     pub fn set_disasm(&mut self, val: bool) {
102         self.want_disasm = val;
103     }
104 
105     /// Compile the function, and emit machine code into a `Vec<u8>`.
106     ///
107     /// Run the function through all the passes necessary to generate code for the target ISA
108     /// represented by `isa`, as well as the final step of emitting machine code into a
109     /// `Vec<u8>`. The machine code is not relocated. Instead, any relocations can be obtained
110     /// from `compiled_code()`.
111     ///
112     /// This function calls `compile`, taking care to resize `mem` as
113     /// needed, so it provides a safe interface.
114     ///
115     /// Returns information about the function's code and read-only data.
116     pub fn compile_and_emit(
117         &mut self,
118         isa: &dyn TargetIsa,
119         mem: &mut Vec<u8>,
120     ) -> CompileResult<&CompiledCode> {
121         let compiled_code = self.compile(isa)?;
122         let code_info = compiled_code.code_info();
123         let old_len = mem.len();
124         mem.resize(old_len + code_info.total_size as usize, 0);
125         mem[old_len..].copy_from_slice(compiled_code.code_buffer());
126         Ok(compiled_code)
127     }
128 
129     /// Internally compiles the function into a stencil.
130     ///
131     /// Public only for testing and fuzzing purposes.
132     pub fn compile_stencil(&mut self, isa: &dyn TargetIsa) -> CodegenResult<CompiledCodeStencil> {
133         let _tt = timing::compile();
134 
135         self.verify_if(isa)?;
136 
137         let opt_level = isa.flags().opt_level();
138         log::trace!(
139             "Compiling (opt level {:?}):\n{}",
140             opt_level,
141             self.func.display()
142         );
143 
144         self.compute_cfg();
145         if opt_level != OptLevel::None {
146             self.preopt(isa)?;
147         }
148         if isa.flags().enable_nan_canonicalization() {
149             self.canonicalize_nans(isa)?;
150         }
151 
152         self.legalize(isa)?;
153         if opt_level != OptLevel::None {
154             self.compute_domtree();
155             self.compute_loop_analysis();
156             self.licm(isa)?;
157             self.simple_gvn(isa)?;
158         }
159 
160         self.compute_domtree();
161         self.eliminate_unreachable_code(isa)?;
162         if opt_level != OptLevel::None {
163             self.dce(isa)?;
164         }
165 
166         self.remove_constant_phis(isa)?;
167 
168         if opt_level != OptLevel::None && isa.flags().enable_alias_analysis() {
169             self.replace_redundant_loads()?;
170             self.simple_gvn(isa)?;
171         }
172 
173         isa.compile_function(&self.func, self.want_disasm)
174     }
175 
176     /// Compile the function.
177     ///
178     /// Run the function through all the passes necessary to generate code for the target ISA
179     /// represented by `isa`. This does not include the final step of emitting machine code into a
180     /// code sink.
181     ///
182     /// Returns information about the function's code and read-only data.
183     pub fn compile(&mut self, isa: &dyn TargetIsa) -> CompileResult<&CompiledCode> {
184         let _tt = timing::compile();
185         let stencil = self.compile_stencil(isa).map_err(|error| CompileError {
186             inner: error,
187             func: &self.func,
188         })?;
189         Ok(self
190             .compiled_code
191             .insert(stencil.apply_params(&self.func.params)))
192     }
193 
194     /// If available, return information about the code layout in the
195     /// final machine code: the offsets (in bytes) of each basic-block
196     /// start, and all basic-block edges.
197     pub fn get_code_bb_layout(&self) -> Option<(Vec<usize>, Vec<(usize, usize)>)> {
198         if let Some(result) = self.compiled_code.as_ref() {
199             Some((
200                 result.bb_starts.iter().map(|&off| off as usize).collect(),
201                 result
202                     .bb_edges
203                     .iter()
204                     .map(|&(from, to)| (from as usize, to as usize))
205                     .collect(),
206             ))
207         } else {
208             None
209         }
210     }
211 
212     /// Creates unwind information for the function.
213     ///
214     /// Returns `None` if the function has no unwind information.
215     #[cfg(feature = "unwind")]
216     pub fn create_unwind_info(
217         &self,
218         isa: &dyn TargetIsa,
219     ) -> CodegenResult<Option<crate::isa::unwind::UnwindInfo>> {
220         let unwind_info_kind = isa.unwind_info_kind();
221         let result = self.compiled_code.as_ref().unwrap();
222         isa.emit_unwind_info(result, unwind_info_kind)
223     }
224 
225     /// Run the verifier on the function.
226     ///
227     /// Also check that the dominator tree and control flow graph are consistent with the function.
228     pub fn verify<'a, FOI: Into<FlagsOrIsa<'a>>>(&self, fisa: FOI) -> VerifierResult<()> {
229         let mut errors = VerifierErrors::default();
230         let _ = verify_context(&self.func, &self.cfg, &self.domtree, fisa, &mut errors);
231 
232         if errors.is_empty() {
233             Ok(())
234         } else {
235             Err(errors)
236         }
237     }
238 
239     /// Run the verifier only if the `enable_verifier` setting is true.
240     pub fn verify_if<'a, FOI: Into<FlagsOrIsa<'a>>>(&self, fisa: FOI) -> CodegenResult<()> {
241         let fisa = fisa.into();
242         if fisa.flags.enable_verifier() {
243             self.verify(fisa)?;
244         }
245         Ok(())
246     }
247 
248     /// Perform dead-code elimination on the function.
249     pub fn dce<'a, FOI: Into<FlagsOrIsa<'a>>>(&mut self, fisa: FOI) -> CodegenResult<()> {
250         do_dce(&mut self.func, &mut self.domtree);
251         self.verify_if(fisa)?;
252         Ok(())
253     }
254 
255     /// Perform constant-phi removal on the function.
256     pub fn remove_constant_phis<'a, FOI: Into<FlagsOrIsa<'a>>>(
257         &mut self,
258         fisa: FOI,
259     ) -> CodegenResult<()> {
260         do_remove_constant_phis(&mut self.func, &mut self.domtree);
261         self.verify_if(fisa)?;
262         Ok(())
263     }
264 
265     /// Perform pre-legalization rewrites on the function.
266     pub fn preopt(&mut self, isa: &dyn TargetIsa) -> CodegenResult<()> {
267         do_preopt(&mut self.func, &mut self.cfg, isa);
268         self.verify_if(isa)?;
269         Ok(())
270     }
271 
272     /// Perform NaN canonicalizing rewrites on the function.
273     pub fn canonicalize_nans(&mut self, isa: &dyn TargetIsa) -> CodegenResult<()> {
274         do_nan_canonicalization(&mut self.func);
275         self.verify_if(isa)
276     }
277 
278     /// Run the legalizer for `isa` on the function.
279     pub fn legalize(&mut self, isa: &dyn TargetIsa) -> CodegenResult<()> {
280         // Legalization invalidates the domtree and loop_analysis by mutating the CFG.
281         // TODO: Avoid doing this when legalization doesn't actually mutate the CFG.
282         self.domtree.clear();
283         self.loop_analysis.clear();
284 
285         // Run some specific legalizations only.
286         simple_legalize(&mut self.func, &mut self.cfg, isa);
287         self.verify_if(isa)
288     }
289 
290     /// Compute the control flow graph.
291     pub fn compute_cfg(&mut self) {
292         self.cfg.compute(&self.func)
293     }
294 
295     /// Compute dominator tree.
296     pub fn compute_domtree(&mut self) {
297         self.domtree.compute(&self.func, &self.cfg)
298     }
299 
300     /// Compute the loop analysis.
301     pub fn compute_loop_analysis(&mut self) {
302         self.loop_analysis
303             .compute(&self.func, &self.cfg, &self.domtree)
304     }
305 
306     /// Compute the control flow graph and dominator tree.
307     pub fn flowgraph(&mut self) {
308         self.compute_cfg();
309         self.compute_domtree()
310     }
311 
312     /// Perform simple GVN on the function.
313     pub fn simple_gvn<'a, FOI: Into<FlagsOrIsa<'a>>>(&mut self, fisa: FOI) -> CodegenResult<()> {
314         do_simple_gvn(&mut self.func, &mut self.domtree);
315         self.verify_if(fisa)
316     }
317 
318     /// Perform LICM on the function.
319     pub fn licm(&mut self, isa: &dyn TargetIsa) -> CodegenResult<()> {
320         do_licm(
321             &mut self.func,
322             &mut self.cfg,
323             &mut self.domtree,
324             &mut self.loop_analysis,
325         );
326         self.verify_if(isa)
327     }
328 
329     /// Perform unreachable code elimination.
330     pub fn eliminate_unreachable_code<'a, FOI>(&mut self, fisa: FOI) -> CodegenResult<()>
331     where
332         FOI: Into<FlagsOrIsa<'a>>,
333     {
334         eliminate_unreachable_code(&mut self.func, &mut self.cfg, &self.domtree);
335         self.verify_if(fisa)
336     }
337 
338     /// Replace all redundant loads with the known values in
339     /// memory. These are loads whose values were already loaded by
340     /// other loads earlier, as well as loads whose values were stored
341     /// by a store instruction to the same instruction (so-called
342     /// "store-to-load forwarding").
343     pub fn replace_redundant_loads(&mut self) -> CodegenResult<()> {
344         let mut analysis = AliasAnalysis::new(&mut self.func, &self.domtree);
345         analysis.compute_and_update_aliases();
346         Ok(())
347     }
348 
349     /// Harvest candidate left-hand sides for superoptimization with Souper.
350     #[cfg(feature = "souper-harvest")]
351     pub fn souper_harvest(
352         &mut self,
353         out: &mut std::sync::mpsc::Sender<String>,
354     ) -> CodegenResult<()> {
355         do_souper_harvest(&self.func, out);
356         Ok(())
357     }
358 }
359