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                &self.loop_analysis,
147                &mut self.regalloc_ctx,
148                self.want_disasm,
149                ctrl_plane,
150            );
151        }
152        trace!("****** DONE compiling {}\n", self.func.display_spec());
153        result
154    }
155
156    /// Optimize the function, performing all compilation steps up to
157    /// but not including machine-code lowering and register
158    /// allocation.
159    ///
160    /// Public only for testing purposes.
161    pub fn optimize(
162        &mut self,
163        isa: &dyn TargetIsa,
164        ctrl_plane: &mut ControlPlane,
165    ) -> CodegenResult<()> {
166        log::debug!(
167            "Number of CLIF instructions to optimize: {}",
168            self.func.dfg.num_insts()
169        );
170        log::debug!(
171            "Number of CLIF blocks to optimize: {}",
172            self.func.dfg.num_blocks()
173        );
174
175        let opt_level = isa.flags().opt_level();
176        crate::trace!(
177            "Optimizing (opt level {:?}):\n{}",
178            opt_level,
179            self.func.display()
180        );
181
182        if isa.flags().enable_nan_canonicalization() {
183            self.canonicalize_nans(isa)?;
184        }
185
186        self.verify_if(isa)?;
187
188        self.compute_cfg();
189        self.compute_domtree();
190        self.eliminate_unreachable_code(isa)?;
191        self.remove_constant_phis(isa)?;
192
193        self.func.dfg.resolve_all_aliases();
194
195        if opt_level != OptLevel::None {
196            self.egraph_pass(isa, ctrl_plane)?;
197        }
198
199        Ok(())
200    }
201
202    /// Perform function call inlining.
203    ///
204    /// Returns `true` if any function call was inlined, `false` otherwise.
205    pub fn inline(&mut self, inliner: impl Inline) -> CodegenResult<bool> {
206        do_inlining(&mut self.func, inliner)
207    }
208
209    /// Compile the function,
210    ///
211    /// Run the function through all the passes necessary to generate
212    /// code for the target ISA represented by `isa`. The generated
213    /// machine code is not relocated. Instead, any relocations can be
214    /// obtained from `compiled_code.buffer.relocs()`.
215    ///
216    /// Performs any optimizations that are enabled, unless
217    /// `optimize()` was already invoked.
218    ///
219    /// Returns the generated machine code as well as information about
220    /// the function's code and read-only data.
221    pub fn compile(
222        &mut self,
223        isa: &dyn TargetIsa,
224        ctrl_plane: &mut ControlPlane,
225    ) -> CompileResult<'_, &CompiledCode> {
226        let stencil = self
227            .compile_stencil(isa, ctrl_plane)
228            .map_err(|error| CompileError {
229                inner: error,
230                func: &self.func,
231            })?;
232        Ok(self
233            .compiled_code
234            .insert(stencil.apply_params(&self.func.params)))
235    }
236
237    /// If available, return information about the code layout in the
238    /// final machine code: the offsets (in bytes) of each basic-block
239    /// start, and all basic-block edges.
240    #[deprecated = "use CompiledCode::get_code_bb_layout"]
241    pub fn get_code_bb_layout(&self) -> Option<(Vec<usize>, Vec<(usize, usize)>)> {
242        self.compiled_code().map(CompiledCode::get_code_bb_layout)
243    }
244
245    /// Creates unwind information for the function.
246    ///
247    /// Returns `None` if the function has no unwind information.
248    #[cfg(feature = "unwind")]
249    #[deprecated = "use CompiledCode::create_unwind_info"]
250    pub fn create_unwind_info(
251        &self,
252        isa: &dyn TargetIsa,
253    ) -> CodegenResult<Option<crate::isa::unwind::UnwindInfo>> {
254        self.compiled_code().unwrap().create_unwind_info(isa)
255    }
256
257    /// Run the verifier on the function.
258    ///
259    /// Also check that the dominator tree and control flow graph are consistent with the function.
260    ///
261    /// TODO: rename to "CLIF validate" or similar.
262    pub fn verify<'a, FOI: Into<FlagsOrIsa<'a>>>(&self, fisa: FOI) -> VerifierResult<()> {
263        let mut errors = VerifierErrors::default();
264        let _ = verify_context(&self.func, &self.cfg, &self.domtree, fisa, &mut errors);
265
266        if errors.is_empty() {
267            Ok(())
268        } else {
269            Err(errors)
270        }
271    }
272
273    /// Run the verifier only if the `enable_verifier` setting is true.
274    pub fn verify_if<'a, FOI: Into<FlagsOrIsa<'a>>>(&self, fisa: FOI) -> CodegenResult<()> {
275        let fisa = fisa.into();
276        if fisa.flags.enable_verifier() {
277            self.verify(fisa)?;
278        }
279        Ok(())
280    }
281
282    /// Perform constant-phi removal on the function.
283    pub fn remove_constant_phis<'a, FOI: Into<FlagsOrIsa<'a>>>(
284        &mut self,
285        fisa: FOI,
286    ) -> CodegenResult<()> {
287        do_remove_constant_phis(&mut self.func, &mut self.domtree);
288        self.verify_if(fisa)?;
289        Ok(())
290    }
291
292    /// Perform NaN canonicalizing rewrites on the function.
293    pub fn canonicalize_nans(&mut self, isa: &dyn TargetIsa) -> CodegenResult<()> {
294        // Currently only RiscV64 is the only arch that may not have vector support.
295        let has_vector_support = match isa.triple().architecture {
296            Architecture::Riscv64(_) => match isa.isa_flags().iter().find(|f| f.name == "has_v") {
297                Some(value) => value.as_bool().unwrap_or(false),
298                None => false,
299            },
300            _ => true,
301        };
302        do_nan_canonicalization(&mut self.func, has_vector_support);
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    }
315
316    /// Compute the loop analysis.
317    pub fn compute_loop_analysis(&mut self) {
318        self.loop_analysis
319            .compute(&self.func, &self.cfg, &self.domtree)
320    }
321
322    /// Compute the control flow graph and dominator tree.
323    pub fn flowgraph(&mut self) {
324        self.compute_cfg();
325        self.compute_domtree()
326    }
327
328    /// Perform unreachable code elimination.
329    pub fn eliminate_unreachable_code<'a, FOI>(&mut self, fisa: FOI) -> CodegenResult<()>
330    where
331        FOI: Into<FlagsOrIsa<'a>>,
332    {
333        let domtree = &self.domtree;
334        eliminate_unreachable_code(&mut self.func, &mut self.cfg, |block| {
335            domtree.is_reachable(block)
336        });
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, &self.cfg);
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            ctrl_plane,
385            &mut self.cfg,
386        );
387        pass.run();
388        log::debug!("egraph stats: {:?}", pass.stats);
389        trace!("After egraph optimization:\n{}", self.func.display());
390
391        // Branch optimizations can invalidate these; recompute them.
392        self.compute_cfg();
393        self.compute_domtree();
394        self.compute_loop_analysis();
395
396        self.verify_if(fisa)?;
397
398        Ok(())
399    }
400}