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;
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     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` and `emit_to_memory`, 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     /// Compile the function.
130     ///
131     /// Run the function through all the passes necessary to generate code for the target ISA
132     /// represented by `isa`. This does not include the final step of emitting machine code into a
133     /// code sink.
134     ///
135     /// Returns information about the function's code and read-only data.
136     pub fn compile(&mut self, isa: &dyn TargetIsa) -> CompileResult<&CompiledCode> {
137         let _tt = timing::compile();
138 
139         let mut inner = || {
140             self.verify_if(isa)?;
141 
142             let opt_level = isa.flags().opt_level();
143             log::trace!(
144                 "Compiling (opt level {:?}):\n{}",
145                 opt_level,
146                 self.func.display()
147             );
148 
149             self.compute_cfg();
150             if opt_level != OptLevel::None {
151                 self.preopt(isa)?;
152             }
153             if isa.flags().enable_nan_canonicalization() {
154                 self.canonicalize_nans(isa)?;
155             }
156 
157             self.legalize(isa)?;
158             if opt_level != OptLevel::None {
159                 self.compute_domtree();
160                 self.compute_loop_analysis();
161                 self.licm(isa)?;
162                 self.simple_gvn(isa)?;
163             }
164 
165             self.compute_domtree();
166             self.eliminate_unreachable_code(isa)?;
167             if opt_level != OptLevel::None {
168                 self.dce(isa)?;
169             }
170 
171             self.remove_constant_phis(isa)?;
172 
173             if opt_level != OptLevel::None && isa.flags().enable_alias_analysis() {
174                 self.replace_redundant_loads()?;
175                 self.simple_gvn(isa)?;
176             }
177 
178             let result = isa.compile_function(&self.func, self.want_disasm)?;
179             self.compiled_code = Some(result);
180             Ok(())
181         };
182 
183         inner()
184             .map(|_| self.compiled_code.as_ref().unwrap())
185             .map_err(|error| CompileError {
186                 inner: error,
187                 func: &self.func,
188             })
189     }
190 
191     /// If available, return information about the code layout in the
192     /// final machine code: the offsets (in bytes) of each basic-block
193     /// start, and all basic-block edges.
194     pub fn get_code_bb_layout(&self) -> Option<(Vec<usize>, Vec<(usize, usize)>)> {
195         if let Some(result) = self.compiled_code.as_ref() {
196             Some((
197                 result.bb_starts.iter().map(|&off| off as usize).collect(),
198                 result
199                     .bb_edges
200                     .iter()
201                     .map(|&(from, to)| (from as usize, to as usize))
202                     .collect(),
203             ))
204         } else {
205             None
206         }
207     }
208 
209     /// Creates unwind information for the function.
210     ///
211     /// Returns `None` if the function has no unwind information.
212     #[cfg(feature = "unwind")]
213     pub fn create_unwind_info(
214         &self,
215         isa: &dyn TargetIsa,
216     ) -> CodegenResult<Option<crate::isa::unwind::UnwindInfo>> {
217         let unwind_info_kind = isa.unwind_info_kind();
218         let result = self.compiled_code.as_ref().unwrap();
219         isa.emit_unwind_info(result, unwind_info_kind)
220     }
221 
222     /// Run the verifier on the function.
223     ///
224     /// Also check that the dominator tree and control flow graph are consistent with the function.
225     pub fn verify<'a, FOI: Into<FlagsOrIsa<'a>>>(&self, fisa: FOI) -> VerifierResult<()> {
226         let mut errors = VerifierErrors::default();
227         let _ = verify_context(&self.func, &self.cfg, &self.domtree, fisa, &mut errors);
228 
229         if errors.is_empty() {
230             Ok(())
231         } else {
232             Err(errors)
233         }
234     }
235 
236     /// Run the verifier only if the `enable_verifier` setting is true.
237     pub fn verify_if<'a, FOI: Into<FlagsOrIsa<'a>>>(&self, fisa: FOI) -> CodegenResult<()> {
238         let fisa = fisa.into();
239         if fisa.flags.enable_verifier() {
240             self.verify(fisa)?;
241         }
242         Ok(())
243     }
244 
245     /// Perform dead-code elimination on the function.
246     pub fn dce<'a, FOI: Into<FlagsOrIsa<'a>>>(&mut self, fisa: FOI) -> CodegenResult<()> {
247         do_dce(&mut self.func, &mut self.domtree);
248         self.verify_if(fisa)?;
249         Ok(())
250     }
251 
252     /// Perform constant-phi removal on the function.
253     pub fn remove_constant_phis<'a, FOI: Into<FlagsOrIsa<'a>>>(
254         &mut self,
255         fisa: FOI,
256     ) -> CodegenResult<()> {
257         do_remove_constant_phis(&mut self.func, &mut self.domtree);
258         self.verify_if(fisa)?;
259         Ok(())
260     }
261 
262     /// Perform pre-legalization rewrites on the function.
263     pub fn preopt(&mut self, isa: &dyn TargetIsa) -> CodegenResult<()> {
264         do_preopt(&mut self.func, &mut self.cfg, isa);
265         self.verify_if(isa)?;
266         Ok(())
267     }
268 
269     /// Perform NaN canonicalizing rewrites on the function.
270     pub fn canonicalize_nans(&mut self, isa: &dyn TargetIsa) -> CodegenResult<()> {
271         do_nan_canonicalization(&mut self.func);
272         self.verify_if(isa)
273     }
274 
275     /// Run the legalizer for `isa` on the function.
276     pub fn legalize(&mut self, isa: &dyn TargetIsa) -> CodegenResult<()> {
277         // Legalization invalidates the domtree and loop_analysis by mutating the CFG.
278         // TODO: Avoid doing this when legalization doesn't actually mutate the CFG.
279         self.domtree.clear();
280         self.loop_analysis.clear();
281 
282         // Run some specific legalizations only.
283         simple_legalize(&mut self.func, &mut self.cfg, isa);
284         self.verify_if(isa)
285     }
286 
287     /// Compute the control flow graph.
288     pub fn compute_cfg(&mut self) {
289         self.cfg.compute(&self.func)
290     }
291 
292     /// Compute dominator tree.
293     pub fn compute_domtree(&mut self) {
294         self.domtree.compute(&self.func, &self.cfg)
295     }
296 
297     /// Compute the loop analysis.
298     pub fn compute_loop_analysis(&mut self) {
299         self.loop_analysis
300             .compute(&self.func, &self.cfg, &self.domtree)
301     }
302 
303     /// Compute the control flow graph and dominator tree.
304     pub fn flowgraph(&mut self) {
305         self.compute_cfg();
306         self.compute_domtree()
307     }
308 
309     /// Perform simple GVN on the function.
310     pub fn simple_gvn<'a, FOI: Into<FlagsOrIsa<'a>>>(&mut self, fisa: FOI) -> CodegenResult<()> {
311         do_simple_gvn(&mut self.func, &mut self.domtree);
312         self.verify_if(fisa)
313     }
314 
315     /// Perform LICM on the function.
316     pub fn licm(&mut self, isa: &dyn TargetIsa) -> CodegenResult<()> {
317         do_licm(
318             &mut self.func,
319             &mut self.cfg,
320             &mut self.domtree,
321             &mut self.loop_analysis,
322         );
323         self.verify_if(isa)
324     }
325 
326     /// Perform unreachable code elimination.
327     pub fn eliminate_unreachable_code<'a, FOI>(&mut self, fisa: FOI) -> CodegenResult<()>
328     where
329         FOI: Into<FlagsOrIsa<'a>>,
330     {
331         eliminate_unreachable_code(&mut self.func, &mut self.cfg, &self.domtree);
332         self.verify_if(fisa)
333     }
334 
335     /// Replace all redundant loads with the known values in
336     /// memory. These are loads whose values were already loaded by
337     /// other loads earlier, as well as loads whose values were stored
338     /// by a store instruction to the same instruction (so-called
339     /// "store-to-load forwarding").
340     pub fn replace_redundant_loads(&mut self) -> CodegenResult<()> {
341         let mut analysis = AliasAnalysis::new(&mut self.func, &self.domtree);
342         analysis.compute_and_update_aliases();
343         Ok(())
344     }
345 
346     /// Harvest candidate left-hand sides for superoptimization with Souper.
347     #[cfg(feature = "souper-harvest")]
348     pub fn souper_harvest(
349         &mut self,
350         out: &mut std::sync::mpsc::Sender<String>,
351     ) -> CodegenResult<()> {
352         do_souper_harvest(&self.func, out);
353         Ok(())
354     }
355 }
356