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