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::egraph::EgraphPass;
15use crate::flowgraph::ControlFlowGraph;
16use crate::inline::{Inline, do_inlining};
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    /// 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
60impl 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}