Path: blob/main/cranelift/codegen/src/context.rs
1693 views
//! Cranelift compilation context and main entry point.1//!2//! When compiling many small functions, it is important to avoid repeatedly allocating and3//! deallocating the data structures needed for compilation. The `Context` struct is used to hold4//! on to memory allocations between function compilations.5//!6//! The context does not hold a `TargetIsa` instance which has to be provided as an argument7//! instead. This is because an ISA instance is immutable and can be used by multiple compilation8//! contexts concurrently. Typically, you would have one context per compilation thread and only a9//! single ISA instance.1011use crate::alias_analysis::AliasAnalysis;12use crate::dominator_tree::DominatorTree;13use crate::egraph::EgraphPass;14use crate::flowgraph::ControlFlowGraph;15use crate::inline::{Inline, do_inlining};16use crate::ir::Function;17use crate::isa::TargetIsa;18use crate::legalizer::simple_legalize;19use crate::loop_analysis::LoopAnalysis;20use crate::machinst::{CompiledCode, CompiledCodeStencil};21use crate::nan_canonicalization::do_nan_canonicalization;22use crate::remove_constant_phis::do_remove_constant_phis;23use crate::result::{CodegenResult, CompileResult};24use crate::settings::{FlagsOrIsa, OptLevel};25use crate::trace;26use crate::unreachable_code::eliminate_unreachable_code;27use crate::verifier::{VerifierErrors, VerifierResult, verify_context};28use crate::{CompileError, timing};29#[cfg(feature = "souper-harvest")]30use alloc::string::String;31use alloc::vec::Vec;32use cranelift_control::ControlPlane;33use target_lexicon::Architecture;3435#[cfg(feature = "souper-harvest")]36use crate::souper_harvest::do_souper_harvest;3738/// Persistent data structures and compilation pipeline.39pub struct Context {40/// The function we're compiling.41pub func: Function,4243/// The control flow graph of `func`.44pub cfg: ControlFlowGraph,4546/// Dominator tree for `func`.47pub domtree: DominatorTree,4849/// Loop analysis of `func`.50pub loop_analysis: LoopAnalysis,5152/// Result of MachBackend compilation, if computed.53pub(crate) compiled_code: Option<CompiledCode>,5455/// Flag: do we want a disassembly with the CompiledCode?56pub want_disasm: bool,57}5859impl Context {60/// Allocate a new compilation context.61///62/// The returned instance should be reused for compiling multiple functions in order to avoid63/// needless allocator thrashing.64pub fn new() -> Self {65Self::for_function(Function::new())66}6768/// Allocate a new compilation context with an existing Function.69///70/// The returned instance should be reused for compiling multiple functions in order to avoid71/// needless allocator thrashing.72pub fn for_function(func: Function) -> Self {73Self {74func,75cfg: ControlFlowGraph::new(),76domtree: DominatorTree::new(),77loop_analysis: LoopAnalysis::new(),78compiled_code: None,79want_disasm: false,80}81}8283/// Clear all data structures in this context.84pub fn clear(&mut self) {85self.func.clear();86self.cfg.clear();87self.domtree.clear();88self.loop_analysis.clear();89self.compiled_code = None;90self.want_disasm = false;91}9293/// Returns the compilation result for this function, available after any `compile` function94/// has been called.95pub fn compiled_code(&self) -> Option<&CompiledCode> {96self.compiled_code.as_ref()97}9899/// Returns the compilation result for this function, available after any `compile` function100/// has been called.101pub fn take_compiled_code(&mut self) -> Option<CompiledCode> {102self.compiled_code.take()103}104105/// Set the flag to request a disassembly when compiling with a106/// `MachBackend` backend.107pub fn set_disasm(&mut self, val: bool) {108self.want_disasm = val;109}110111/// Compile the function, and emit machine code into a `Vec<u8>`.112#[deprecated = "use Context::compile"]113pub fn compile_and_emit(114&mut self,115isa: &dyn TargetIsa,116mem: &mut Vec<u8>,117ctrl_plane: &mut ControlPlane,118) -> CompileResult<'_, &CompiledCode> {119let compiled_code = self.compile(isa, ctrl_plane)?;120mem.extend_from_slice(compiled_code.code_buffer());121Ok(compiled_code)122}123124/// Internally compiles the function into a stencil.125///126/// Public only for testing and fuzzing purposes.127pub fn compile_stencil(128&mut self,129isa: &dyn TargetIsa,130ctrl_plane: &mut ControlPlane,131) -> CodegenResult<CompiledCodeStencil> {132let result;133trace!("****** START compiling {}", self.func.display_spec());134{135let _tt = timing::compile();136137self.verify_if(isa)?;138self.optimize(isa, ctrl_plane)?;139result = isa.compile_function(&self.func, &self.domtree, self.want_disasm, ctrl_plane);140}141trace!("****** DONE compiling {}\n", self.func.display_spec());142result143}144145/// Optimize the function, performing all compilation steps up to146/// but not including machine-code lowering and register147/// allocation.148///149/// Public only for testing purposes.150pub fn optimize(151&mut self,152isa: &dyn TargetIsa,153ctrl_plane: &mut ControlPlane,154) -> CodegenResult<()> {155log::debug!(156"Number of CLIF instructions to optimize: {}",157self.func.dfg.num_insts()158);159log::debug!(160"Number of CLIF blocks to optimize: {}",161self.func.dfg.num_blocks()162);163164let opt_level = isa.flags().opt_level();165crate::trace!(166"Optimizing (opt level {:?}):\n{}",167opt_level,168self.func.display()169);170171if isa.flags().enable_nan_canonicalization() {172self.canonicalize_nans(isa)?;173}174175self.legalize(isa)?;176177self.compute_cfg();178self.compute_domtree();179self.eliminate_unreachable_code(isa)?;180self.remove_constant_phis(isa)?;181182self.func.dfg.resolve_all_aliases();183184if opt_level != OptLevel::None {185self.egraph_pass(isa, ctrl_plane)?;186}187188Ok(())189}190191/// Perform function call inlining.192///193/// Returns `true` if any function call was inlined, `false` otherwise.194pub fn inline(&mut self, inliner: impl Inline) -> CodegenResult<bool> {195do_inlining(&mut self.func, inliner)196}197198/// Compile the function,199///200/// Run the function through all the passes necessary to generate201/// code for the target ISA represented by `isa`. The generated202/// machine code is not relocated. Instead, any relocations can be203/// obtained from `compiled_code.buffer.relocs()`.204///205/// Performs any optimizations that are enabled, unless206/// `optimize()` was already invoked.207///208/// Returns the generated machine code as well as information about209/// the function's code and read-only data.210pub fn compile(211&mut self,212isa: &dyn TargetIsa,213ctrl_plane: &mut ControlPlane,214) -> CompileResult<'_, &CompiledCode> {215let stencil = self216.compile_stencil(isa, ctrl_plane)217.map_err(|error| CompileError {218inner: error,219func: &self.func,220})?;221Ok(self222.compiled_code223.insert(stencil.apply_params(&self.func.params)))224}225226/// If available, return information about the code layout in the227/// final machine code: the offsets (in bytes) of each basic-block228/// start, and all basic-block edges.229#[deprecated = "use CompiledCode::get_code_bb_layout"]230pub fn get_code_bb_layout(&self) -> Option<(Vec<usize>, Vec<(usize, usize)>)> {231self.compiled_code().map(CompiledCode::get_code_bb_layout)232}233234/// Creates unwind information for the function.235///236/// Returns `None` if the function has no unwind information.237#[cfg(feature = "unwind")]238#[deprecated = "use CompiledCode::create_unwind_info"]239pub fn create_unwind_info(240&self,241isa: &dyn TargetIsa,242) -> CodegenResult<Option<crate::isa::unwind::UnwindInfo>> {243self.compiled_code().unwrap().create_unwind_info(isa)244}245246/// Run the verifier on the function.247///248/// Also check that the dominator tree and control flow graph are consistent with the function.249///250/// TODO: rename to "CLIF validate" or similar.251pub fn verify<'a, FOI: Into<FlagsOrIsa<'a>>>(&self, fisa: FOI) -> VerifierResult<()> {252let mut errors = VerifierErrors::default();253let _ = verify_context(&self.func, &self.cfg, &self.domtree, fisa, &mut errors);254255if errors.is_empty() {256Ok(())257} else {258Err(errors)259}260}261262/// Run the verifier only if the `enable_verifier` setting is true.263pub fn verify_if<'a, FOI: Into<FlagsOrIsa<'a>>>(&self, fisa: FOI) -> CodegenResult<()> {264let fisa = fisa.into();265if fisa.flags.enable_verifier() {266self.verify(fisa)?;267}268Ok(())269}270271/// Perform constant-phi removal on the function.272pub fn remove_constant_phis<'a, FOI: Into<FlagsOrIsa<'a>>>(273&mut self,274fisa: FOI,275) -> CodegenResult<()> {276do_remove_constant_phis(&mut self.func, &mut self.domtree);277self.verify_if(fisa)?;278Ok(())279}280281/// Perform NaN canonicalizing rewrites on the function.282pub fn canonicalize_nans(&mut self, isa: &dyn TargetIsa) -> CodegenResult<()> {283// Currently only RiscV64 is the only arch that may not have vector support.284let has_vector_support = match isa.triple().architecture {285Architecture::Riscv64(_) => match isa.isa_flags().iter().find(|f| f.name == "has_v") {286Some(value) => value.as_bool().unwrap_or(false),287None => false,288},289_ => true,290};291do_nan_canonicalization(&mut self.func, has_vector_support);292self.verify_if(isa)293}294295/// Run the legalizer for `isa` on the function.296pub fn legalize(&mut self, isa: &dyn TargetIsa) -> CodegenResult<()> {297// Legalization invalidates the domtree and loop_analysis by mutating the CFG.298// TODO: Avoid doing this when legalization doesn't actually mutate the CFG.299self.domtree.clear();300self.loop_analysis.clear();301self.cfg.clear();302303// Run some specific legalizations only.304simple_legalize(&mut self.func, isa);305self.verify_if(isa)306}307308/// Compute the control flow graph.309pub fn compute_cfg(&mut self) {310self.cfg.compute(&self.func)311}312313/// Compute dominator tree.314pub fn compute_domtree(&mut self) {315self.domtree.compute(&self.func, &self.cfg);316}317318/// Compute the loop analysis.319pub fn compute_loop_analysis(&mut self) {320self.loop_analysis321.compute(&self.func, &self.cfg, &self.domtree)322}323324/// Compute the control flow graph and dominator tree.325pub fn flowgraph(&mut self) {326self.compute_cfg();327self.compute_domtree()328}329330/// Perform unreachable code elimination.331pub fn eliminate_unreachable_code<'a, FOI>(&mut self, fisa: FOI) -> CodegenResult<()>332where333FOI: Into<FlagsOrIsa<'a>>,334{335eliminate_unreachable_code(&mut self.func, &mut self.cfg, &self.domtree);336self.verify_if(fisa)337}338339/// Replace all redundant loads with the known values in340/// memory. These are loads whose values were already loaded by341/// other loads earlier, as well as loads whose values were stored342/// by a store instruction to the same instruction (so-called343/// "store-to-load forwarding").344pub fn replace_redundant_loads(&mut self) -> CodegenResult<()> {345let mut analysis = AliasAnalysis::new(&self.func, &self.domtree);346analysis.compute_and_update_aliases(&mut self.func);347Ok(())348}349350/// Harvest candidate left-hand sides for superoptimization with Souper.351#[cfg(feature = "souper-harvest")]352pub fn souper_harvest(353&mut self,354out: &mut std::sync::mpsc::Sender<String>,355) -> CodegenResult<()> {356do_souper_harvest(&self.func, out);357Ok(())358}359360/// Run optimizations via the egraph infrastructure.361pub fn egraph_pass<'a, FOI>(362&mut self,363fisa: FOI,364ctrl_plane: &mut ControlPlane,365) -> CodegenResult<()>366where367FOI: Into<FlagsOrIsa<'a>>,368{369let _tt = timing::egraph();370371trace!(372"About to optimize with egraph phase:\n{}",373self.func.display()374);375let fisa = fisa.into();376self.compute_loop_analysis();377let mut alias_analysis = AliasAnalysis::new(&self.func, &self.domtree);378let mut pass = EgraphPass::new(379&mut self.func,380&self.domtree,381&self.loop_analysis,382&mut alias_analysis,383&fisa.flags,384ctrl_plane,385);386pass.run();387log::debug!("egraph stats: {:?}", pass.stats);388trace!("After egraph optimization:\n{}", self.func.display());389390self.verify_if(fisa)391}392}393394395