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