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