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