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