Skip to main content

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