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::egraph::EgraphPass;
15 use crate::flowgraph::ControlFlowGraph;
16 use crate::inline::{Inline, do_inlining};
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     /// 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     /// Returns the compilation result for this function, available after any `compile` function
101     /// has been called.
102     pub fn take_compiled_code(&mut self) -> Option<CompiledCode> {
103         self.compiled_code.take()
104     }
105 
106     /// Set the flag to request a disassembly when compiling with a
107     /// `MachBackend` backend.
108     pub fn set_disasm(&mut self, val: bool) {
109         self.want_disasm = val;
110     }
111 
112     /// Compile the function, and emit machine code into a `Vec<u8>`.
113     #[deprecated = "use Context::compile"]
114     pub fn compile_and_emit(
115         &mut self,
116         isa: &dyn TargetIsa,
117         mem: &mut Vec<u8>,
118         ctrl_plane: &mut ControlPlane,
119     ) -> CompileResult<'_, &CompiledCode> {
120         let compiled_code = self.compile(isa, ctrl_plane)?;
121         mem.extend_from_slice(compiled_code.code_buffer());
122         Ok(compiled_code)
123     }
124 
125     /// Internally compiles the function into a stencil.
126     ///
127     /// Public only for testing and fuzzing purposes.
128     pub fn compile_stencil(
129         &mut self,
130         isa: &dyn TargetIsa,
131         ctrl_plane: &mut ControlPlane,
132     ) -> CodegenResult<CompiledCodeStencil> {
133         let result;
134         trace!("****** START compiling {}", self.func.display_spec());
135         {
136             let _tt = timing::compile();
137 
138             self.verify_if(isa)?;
139             self.optimize(isa, ctrl_plane)?;
140             result = isa.compile_function(&self.func, &self.domtree, self.want_disasm, ctrl_plane);
141         }
142         trace!("****** DONE compiling {}\n", self.func.display_spec());
143         result
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(
152         &mut self,
153         isa: &dyn TargetIsa,
154         ctrl_plane: &mut ControlPlane,
155     ) -> CodegenResult<()> {
156         log::debug!(
157             "Number of CLIF instructions to optimize: {}",
158             self.func.dfg.num_insts()
159         );
160         log::debug!(
161             "Number of CLIF blocks to optimize: {}",
162             self.func.dfg.num_blocks()
163         );
164 
165         let opt_level = isa.flags().opt_level();
166         crate::trace!(
167             "Optimizing (opt level {:?}):\n{}",
168             opt_level,
169             self.func.display()
170         );
171 
172         if isa.flags().enable_nan_canonicalization() {
173             self.canonicalize_nans(isa)?;
174         }
175 
176         self.legalize(isa)?;
177 
178         self.compute_cfg();
179         self.compute_domtree();
180         self.eliminate_unreachable_code(isa)?;
181         self.remove_constant_phis(isa)?;
182 
183         self.func.dfg.resolve_all_aliases();
184 
185         if opt_level != OptLevel::None {
186             self.egraph_pass(isa, ctrl_plane)?;
187         }
188 
189         Ok(())
190     }
191 
192     /// Perform function call inlining.
193     ///
194     /// Returns `true` if any function call was inlined, `false` otherwise.
195     pub fn inline(&mut self, inliner: impl Inline) -> CodegenResult<bool> {
196         do_inlining(&mut self.func, inliner)
197     }
198 
199     /// Compile the function,
200     ///
201     /// Run the function through all the passes necessary to generate
202     /// code for the target ISA represented by `isa`. The generated
203     /// machine code is not relocated. Instead, any relocations can be
204     /// obtained from `compiled_code.buffer.relocs()`.
205     ///
206     /// Performs any optimizations that are enabled, unless
207     /// `optimize()` was already invoked.
208     ///
209     /// Returns the generated machine code as well as information about
210     /// the function's code and read-only data.
211     pub fn compile(
212         &mut self,
213         isa: &dyn TargetIsa,
214         ctrl_plane: &mut ControlPlane,
215     ) -> CompileResult<'_, &CompiledCode> {
216         let stencil = self
217             .compile_stencil(isa, ctrl_plane)
218             .map_err(|error| CompileError {
219                 inner: error,
220                 func: &self.func,
221             })?;
222         Ok(self
223             .compiled_code
224             .insert(stencil.apply_params(&self.func.params)))
225     }
226 
227     /// If available, return information about the code layout in the
228     /// final machine code: the offsets (in bytes) of each basic-block
229     /// start, and all basic-block edges.
230     #[deprecated = "use CompiledCode::get_code_bb_layout"]
231     pub fn get_code_bb_layout(&self) -> Option<(Vec<usize>, Vec<(usize, usize)>)> {
232         self.compiled_code().map(CompiledCode::get_code_bb_layout)
233     }
234 
235     /// Creates unwind information for the function.
236     ///
237     /// Returns `None` if the function has no unwind information.
238     #[cfg(feature = "unwind")]
239     #[deprecated = "use CompiledCode::create_unwind_info"]
240     pub fn create_unwind_info(
241         &self,
242         isa: &dyn TargetIsa,
243     ) -> CodegenResult<Option<crate::isa::unwind::UnwindInfo>> {
244         self.compiled_code().unwrap().create_unwind_info(isa)
245     }
246 
247     /// Run the verifier on the function.
248     ///
249     /// Also check that the dominator tree and control flow graph are consistent with the function.
250     ///
251     /// TODO: rename to "CLIF validate" or similar.
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 constant-phi removal on the function.
273     pub fn remove_constant_phis<'a, FOI: Into<FlagsOrIsa<'a>>>(
274         &mut self,
275         fisa: FOI,
276     ) -> CodegenResult<()> {
277         do_remove_constant_phis(&mut self.func, &mut self.domtree);
278         self.verify_if(fisa)?;
279         Ok(())
280     }
281 
282     /// Perform NaN canonicalizing rewrites on the function.
283     pub fn canonicalize_nans(&mut self, isa: &dyn TargetIsa) -> CodegenResult<()> {
284         // Currently only RiscV64 is the only arch that may not have vector support.
285         let has_vector_support = match isa.triple().architecture {
286             Architecture::Riscv64(_) => match isa.isa_flags().iter().find(|f| f.name == "has_v") {
287                 Some(value) => value.as_bool().unwrap_or(false),
288                 None => false,
289             },
290             _ => true,
291         };
292         do_nan_canonicalization(&mut self.func, has_vector_support);
293         self.verify_if(isa)
294     }
295 
296     /// Run the legalizer for `isa` on the function.
297     pub fn legalize(&mut self, isa: &dyn TargetIsa) -> CodegenResult<()> {
298         // Legalization invalidates the domtree and loop_analysis by mutating the CFG.
299         // TODO: Avoid doing this when legalization doesn't actually mutate the CFG.
300         self.domtree.clear();
301         self.loop_analysis.clear();
302         self.cfg.clear();
303 
304         // Run some specific legalizations only.
305         simple_legalize(&mut self.func, isa);
306         self.verify_if(isa)
307     }
308 
309     /// Compute the control flow graph.
310     pub fn compute_cfg(&mut self) {
311         self.cfg.compute(&self.func)
312     }
313 
314     /// Compute dominator tree.
315     pub fn compute_domtree(&mut self) {
316         self.domtree.compute(&self.func, &self.cfg);
317     }
318 
319     /// Compute the loop analysis.
320     pub fn compute_loop_analysis(&mut self) {
321         self.loop_analysis
322             .compute(&self.func, &self.cfg, &self.domtree)
323     }
324 
325     /// Compute the control flow graph and dominator tree.
326     pub fn flowgraph(&mut self) {
327         self.compute_cfg();
328         self.compute_domtree()
329     }
330 
331     /// Perform unreachable code elimination.
332     pub fn eliminate_unreachable_code<'a, FOI>(&mut self, fisa: FOI) -> CodegenResult<()>
333     where
334         FOI: Into<FlagsOrIsa<'a>>,
335     {
336         eliminate_unreachable_code(&mut self.func, &mut self.cfg, &self.domtree);
337         self.verify_if(fisa)
338     }
339 
340     /// Replace all redundant loads with the known values in
341     /// memory. These are loads whose values were already loaded by
342     /// other loads earlier, as well as loads whose values were stored
343     /// by a store instruction to the same instruction (so-called
344     /// "store-to-load forwarding").
345     pub fn replace_redundant_loads(&mut self) -> CodegenResult<()> {
346         let mut analysis = AliasAnalysis::new(&self.func, &self.domtree);
347         analysis.compute_and_update_aliases(&mut self.func);
348         Ok(())
349     }
350 
351     /// Harvest candidate left-hand sides for superoptimization with Souper.
352     #[cfg(feature = "souper-harvest")]
353     pub fn souper_harvest(
354         &mut self,
355         out: &mut std::sync::mpsc::Sender<String>,
356     ) -> CodegenResult<()> {
357         do_souper_harvest(&self.func, out);
358         Ok(())
359     }
360 
361     /// Run optimizations via the egraph infrastructure.
362     pub fn egraph_pass<'a, FOI>(
363         &mut self,
364         fisa: FOI,
365         ctrl_plane: &mut ControlPlane,
366     ) -> CodegenResult<()>
367     where
368         FOI: Into<FlagsOrIsa<'a>>,
369     {
370         let _tt = timing::egraph();
371 
372         trace!(
373             "About to optimize with egraph phase:\n{}",
374             self.func.display()
375         );
376         let fisa = fisa.into();
377         self.compute_loop_analysis();
378         let mut alias_analysis = AliasAnalysis::new(&self.func, &self.domtree);
379         let mut pass = EgraphPass::new(
380             &mut self.func,
381             &self.domtree,
382             &self.loop_analysis,
383             &mut alias_analysis,
384             &fisa.flags,
385             ctrl_plane,
386         );
387         pass.run();
388         log::debug!("egraph stats: {:?}", pass.stats);
389         trace!("After egraph optimization:\n{}", self.func.display());
390 
391         self.verify_if(fisa)
392     }
393 }
394