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::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::{verify_context, VerifierErrors, VerifierResult}; 29 use crate::{timing, CompileError}; 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 /// 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 ctrl_plane: &mut ControlPlane, 127 ) -> CompileResult<&CompiledCode> { 128 let compiled_code = self.compile(isa, ctrl_plane)?; 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( 137 &mut self, 138 isa: &dyn TargetIsa, 139 ctrl_plane: &mut ControlPlane, 140 ) -> CodegenResult<CompiledCodeStencil> { 141 let _tt = timing::compile(); 142 143 self.verify_if(isa)?; 144 145 self.optimize(isa, ctrl_plane)?; 146 147 isa.compile_function(&self.func, &self.domtree, self.want_disasm, ctrl_plane) 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 self.compute_cfg(); 177 if isa.flags().enable_nan_canonicalization() { 178 self.canonicalize_nans(isa)?; 179 } 180 181 self.legalize(isa)?; 182 183 self.compute_domtree(); 184 self.eliminate_unreachable_code(isa)?; 185 186 if opt_level != OptLevel::None { 187 self.dce(isa)?; 188 } 189 190 self.remove_constant_phis(isa)?; 191 192 if opt_level != OptLevel::None { 193 self.egraph_pass(isa, ctrl_plane)?; 194 } 195 196 Ok(()) 197 } 198 199 /// Compile the function. 200 /// 201 /// Run the function through all the passes necessary to generate code for the target ISA 202 /// represented by `isa`. This does not include the final step of emitting machine code into a 203 /// code sink. 204 /// 205 /// Returns information about the function's code and read-only data. 206 pub fn compile( 207 &mut self, 208 isa: &dyn TargetIsa, 209 ctrl_plane: &mut ControlPlane, 210 ) -> CompileResult<&CompiledCode> { 211 let stencil = self 212 .compile_stencil(isa, ctrl_plane) 213 .map_err(|error| CompileError { 214 inner: error, 215 func: &self.func, 216 })?; 217 Ok(self 218 .compiled_code 219 .insert(stencil.apply_params(&self.func.params))) 220 } 221 222 /// If available, return information about the code layout in the 223 /// final machine code: the offsets (in bytes) of each basic-block 224 /// start, and all basic-block edges. 225 #[deprecated = "use CompiledCode::get_code_bb_layout"] 226 pub fn get_code_bb_layout(&self) -> Option<(Vec<usize>, Vec<(usize, usize)>)> { 227 self.compiled_code().map(CompiledCode::get_code_bb_layout) 228 } 229 230 /// Creates unwind information for the function. 231 /// 232 /// Returns `None` if the function has no unwind information. 233 #[cfg(feature = "unwind")] 234 #[deprecated = "use CompiledCode::create_unwind_info"] 235 pub fn create_unwind_info( 236 &self, 237 isa: &dyn TargetIsa, 238 ) -> CodegenResult<Option<crate::isa::unwind::UnwindInfo>> { 239 self.compiled_code().unwrap().create_unwind_info(isa) 240 } 241 242 /// Run the verifier on the function. 243 /// 244 /// Also check that the dominator tree and control flow graph are consistent with the function. 245 /// 246 /// TODO: rename to "CLIF validate" or similar. 247 pub fn verify<'a, FOI: Into<FlagsOrIsa<'a>>>(&self, fisa: FOI) -> VerifierResult<()> { 248 let mut errors = VerifierErrors::default(); 249 let _ = verify_context(&self.func, &self.cfg, &self.domtree, fisa, &mut errors); 250 251 if errors.is_empty() { 252 Ok(()) 253 } else { 254 Err(errors) 255 } 256 } 257 258 /// Run the verifier only if the `enable_verifier` setting is true. 259 pub fn verify_if<'a, FOI: Into<FlagsOrIsa<'a>>>(&self, fisa: FOI) -> CodegenResult<()> { 260 let fisa = fisa.into(); 261 if fisa.flags.enable_verifier() { 262 self.verify(fisa)?; 263 } 264 Ok(()) 265 } 266 267 /// Perform dead-code elimination on the function. 268 pub fn dce<'a, FOI: Into<FlagsOrIsa<'a>>>(&mut self, fisa: FOI) -> CodegenResult<()> { 269 do_dce(&mut self.func, &mut self.domtree); 270 self.verify_if(fisa)?; 271 Ok(()) 272 } 273 274 /// Perform constant-phi removal on the function. 275 pub fn remove_constant_phis<'a, FOI: Into<FlagsOrIsa<'a>>>( 276 &mut self, 277 fisa: FOI, 278 ) -> CodegenResult<()> { 279 do_remove_constant_phis(&mut self.func, &mut self.domtree); 280 self.verify_if(fisa)?; 281 Ok(()) 282 } 283 284 /// Perform NaN canonicalizing rewrites on the function. 285 pub fn canonicalize_nans(&mut self, isa: &dyn TargetIsa) -> CodegenResult<()> { 286 // Currently only RiscV64 is the only arch that may not have vector support. 287 let has_vector_support = match isa.triple().architecture { 288 Architecture::Riscv64(_) => match isa.isa_flags().iter().find(|f| f.name == "has_v") { 289 Some(value) => value.as_bool().unwrap_or(false), 290 None => false, 291 }, 292 _ => true, 293 }; 294 do_nan_canonicalization(&mut self.func, has_vector_support); 295 self.verify_if(isa) 296 } 297 298 /// Run the legalizer for `isa` on the function. 299 pub fn legalize(&mut self, isa: &dyn TargetIsa) -> CodegenResult<()> { 300 // Legalization invalidates the domtree and loop_analysis by mutating the CFG. 301 // TODO: Avoid doing this when legalization doesn't actually mutate the CFG. 302 self.domtree.clear(); 303 self.loop_analysis.clear(); 304 305 // Run some specific legalizations only. 306 simple_legalize(&mut self.func, &mut self.cfg, isa); 307 self.verify_if(isa) 308 } 309 310 /// Compute the control flow graph. 311 pub fn compute_cfg(&mut self) { 312 self.cfg.compute(&self.func) 313 } 314 315 /// Compute dominator tree. 316 pub fn compute_domtree(&mut self) { 317 self.domtree.compute(&self.func, &self.cfg) 318 } 319 320 /// Compute the loop analysis. 321 pub fn compute_loop_analysis(&mut self) { 322 self.loop_analysis 323 .compute(&self.func, &self.cfg, &self.domtree) 324 } 325 326 /// Compute the control flow graph and dominator tree. 327 pub fn flowgraph(&mut self) { 328 self.compute_cfg(); 329 self.compute_domtree() 330 } 331 332 /// Perform unreachable code elimination. 333 pub fn eliminate_unreachable_code<'a, FOI>(&mut self, fisa: FOI) -> CodegenResult<()> 334 where 335 FOI: Into<FlagsOrIsa<'a>>, 336 { 337 eliminate_unreachable_code(&mut self.func, &mut self.cfg, &self.domtree); 338 self.verify_if(fisa) 339 } 340 341 /// Replace all redundant loads with the known values in 342 /// memory. These are loads whose values were already loaded by 343 /// other loads earlier, as well as loads whose values were stored 344 /// by a store instruction to the same instruction (so-called 345 /// "store-to-load forwarding"). 346 pub fn replace_redundant_loads(&mut self) -> CodegenResult<()> { 347 let mut analysis = AliasAnalysis::new(&self.func, &self.domtree); 348 analysis.compute_and_update_aliases(&mut self.func); 349 Ok(()) 350 } 351 352 /// Harvest candidate left-hand sides for superoptimization with Souper. 353 #[cfg(feature = "souper-harvest")] 354 pub fn souper_harvest( 355 &mut self, 356 out: &mut std::sync::mpsc::Sender<String>, 357 ) -> CodegenResult<()> { 358 do_souper_harvest(&self.func, out); 359 Ok(()) 360 } 361 362 /// Run optimizations via the egraph infrastructure. 363 pub fn egraph_pass<'a, FOI>( 364 &mut self, 365 fisa: FOI, 366 ctrl_plane: &mut ControlPlane, 367 ) -> CodegenResult<()> 368 where 369 FOI: Into<FlagsOrIsa<'a>>, 370 { 371 let _tt = timing::egraph(); 372 373 trace!( 374 "About to optimize with egraph phase:\n{}", 375 self.func.display() 376 ); 377 let fisa = fisa.into(); 378 self.compute_loop_analysis(); 379 let mut alias_analysis = AliasAnalysis::new(&self.func, &self.domtree); 380 let mut pass = EgraphPass::new( 381 &mut self.func, 382 &self.domtree, 383 &self.loop_analysis, 384 &mut alias_analysis, 385 &fisa.flags, 386 ctrl_plane, 387 ); 388 pass.run(); 389 log::debug!("egraph stats: {:?}", pass.stats); 390 trace!("pinned_union_count: {}", pass.eclasses.pinned_union_count); 391 trace!("After egraph optimization:\n{}", self.func.display()); 392 393 self.verify_if(fisa) 394 } 395 } 396