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::dominator_tree::DominatorTree;
14 use crate::dominator_tree::DominatorTreePreorder;
15 use crate::egraph::EgraphPass;
16 use crate::flowgraph::ControlFlowGraph;
17 use crate::ir::Function;
18 use crate::isa::TargetIsa;
19 use crate::legalizer::simple_legalize;
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::trace;
27 use crate::unreachable_code::eliminate_unreachable_code;
28 use crate::verifier::{VerifierErrors, VerifierResult, verify_context};
29 use crate::{CompileError, timing};
30 #[cfg(feature = "souper-harvest")]
31 use alloc::string::String;
32 use alloc::vec::Vec;
33 use cranelift_control::ControlPlane;
34 use target_lexicon::Architecture;
35 
36 #[cfg(feature = "souper-harvest")]
37 use crate::souper_harvest::do_souper_harvest;
38 
39 /// Persistent data structures and compilation pipeline.
40 pub struct Context {
41     /// The function we're compiling.
42     pub func: Function,
43 
44     /// The control flow graph of `func`.
45     pub cfg: ControlFlowGraph,
46 
47     /// Dominator tree for `func`.
48     pub domtree: DominatorTree,
49 
50     /// Dominator tree with dominance stored implicitly via visit-order indices for `func`
51     domtree_preorder: DominatorTreePreorder,
52 
53     /// Loop analysis of `func`.
54     pub loop_analysis: LoopAnalysis,
55 
56     /// Result of MachBackend compilation, if computed.
57     pub(crate) compiled_code: Option<CompiledCode>,
58 
59     /// Flag: do we want a disassembly with the CompiledCode?
60     pub want_disasm: bool,
61 }
62 
63 impl Context {
64     /// Allocate a new compilation context.
65     ///
66     /// The returned instance should be reused for compiling multiple functions in order to avoid
67     /// needless allocator thrashing.
68     pub fn new() -> Self {
69         Self::for_function(Function::new())
70     }
71 
72     /// Allocate a new compilation context with an existing Function.
73     ///
74     /// The returned instance should be reused for compiling multiple functions in order to avoid
75     /// needless allocator thrashing.
76     pub fn for_function(func: Function) -> Self {
77         Self {
78             func,
79             cfg: ControlFlowGraph::new(),
80             domtree: DominatorTree::new(),
81             domtree_preorder: DominatorTreePreorder::new(),
82             loop_analysis: LoopAnalysis::new(),
83             compiled_code: None,
84             want_disasm: false,
85         }
86     }
87 
88     /// Clear all data structures in this context.
89     pub fn clear(&mut self) {
90         self.func.clear();
91         self.cfg.clear();
92         self.domtree.clear();
93         self.loop_analysis.clear();
94         self.compiled_code = None;
95         self.want_disasm = false;
96     }
97 
98     /// Returns the compilation result for this function, available after any `compile` function
99     /// has been called.
100     pub fn compiled_code(&self) -> Option<&CompiledCode> {
101         self.compiled_code.as_ref()
102     }
103 
104     /// Returns the compilation result for this function, available after any `compile` function
105     /// has been called.
106     pub fn take_compiled_code(&mut self) -> Option<CompiledCode> {
107         self.compiled_code.take()
108     }
109 
110     /// Set the flag to request a disassembly when compiling with a
111     /// `MachBackend` backend.
112     pub fn set_disasm(&mut self, val: bool) {
113         self.want_disasm = val;
114     }
115 
116     /// Compile the function, and emit machine code into a `Vec<u8>`.
117     #[deprecated = "use Context::compile"]
118     pub fn compile_and_emit(
119         &mut self,
120         isa: &dyn TargetIsa,
121         mem: &mut Vec<u8>,
122         ctrl_plane: &mut ControlPlane,
123     ) -> CompileResult<'_, &CompiledCode> {
124         let compiled_code = self.compile(isa, ctrl_plane)?;
125         mem.extend_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(
133         &mut self,
134         isa: &dyn TargetIsa,
135         ctrl_plane: &mut ControlPlane,
136     ) -> CodegenResult<CompiledCodeStencil> {
137         let result;
138         trace!("****** START compiling {}", self.func.display_spec());
139         {
140             let _tt = timing::compile();
141 
142             self.verify_if(isa)?;
143             self.optimize(isa, ctrl_plane)?;
144             result = isa.compile_function(&self.func, &self.domtree, self.want_disasm, ctrl_plane);
145         }
146         trace!("****** DONE compiling {}\n", self.func.display_spec());
147         result
148     }
149 
150     /// Optimize the function, performing all compilation steps up to
151     /// but not including machine-code lowering and register
152     /// allocation.
153     ///
154     /// Public only for testing purposes.
155     pub fn optimize(
156         &mut self,
157         isa: &dyn TargetIsa,
158         ctrl_plane: &mut ControlPlane,
159     ) -> CodegenResult<()> {
160         log::debug!(
161             "Number of CLIF instructions to optimize: {}",
162             self.func.dfg.num_insts()
163         );
164         log::debug!(
165             "Number of CLIF blocks to optimize: {}",
166             self.func.dfg.num_blocks()
167         );
168 
169         let opt_level = isa.flags().opt_level();
170         crate::trace!(
171             "Optimizing (opt level {:?}):\n{}",
172             opt_level,
173             self.func.display()
174         );
175 
176         if isa.flags().enable_nan_canonicalization() {
177             self.canonicalize_nans(isa)?;
178         }
179 
180         self.legalize(isa)?;
181 
182         self.compute_cfg();
183         self.compute_domtree();
184         self.eliminate_unreachable_code(isa)?;
185         self.remove_constant_phis(isa)?;
186 
187         self.func.dfg.resolve_all_aliases();
188 
189         if opt_level != OptLevel::None {
190             self.egraph_pass(isa, ctrl_plane)?;
191         }
192 
193         Ok(())
194     }
195 
196     /// Compile the function,
197     ///
198     /// Run the function through all the passes necessary to generate
199     /// code for the target ISA represented by `isa`. The generated
200     /// machine code is not relocated. Instead, any relocations can be
201     /// obtained from `compiled_code.buffer.relocs()`.
202     ///
203     /// Performs any optimizations that are enabled, unless
204     /// `optimize()` was already invoked.
205     ///
206     /// Returns the generated machine code as well as information about
207     /// the function's code and read-only data.
208     pub fn compile(
209         &mut self,
210         isa: &dyn TargetIsa,
211         ctrl_plane: &mut ControlPlane,
212     ) -> CompileResult<'_, &CompiledCode> {
213         let stencil = self
214             .compile_stencil(isa, ctrl_plane)
215             .map_err(|error| CompileError {
216                 inner: error,
217                 func: &self.func,
218             })?;
219         Ok(self
220             .compiled_code
221             .insert(stencil.apply_params(&self.func.params)))
222     }
223 
224     /// If available, return information about the code layout in the
225     /// final machine code: the offsets (in bytes) of each basic-block
226     /// start, and all basic-block edges.
227     #[deprecated = "use CompiledCode::get_code_bb_layout"]
228     pub fn get_code_bb_layout(&self) -> Option<(Vec<usize>, Vec<(usize, usize)>)> {
229         self.compiled_code().map(CompiledCode::get_code_bb_layout)
230     }
231 
232     /// Creates unwind information for the function.
233     ///
234     /// Returns `None` if the function has no unwind information.
235     #[cfg(feature = "unwind")]
236     #[deprecated = "use CompiledCode::create_unwind_info"]
237     pub fn create_unwind_info(
238         &self,
239         isa: &dyn TargetIsa,
240     ) -> CodegenResult<Option<crate::isa::unwind::UnwindInfo>> {
241         self.compiled_code().unwrap().create_unwind_info(isa)
242     }
243 
244     /// Run the verifier on the function.
245     ///
246     /// Also check that the dominator tree and control flow graph are consistent with the function.
247     ///
248     /// TODO: rename to "CLIF validate" or similar.
249     pub fn verify<'a, FOI: Into<FlagsOrIsa<'a>>>(&self, fisa: FOI) -> VerifierResult<()> {
250         let mut errors = VerifierErrors::default();
251         let _ = verify_context(&self.func, &self.cfg, &self.domtree, fisa, &mut errors);
252 
253         if errors.is_empty() {
254             Ok(())
255         } else {
256             Err(errors)
257         }
258     }
259 
260     /// Run the verifier only if the `enable_verifier` setting is true.
261     pub fn verify_if<'a, FOI: Into<FlagsOrIsa<'a>>>(&self, fisa: FOI) -> CodegenResult<()> {
262         let fisa = fisa.into();
263         if fisa.flags.enable_verifier() {
264             self.verify(fisa)?;
265         }
266         Ok(())
267     }
268 
269     /// Perform constant-phi removal on the function.
270     pub fn remove_constant_phis<'a, FOI: Into<FlagsOrIsa<'a>>>(
271         &mut self,
272         fisa: FOI,
273     ) -> CodegenResult<()> {
274         do_remove_constant_phis(&mut self.func, &mut self.domtree);
275         self.verify_if(fisa)?;
276         Ok(())
277     }
278 
279     /// Perform NaN canonicalizing rewrites on the function.
280     pub fn canonicalize_nans(&mut self, isa: &dyn TargetIsa) -> CodegenResult<()> {
281         // Currently only RiscV64 is the only arch that may not have vector support.
282         let has_vector_support = match isa.triple().architecture {
283             Architecture::Riscv64(_) => match isa.isa_flags().iter().find(|f| f.name == "has_v") {
284                 Some(value) => value.as_bool().unwrap_or(false),
285                 None => false,
286             },
287             _ => true,
288         };
289         do_nan_canonicalization(&mut self.func, has_vector_support);
290         self.verify_if(isa)
291     }
292 
293     /// Run the legalizer for `isa` on the function.
294     pub fn legalize(&mut self, isa: &dyn TargetIsa) -> CodegenResult<()> {
295         // Legalization invalidates the domtree and loop_analysis by mutating the CFG.
296         // TODO: Avoid doing this when legalization doesn't actually mutate the CFG.
297         self.domtree.clear();
298         self.loop_analysis.clear();
299         self.cfg.clear();
300 
301         // Run some specific legalizations only.
302         simple_legalize(&mut self.func, isa);
303         self.verify_if(isa)
304     }
305 
306     /// Compute the control flow graph.
307     pub fn compute_cfg(&mut self) {
308         self.cfg.compute(&self.func)
309     }
310 
311     /// Compute dominator tree.
312     pub fn compute_domtree(&mut self) {
313         self.domtree.compute(&self.func, &self.cfg);
314         self.domtree_preorder.compute(&self.domtree);
315     }
316 
317     /// Compute the loop analysis.
318     pub fn compute_loop_analysis(&mut self) {
319         self.loop_analysis
320             .compute(&self.func, &self.cfg, &self.domtree)
321     }
322 
323     /// Compute the control flow graph and dominator tree.
324     pub fn flowgraph(&mut self) {
325         self.compute_cfg();
326         self.compute_domtree()
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(&self.func, &self.domtree_preorder);
345         analysis.compute_and_update_aliases(&mut self.func);
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     /// Run optimizations via the egraph infrastructure.
360     pub fn egraph_pass<'a, FOI>(
361         &mut self,
362         fisa: FOI,
363         ctrl_plane: &mut ControlPlane,
364     ) -> CodegenResult<()>
365     where
366         FOI: Into<FlagsOrIsa<'a>>,
367     {
368         let _tt = timing::egraph();
369 
370         trace!(
371             "About to optimize with egraph phase:\n{}",
372             self.func.display()
373         );
374         let fisa = fisa.into();
375         self.compute_loop_analysis();
376         let mut alias_analysis = AliasAnalysis::new(&self.func, &self.domtree_preorder);
377         let mut pass = EgraphPass::new(
378             &mut self.func,
379             &self.domtree,
380             &self.loop_analysis,
381             &mut alias_analysis,
382             &fisa.flags,
383             ctrl_plane,
384         );
385         pass.run();
386         log::debug!("egraph stats: {:?}", pass.stats);
387         trace!("After egraph optimization:\n{}", self.func.display());
388 
389         self.verify_if(fisa)
390     }
391 }
392