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