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