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