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