cranelift_codegen/
context.rs

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
12use crate::alias_analysis::AliasAnalysis;
13use crate::dce::do_dce;
14use crate::dominator_tree::DominatorTree;
15use crate::egraph::EgraphPass;
16use crate::flowgraph::ControlFlowGraph;
17use crate::ir::Function;
18use crate::isa::TargetIsa;
19use crate::legalizer::simple_legalize;
20use crate::licm::do_licm;
21use crate::loop_analysis::LoopAnalysis;
22use crate::machinst::{CompiledCode, CompiledCodeStencil};
23use crate::nan_canonicalization::do_nan_canonicalization;
24use crate::remove_constant_phis::do_remove_constant_phis;
25use crate::result::{CodegenResult, CompileResult};
26use crate::settings::{FlagsOrIsa, OptLevel};
27use crate::simple_gvn::do_simple_gvn;
28use crate::simple_preopt::do_preopt;
29use crate::trace;
30use crate::unreachable_code::eliminate_unreachable_code;
31use crate::verifier::{verify_context, VerifierErrors, VerifierResult};
32use crate::{timing, CompileError};
33#[cfg(feature = "souper-harvest")]
34use alloc::string::String;
35use alloc::vec::Vec;
36
37#[cfg(feature = "souper-harvest")]
38use crate::souper_harvest::do_souper_harvest;
39
40/// Persistent data structures and compilation pipeline.
41pub 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
61impl 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 stencil = self.compile_stencil(isa).map_err(|error| CompileError {
217            inner: error,
218            func: &self.func,
219        })?;
220        Ok(self
221            .compiled_code
222            .insert(stencil.apply_params(&self.func.params)))
223    }
224
225    /// If available, return information about the code layout in the
226    /// final machine code: the offsets (in bytes) of each basic-block
227    /// start, and all basic-block edges.
228    #[deprecated = "use CompiledCode::get_code_bb_layout"]
229    pub fn get_code_bb_layout(&self) -> Option<(Vec<usize>, Vec<(usize, usize)>)> {
230        self.compiled_code().map(CompiledCode::get_code_bb_layout)
231    }
232
233    /// Creates unwind information for the function.
234    ///
235    /// Returns `None` if the function has no unwind information.
236    #[cfg(feature = "unwind")]
237    #[deprecated = "use CompiledCode::create_unwind_info"]
238    pub fn create_unwind_info(
239        &self,
240        isa: &dyn TargetIsa,
241    ) -> CodegenResult<Option<crate::isa::unwind::UnwindInfo>> {
242        self.compiled_code().unwrap().create_unwind_info(isa)
243    }
244
245    /// Run the verifier on the function.
246    ///
247    /// Also check that the dominator tree and control flow graph are consistent with the function.
248    pub fn verify<'a, FOI: Into<FlagsOrIsa<'a>>>(&self, fisa: FOI) -> VerifierResult<()> {
249        let mut errors = VerifierErrors::default();
250        let _ = verify_context(&self.func, &self.cfg, &self.domtree, fisa, &mut errors);
251
252        if errors.is_empty() {
253            Ok(())
254        } else {
255            Err(errors)
256        }
257    }
258
259    /// Run the verifier only if the `enable_verifier` setting is true.
260    pub fn verify_if<'a, FOI: Into<FlagsOrIsa<'a>>>(&self, fisa: FOI) -> CodegenResult<()> {
261        let fisa = fisa.into();
262        if fisa.flags.enable_verifier() {
263            self.verify(fisa)?;
264        }
265        Ok(())
266    }
267
268    /// Perform dead-code elimination on the function.
269    pub fn dce<'a, FOI: Into<FlagsOrIsa<'a>>>(&mut self, fisa: FOI) -> CodegenResult<()> {
270        do_dce(&mut self.func, &mut self.domtree);
271        self.verify_if(fisa)?;
272        Ok(())
273    }
274
275    /// Perform constant-phi removal on the function.
276    pub fn remove_constant_phis<'a, FOI: Into<FlagsOrIsa<'a>>>(
277        &mut self,
278        fisa: FOI,
279    ) -> CodegenResult<()> {
280        do_remove_constant_phis(&mut self.func, &mut self.domtree);
281        self.verify_if(fisa)?;
282        Ok(())
283    }
284
285    /// Perform pre-legalization rewrites on the function.
286    pub fn preopt(&mut self, isa: &dyn TargetIsa) -> CodegenResult<()> {
287        do_preopt(&mut self.func, isa);
288        self.verify_if(isa)?;
289        Ok(())
290    }
291
292    /// Perform NaN canonicalizing rewrites on the function.
293    pub fn canonicalize_nans(&mut self, isa: &dyn TargetIsa) -> CodegenResult<()> {
294        do_nan_canonicalization(&mut self.func);
295        self.verify_if(isa)
296    }
297
298    /// Run the legalizer for `isa` on the function.
299    pub fn legalize(&mut self, isa: &dyn TargetIsa) -> CodegenResult<()> {
300        // Legalization invalidates the domtree and loop_analysis by mutating the CFG.
301        // TODO: Avoid doing this when legalization doesn't actually mutate the CFG.
302        self.domtree.clear();
303        self.loop_analysis.clear();
304
305        // Run some specific legalizations only.
306        simple_legalize(&mut self.func, &mut self.cfg, isa);
307        self.verify_if(isa)
308    }
309
310    /// Compute the control flow graph.
311    pub fn compute_cfg(&mut self) {
312        self.cfg.compute(&self.func)
313    }
314
315    /// Compute dominator tree.
316    pub fn compute_domtree(&mut self) {
317        self.domtree.compute(&self.func, &self.cfg)
318    }
319
320    /// Compute the loop analysis.
321    pub fn compute_loop_analysis(&mut self) {
322        self.loop_analysis
323            .compute(&self.func, &self.cfg, &self.domtree)
324    }
325
326    /// Compute the control flow graph and dominator tree.
327    pub fn flowgraph(&mut self) {
328        self.compute_cfg();
329        self.compute_domtree()
330    }
331
332    /// Perform simple GVN on the function.
333    pub fn simple_gvn<'a, FOI: Into<FlagsOrIsa<'a>>>(&mut self, fisa: FOI) -> CodegenResult<()> {
334        do_simple_gvn(&mut self.func, &mut self.domtree);
335        self.verify_if(fisa)
336    }
337
338    /// Perform LICM on the function.
339    pub fn licm(&mut self, isa: &dyn TargetIsa) -> CodegenResult<()> {
340        do_licm(
341            &mut self.func,
342            &mut self.cfg,
343            &mut self.domtree,
344            &mut self.loop_analysis,
345        );
346        self.verify_if(isa)
347    }
348
349    /// Perform unreachable code elimination.
350    pub fn eliminate_unreachable_code<'a, FOI>(&mut self, fisa: FOI) -> CodegenResult<()>
351    where
352        FOI: Into<FlagsOrIsa<'a>>,
353    {
354        eliminate_unreachable_code(&mut self.func, &mut self.cfg, &self.domtree);
355        self.verify_if(fisa)
356    }
357
358    /// Replace all redundant loads with the known values in
359    /// memory. These are loads whose values were already loaded by
360    /// other loads earlier, as well as loads whose values were stored
361    /// by a store instruction to the same instruction (so-called
362    /// "store-to-load forwarding").
363    pub fn replace_redundant_loads(&mut self) -> CodegenResult<()> {
364        let mut analysis = AliasAnalysis::new(&self.func, &self.domtree);
365        analysis.compute_and_update_aliases(&mut self.func);
366        Ok(())
367    }
368
369    /// Harvest candidate left-hand sides for superoptimization with Souper.
370    #[cfg(feature = "souper-harvest")]
371    pub fn souper_harvest(
372        &mut self,
373        out: &mut std::sync::mpsc::Sender<String>,
374    ) -> CodegenResult<()> {
375        do_souper_harvest(&self.func, out);
376        Ok(())
377    }
378
379    /// Run optimizations via the egraph infrastructure.
380    pub fn egraph_pass(&mut self) -> CodegenResult<()> {
381        let _tt = timing::egraph();
382
383        trace!(
384            "About to optimize with egraph phase:\n{}",
385            self.func.display()
386        );
387        self.compute_loop_analysis();
388        let mut alias_analysis = AliasAnalysis::new(&self.func, &self.domtree);
389        let mut pass = EgraphPass::new(
390            &mut self.func,
391            &self.domtree,
392            &self.loop_analysis,
393            &mut alias_analysis,
394        );
395        pass.run();
396        log::debug!("egraph stats: {:?}", pass.stats);
397        trace!("After egraph optimization:\n{}", self.func.display());
398        Ok(())
399    }
400}