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