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