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