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