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