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::egraph::EgraphPass; 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::{CompiledCode, CompiledCodeStencil}; 23 use crate::nan_canonicalization::do_nan_canonicalization; 24 use crate::remove_constant_phis::do_remove_constant_phis; 25 use crate::result::{CodegenResult, CompileResult}; 26 use crate::settings::{FlagsOrIsa, OptLevel}; 27 use crate::simple_gvn::do_simple_gvn; 28 use crate::simple_preopt::do_preopt; 29 use crate::trace; 30 use crate::unreachable_code::eliminate_unreachable_code; 31 use crate::verifier::{verify_context, VerifierErrors, VerifierResult}; 32 use crate::{timing, CompileError}; 33 #[cfg(feature = "souper-harvest")] 34 use alloc::string::String; 35 use alloc::vec::Vec; 36 37 #[cfg(feature = "souper-harvest")] 38 use crate::souper_harvest::do_souper_harvest; 39 40 /// Persistent data structures and compilation pipeline. 41 pub struct Context { 42 /// The function we're compiling. 43 pub func: Function, 44 45 /// The control flow graph of `func`. 46 pub cfg: ControlFlowGraph, 47 48 /// Dominator tree for `func`. 49 pub domtree: DominatorTree, 50 51 /// Loop analysis of `func`. 52 pub loop_analysis: LoopAnalysis, 53 54 /// Result of MachBackend compilation, if computed. 55 pub(crate) compiled_code: Option<CompiledCode>, 56 57 /// Flag: do we want a disassembly with the CompiledCode? 58 pub want_disasm: bool, 59 } 60 61 impl Context { 62 /// Allocate a new compilation context. 63 /// 64 /// The returned instance should be reused for compiling multiple functions in order to avoid 65 /// needless allocator thrashing. 66 pub fn new() -> Self { 67 Self::for_function(Function::new()) 68 } 69 70 /// Allocate a new compilation context with an existing Function. 71 /// 72 /// The returned instance should be reused for compiling multiple functions in order to avoid 73 /// needless allocator thrashing. 74 pub fn for_function(func: Function) -> Self { 75 Self { 76 func, 77 cfg: ControlFlowGraph::new(), 78 domtree: DominatorTree::new(), 79 loop_analysis: LoopAnalysis::new(), 80 compiled_code: None, 81 want_disasm: false, 82 } 83 } 84 85 /// Clear all data structures in this context. 86 pub fn clear(&mut self) { 87 self.func.clear(); 88 self.cfg.clear(); 89 self.domtree.clear(); 90 self.loop_analysis.clear(); 91 self.compiled_code = None; 92 self.want_disasm = false; 93 } 94 95 /// Returns the compilation result for this function, available after any `compile` function 96 /// has been called. 97 pub fn compiled_code(&self) -> Option<&CompiledCode> { 98 self.compiled_code.as_ref() 99 } 100 101 /// Set the flag to request a disassembly when compiling with a 102 /// `MachBackend` backend. 103 pub fn set_disasm(&mut self, val: bool) { 104 self.want_disasm = val; 105 } 106 107 /// Compile the function, and emit machine code into a `Vec<u8>`. 108 /// 109 /// Run the function through all the passes necessary to generate 110 /// code for the target ISA represented by `isa`, as well as the 111 /// final step of emitting machine code into a `Vec<u8>`. The 112 /// machine code is not relocated. Instead, any relocations can be 113 /// obtained from `compiled_code()`. 114 /// 115 /// Performs any optimizations that are enabled, unless 116 /// `optimize()` was already invoked. 117 /// 118 /// This function calls `compile`, taking care to resize `mem` as 119 /// needed. 120 /// 121 /// Returns information about the function's code and read-only 122 /// data. 123 pub fn compile_and_emit( 124 &mut self, 125 isa: &dyn TargetIsa, 126 mem: &mut Vec<u8>, 127 ) -> CompileResult<&CompiledCode> { 128 let compiled_code = self.compile(isa)?; 129 mem.extend_from_slice(compiled_code.code_buffer()); 130 Ok(compiled_code) 131 } 132 133 /// Internally compiles the function into a stencil. 134 /// 135 /// Public only for testing and fuzzing purposes. 136 pub fn compile_stencil(&mut self, isa: &dyn TargetIsa) -> CodegenResult<CompiledCodeStencil> { 137 let _tt = timing::compile(); 138 139 self.verify_if(isa)?; 140 141 self.optimize(isa)?; 142 143 isa.compile_function(&self.func, &self.domtree, self.want_disasm) 144 } 145 146 /// Optimize the function, performing all compilation steps up to 147 /// but not including machine-code lowering and register 148 /// allocation. 149 /// 150 /// Public only for testing purposes. 151 pub fn optimize(&mut self, isa: &dyn TargetIsa) -> CodegenResult<()> { 152 log::debug!( 153 "Number of CLIF instructions to optimize: {}", 154 self.func.dfg.num_insts() 155 ); 156 log::debug!( 157 "Number of CLIF blocks to optimize: {}", 158 self.func.dfg.num_blocks() 159 ); 160 161 let opt_level = isa.flags().opt_level(); 162 crate::trace!( 163 "Optimizing (opt level {:?}):\n{}", 164 opt_level, 165 self.func.display() 166 ); 167 168 self.compute_cfg(); 169 if !isa.flags().use_egraphs() && opt_level != OptLevel::None { 170 self.preopt(isa)?; 171 } 172 if isa.flags().enable_nan_canonicalization() { 173 self.canonicalize_nans(isa)?; 174 } 175 176 self.legalize(isa)?; 177 178 if !isa.flags().use_egraphs() && opt_level != OptLevel::None { 179 self.compute_domtree(); 180 self.compute_loop_analysis(); 181 self.licm(isa)?; 182 self.simple_gvn(isa)?; 183 } 184 185 self.compute_domtree(); 186 self.eliminate_unreachable_code(isa)?; 187 188 if opt_level != OptLevel::None { 189 self.dce(isa)?; 190 } 191 192 self.remove_constant_phis(isa)?; 193 194 if opt_level != OptLevel::None { 195 if isa.flags().use_egraphs() { 196 self.egraph_pass()?; 197 } else if isa.flags().enable_alias_analysis() { 198 for _ in 0..2 { 199 self.replace_redundant_loads()?; 200 self.simple_gvn(isa)?; 201 } 202 } 203 } 204 205 Ok(()) 206 } 207 208 /// Compile the function. 209 /// 210 /// Run the function through all the passes necessary to generate code for the target ISA 211 /// represented by `isa`. This does not include the final step of emitting machine code into a 212 /// code sink. 213 /// 214 /// Returns information about the function's code and read-only data. 215 pub fn compile(&mut self, isa: &dyn TargetIsa) -> CompileResult<&CompiledCode> { 216 let _tt = timing::compile(); 217 let stencil = self.compile_stencil(isa).map_err(|error| CompileError { 218 inner: error, 219 func: &self.func, 220 })?; 221 Ok(self 222 .compiled_code 223 .insert(stencil.apply_params(&self.func.params))) 224 } 225 226 /// If available, return information about the code layout in the 227 /// final machine code: the offsets (in bytes) of each basic-block 228 /// start, and all basic-block edges. 229 #[deprecated = "use CompiledCode::get_code_bb_layout"] 230 pub fn get_code_bb_layout(&self) -> Option<(Vec<usize>, Vec<(usize, usize)>)> { 231 self.compiled_code().map(CompiledCode::get_code_bb_layout) 232 } 233 234 /// Creates unwind information for the function. 235 /// 236 /// Returns `None` if the function has no unwind information. 237 #[cfg(feature = "unwind")] 238 #[deprecated = "use CompiledCode::create_unwind_info"] 239 pub fn create_unwind_info( 240 &self, 241 isa: &dyn TargetIsa, 242 ) -> CodegenResult<Option<crate::isa::unwind::UnwindInfo>> { 243 self.compiled_code().unwrap().create_unwind_info(isa) 244 } 245 246 /// Run the verifier on the function. 247 /// 248 /// Also check that the dominator tree and control flow graph are consistent with the function. 249 pub fn verify<'a, FOI: Into<FlagsOrIsa<'a>>>(&self, fisa: FOI) -> VerifierResult<()> { 250 let mut errors = VerifierErrors::default(); 251 let _ = verify_context(&self.func, &self.cfg, &self.domtree, fisa, &mut errors); 252 253 if errors.is_empty() { 254 Ok(()) 255 } else { 256 Err(errors) 257 } 258 } 259 260 /// Run the verifier only if the `enable_verifier` setting is true. 261 pub fn verify_if<'a, FOI: Into<FlagsOrIsa<'a>>>(&self, fisa: FOI) -> CodegenResult<()> { 262 let fisa = fisa.into(); 263 if fisa.flags.enable_verifier() { 264 self.verify(fisa)?; 265 } 266 Ok(()) 267 } 268 269 /// Perform dead-code elimination on the function. 270 pub fn dce<'a, FOI: Into<FlagsOrIsa<'a>>>(&mut self, fisa: FOI) -> CodegenResult<()> { 271 do_dce(&mut self.func, &mut self.domtree); 272 self.verify_if(fisa)?; 273 Ok(()) 274 } 275 276 /// Perform constant-phi removal on the function. 277 pub fn remove_constant_phis<'a, FOI: Into<FlagsOrIsa<'a>>>( 278 &mut self, 279 fisa: FOI, 280 ) -> CodegenResult<()> { 281 do_remove_constant_phis(&mut self.func, &mut self.domtree); 282 self.verify_if(fisa)?; 283 Ok(()) 284 } 285 286 /// Perform pre-legalization rewrites on the function. 287 pub fn preopt(&mut self, isa: &dyn TargetIsa) -> CodegenResult<()> { 288 do_preopt(&mut self.func, isa); 289 self.verify_if(isa)?; 290 Ok(()) 291 } 292 293 /// Perform NaN canonicalizing rewrites on the function. 294 pub fn canonicalize_nans(&mut self, isa: &dyn TargetIsa) -> CodegenResult<()> { 295 do_nan_canonicalization(&mut self.func); 296 self.verify_if(isa) 297 } 298 299 /// Run the legalizer for `isa` on the function. 300 pub fn legalize(&mut self, isa: &dyn TargetIsa) -> CodegenResult<()> { 301 // Legalization invalidates the domtree and loop_analysis by mutating the CFG. 302 // TODO: Avoid doing this when legalization doesn't actually mutate the CFG. 303 self.domtree.clear(); 304 self.loop_analysis.clear(); 305 306 // Run some specific legalizations only. 307 simple_legalize(&mut self.func, &mut self.cfg, isa); 308 self.verify_if(isa) 309 } 310 311 /// Compute the control flow graph. 312 pub fn compute_cfg(&mut self) { 313 self.cfg.compute(&self.func) 314 } 315 316 /// Compute dominator tree. 317 pub fn compute_domtree(&mut self) { 318 self.domtree.compute(&self.func, &self.cfg) 319 } 320 321 /// Compute the loop analysis. 322 pub fn compute_loop_analysis(&mut self) { 323 self.loop_analysis 324 .compute(&self.func, &self.cfg, &self.domtree) 325 } 326 327 /// Compute the control flow graph and dominator tree. 328 pub fn flowgraph(&mut self) { 329 self.compute_cfg(); 330 self.compute_domtree() 331 } 332 333 /// Perform simple GVN on the function. 334 pub fn simple_gvn<'a, FOI: Into<FlagsOrIsa<'a>>>(&mut self, fisa: FOI) -> CodegenResult<()> { 335 do_simple_gvn(&mut self.func, &mut self.domtree); 336 self.verify_if(fisa) 337 } 338 339 /// Perform LICM on the function. 340 pub fn licm(&mut self, isa: &dyn TargetIsa) -> CodegenResult<()> { 341 do_licm( 342 &mut self.func, 343 &mut self.cfg, 344 &mut self.domtree, 345 &mut self.loop_analysis, 346 ); 347 self.verify_if(isa) 348 } 349 350 /// Perform unreachable code elimination. 351 pub fn eliminate_unreachable_code<'a, FOI>(&mut self, fisa: FOI) -> CodegenResult<()> 352 where 353 FOI: Into<FlagsOrIsa<'a>>, 354 { 355 eliminate_unreachable_code(&mut self.func, &mut self.cfg, &self.domtree); 356 self.verify_if(fisa) 357 } 358 359 /// Replace all redundant loads with the known values in 360 /// memory. These are loads whose values were already loaded by 361 /// other loads earlier, as well as loads whose values were stored 362 /// by a store instruction to the same instruction (so-called 363 /// "store-to-load forwarding"). 364 pub fn replace_redundant_loads(&mut self) -> CodegenResult<()> { 365 let mut analysis = AliasAnalysis::new(&self.func, &self.domtree); 366 analysis.compute_and_update_aliases(&mut self.func); 367 Ok(()) 368 } 369 370 /// Harvest candidate left-hand sides for superoptimization with Souper. 371 #[cfg(feature = "souper-harvest")] 372 pub fn souper_harvest( 373 &mut self, 374 out: &mut std::sync::mpsc::Sender<String>, 375 ) -> CodegenResult<()> { 376 do_souper_harvest(&self.func, out); 377 Ok(()) 378 } 379 380 /// Run optimizations via the egraph infrastructure. 381 pub fn egraph_pass(&mut self) -> CodegenResult<()> { 382 trace!( 383 "About to optimize with egraph phase:\n{}", 384 self.func.display() 385 ); 386 self.compute_loop_analysis(); 387 let mut alias_analysis = AliasAnalysis::new(&self.func, &self.domtree); 388 let mut pass = EgraphPass::new( 389 &mut self.func, 390 &self.domtree, 391 &self.loop_analysis, 392 &mut alias_analysis, 393 ); 394 pass.run(); 395 log::info!("egraph stats: {:?}", pass.stats); 396 trace!("After egraph optimization:\n{}", self.func.display()); 397 Ok(()) 398 } 399 } 400