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 
12 use crate::alias_analysis::AliasAnalysis;
13 use crate::binemit::CodeInfo;
14 use crate::dce::do_dce;
15 use crate::dominator_tree::DominatorTree;
16 use crate::flowgraph::ControlFlowGraph;
17 use crate::ir::Function;
18 use crate::isa::TargetIsa;
19 use crate::legalizer::simple_legalize;
20 use crate::licm::do_licm;
21 use crate::loop_analysis::LoopAnalysis;
22 use crate::machinst::MachCompileResult;
23 use crate::nan_canonicalization::do_nan_canonicalization;
24 use crate::remove_constant_phis::do_remove_constant_phis;
25 use crate::result::CodegenResult;
26 use crate::settings::{FlagsOrIsa, OptLevel};
27 use crate::simple_gvn::do_simple_gvn;
28 use crate::simple_preopt::do_preopt;
29 use crate::timing;
30 use crate::unreachable_code::eliminate_unreachable_code;
31 use crate::verifier::{verify_context, VerifierErrors, VerifierResult};
32 #[cfg(feature = "souper-harvest")]
33 use alloc::string::String;
34 use alloc::vec::Vec;
35 
36 #[cfg(feature = "souper-harvest")]
37 use crate::souper_harvest::do_souper_harvest;
38 
39 /// Persistent data structures and compilation pipeline.
40 pub 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 mach_compile_result: Option<MachCompileResult>,
55 
56     /// Flag: do we want a disassembly with the MachCompileResult?
57     pub want_disasm: bool,
58 }
59 
60 impl 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             mach_compile_result: 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.mach_compile_result = None;
91         self.want_disasm = false;
92     }
93 
94     /// Set the flag to request a disassembly when compiling with a
95     /// `MachBackend` backend.
96     pub fn set_disasm(&mut self, val: bool) {
97         self.want_disasm = val;
98     }
99 
100     /// Compile the function, and emit machine code into a `Vec<u8>`.
101     ///
102     /// Run the function through all the passes necessary to generate code for the target ISA
103     /// represented by `isa`, as well as the final step of emitting machine code into a
104     /// `Vec<u8>`. The machine code is not relocated. Instead, any relocations can be obtained
105     /// from `mach_compile_result`.
106     ///
107     /// This function calls `compile` and `emit_to_memory`, taking care to resize `mem` as
108     /// needed, so it provides a safe interface.
109     ///
110     /// Returns information about the function's code and read-only data.
111     pub fn compile_and_emit(
112         &mut self,
113         isa: &dyn TargetIsa,
114         mem: &mut Vec<u8>,
115     ) -> CodegenResult<()> {
116         let info = self.compile(isa)?;
117         let old_len = mem.len();
118         mem.resize(old_len + info.total_size as usize, 0);
119         let new_info = unsafe { self.emit_to_memory(mem.as_mut_ptr().add(old_len)) };
120         debug_assert!(new_info == info);
121         Ok(())
122     }
123 
124     /// Compile the function.
125     ///
126     /// Run the function through all the passes necessary to generate code for the target ISA
127     /// represented by `isa`. This does not include the final step of emitting machine code into a
128     /// code sink.
129     ///
130     /// Returns information about the function's code and read-only data.
131     pub fn compile(&mut self, isa: &dyn TargetIsa) -> CodegenResult<CodeInfo> {
132         let _tt = timing::compile();
133         self.verify_if(isa)?;
134 
135         let opt_level = isa.flags().opt_level();
136         log::debug!(
137             "Compiling (opt level {:?}):\n{}",
138             opt_level,
139             self.func.display()
140         );
141 
142         self.compute_cfg();
143         if opt_level != OptLevel::None {
144             self.preopt(isa)?;
145         }
146         if isa.flags().enable_nan_canonicalization() {
147             self.canonicalize_nans(isa)?;
148         }
149 
150         self.legalize(isa)?;
151         if opt_level != OptLevel::None {
152             self.compute_domtree();
153             self.compute_loop_analysis();
154             self.licm(isa)?;
155             self.simple_gvn(isa)?;
156         }
157 
158         self.compute_domtree();
159         self.eliminate_unreachable_code(isa)?;
160         if opt_level != OptLevel::None {
161             self.dce(isa)?;
162         }
163 
164         self.remove_constant_phis(isa)?;
165 
166         if opt_level != OptLevel::None {
167             self.replace_redundant_loads()?;
168             self.simple_gvn(isa)?;
169         }
170 
171         let result = isa.compile_function(&self.func, self.want_disasm)?;
172         let info = result.code_info();
173         self.mach_compile_result = Some(result);
174         Ok(info)
175     }
176 
177     /// Emit machine code directly into raw memory.
178     ///
179     /// Write all of the function's machine code to the memory at `mem`. The size of the machine
180     /// code is returned by `compile` above.
181     ///
182     /// The machine code is not relocated.
183     /// Instead, any relocations can be obtained from `mach_compile_result`.
184     ///
185     /// # Safety
186     ///
187     /// This function is unsafe since it does not perform bounds checking on the memory buffer,
188     /// and it can't guarantee that the `mem` pointer is valid.
189     ///
190     /// Returns information about the emitted code and data.
191     #[deny(unsafe_op_in_unsafe_fn)]
192     pub unsafe fn emit_to_memory(&self, mem: *mut u8) -> CodeInfo {
193         let _tt = timing::binemit();
194         let result = self
195             .mach_compile_result
196             .as_ref()
197             .expect("only using mach backend now");
198         let info = result.code_info();
199 
200         let mem = unsafe { std::slice::from_raw_parts_mut(mem, info.total_size as usize) };
201         mem.copy_from_slice(result.buffer.data());
202 
203         info
204     }
205 
206     /// If available, return information about the code layout in the
207     /// final machine code: the offsets (in bytes) of each basic-block
208     /// start, and all basic-block edges.
209     pub fn get_code_bb_layout(&self) -> Option<(Vec<usize>, Vec<(usize, usize)>)> {
210         if let Some(result) = self.mach_compile_result.as_ref() {
211             Some((
212                 result.bb_starts.iter().map(|&off| off as usize).collect(),
213                 result
214                     .bb_edges
215                     .iter()
216                     .map(|&(from, to)| (from as usize, to as usize))
217                     .collect(),
218             ))
219         } else {
220             None
221         }
222     }
223 
224     /// Creates unwind information for the function.
225     ///
226     /// Returns `None` if the function has no unwind information.
227     #[cfg(feature = "unwind")]
228     pub fn create_unwind_info(
229         &self,
230         isa: &dyn TargetIsa,
231     ) -> CodegenResult<Option<crate::isa::unwind::UnwindInfo>> {
232         let unwind_info_kind = isa.unwind_info_kind();
233         let result = self.mach_compile_result.as_ref().unwrap();
234         isa.emit_unwind_info(result, unwind_info_kind)
235     }
236 
237     /// Run the verifier on the function.
238     ///
239     /// Also check that the dominator tree and control flow graph are consistent with the function.
240     pub fn verify<'a, FOI: Into<FlagsOrIsa<'a>>>(&self, fisa: FOI) -> VerifierResult<()> {
241         let mut errors = VerifierErrors::default();
242         let _ = verify_context(&self.func, &self.cfg, &self.domtree, fisa, &mut errors);
243 
244         if errors.is_empty() {
245             Ok(())
246         } else {
247             Err(errors)
248         }
249     }
250 
251     /// Run the verifier only if the `enable_verifier` setting is true.
252     pub fn verify_if<'a, FOI: Into<FlagsOrIsa<'a>>>(&self, fisa: FOI) -> CodegenResult<()> {
253         let fisa = fisa.into();
254         if fisa.flags.enable_verifier() {
255             self.verify(fisa)?;
256         }
257         Ok(())
258     }
259 
260     /// Perform dead-code elimination on the function.
261     pub fn dce<'a, FOI: Into<FlagsOrIsa<'a>>>(&mut self, fisa: FOI) -> CodegenResult<()> {
262         do_dce(&mut self.func, &mut self.domtree);
263         self.verify_if(fisa)?;
264         Ok(())
265     }
266 
267     /// Perform constant-phi removal on the function.
268     pub fn remove_constant_phis<'a, FOI: Into<FlagsOrIsa<'a>>>(
269         &mut self,
270         fisa: FOI,
271     ) -> CodegenResult<()> {
272         do_remove_constant_phis(&mut self.func, &mut self.domtree);
273         self.verify_if(fisa)?;
274         Ok(())
275     }
276 
277     /// Perform pre-legalization rewrites on the function.
278     pub fn preopt(&mut self, isa: &dyn TargetIsa) -> CodegenResult<()> {
279         do_preopt(&mut self.func, &mut self.cfg, isa);
280         self.verify_if(isa)?;
281         Ok(())
282     }
283 
284     /// Perform NaN canonicalizing rewrites on the function.
285     pub fn canonicalize_nans(&mut self, isa: &dyn TargetIsa) -> CodegenResult<()> {
286         do_nan_canonicalization(&mut self.func);
287         self.verify_if(isa)
288     }
289 
290     /// Run the legalizer for `isa` on the function.
291     pub fn legalize(&mut self, isa: &dyn TargetIsa) -> CodegenResult<()> {
292         // Legalization invalidates the domtree and loop_analysis by mutating the CFG.
293         // TODO: Avoid doing this when legalization doesn't actually mutate the CFG.
294         self.domtree.clear();
295         self.loop_analysis.clear();
296 
297         // Run some specific legalizations only.
298         simple_legalize(&mut self.func, &mut self.cfg, isa);
299         self.verify_if(isa)
300     }
301 
302     /// Compute the control flow graph.
303     pub fn compute_cfg(&mut self) {
304         self.cfg.compute(&self.func)
305     }
306 
307     /// Compute dominator tree.
308     pub fn compute_domtree(&mut self) {
309         self.domtree.compute(&self.func, &self.cfg)
310     }
311 
312     /// Compute the loop analysis.
313     pub fn compute_loop_analysis(&mut self) {
314         self.loop_analysis
315             .compute(&self.func, &self.cfg, &self.domtree)
316     }
317 
318     /// Compute the control flow graph and dominator tree.
319     pub fn flowgraph(&mut self) {
320         self.compute_cfg();
321         self.compute_domtree()
322     }
323 
324     /// Perform simple GVN on the function.
325     pub fn simple_gvn<'a, FOI: Into<FlagsOrIsa<'a>>>(&mut self, fisa: FOI) -> CodegenResult<()> {
326         do_simple_gvn(&mut self.func, &mut self.domtree);
327         self.verify_if(fisa)
328     }
329 
330     /// Perform LICM on the function.
331     pub fn licm(&mut self, isa: &dyn TargetIsa) -> CodegenResult<()> {
332         do_licm(
333             &mut self.func,
334             &mut self.cfg,
335             &mut self.domtree,
336             &mut self.loop_analysis,
337         );
338         self.verify_if(isa)
339     }
340 
341     /// Perform unreachable code elimination.
342     pub fn eliminate_unreachable_code<'a, FOI>(&mut self, fisa: FOI) -> CodegenResult<()>
343     where
344         FOI: Into<FlagsOrIsa<'a>>,
345     {
346         eliminate_unreachable_code(&mut self.func, &mut self.cfg, &self.domtree);
347         self.verify_if(fisa)
348     }
349 
350     /// Replace all redundant loads with the known values in
351     /// memory. These are loads whose values were already loaded by
352     /// other loads earlier, as well as loads whose values were stored
353     /// by a store instruction to the same instruction (so-called
354     /// "store-to-load forwarding").
355     pub fn replace_redundant_loads(&mut self) -> CodegenResult<()> {
356         let mut analysis = AliasAnalysis::new(&mut self.func, &self.domtree);
357         analysis.compute_and_update_aliases();
358         Ok(())
359     }
360 
361     /// Harvest candidate left-hand sides for superoptimization with Souper.
362     #[cfg(feature = "souper-harvest")]
363     pub fn souper_harvest(
364         &mut self,
365         out: &mut std::sync::mpsc::Sender<String>,
366     ) -> CodegenResult<()> {
367         do_souper_harvest(&self.func, out);
368         Ok(())
369     }
370 }
371