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