use binemit::{
relax_branches, shrink_instructions, CodeOffset, MemoryCodeSink, RelocSink, TrapSink,
};
use dce::do_dce;
use dominator_tree::DominatorTree;
use flowgraph::ControlFlowGraph;
use ir::Function;
use isa::TargetIsa;
use legalize_function;
use licm::do_licm;
use loop_analysis::LoopAnalysis;
use nan_canonicalization::do_nan_canonicalization;
use postopt::do_postopt;
use preopt::do_preopt;
use regalloc;
use result::CodegenResult;
use settings::{FlagsOrIsa, OptLevel};
use simple_gvn::do_simple_gvn;
use std::vec::Vec;
use timing;
use unreachable_code::eliminate_unreachable_code;
use verifier::{verify_context, verify_locations, VerifierResult};
pub struct Context {
pub func: Function,
pub cfg: ControlFlowGraph,
pub domtree: DominatorTree,
pub regalloc: regalloc::Context,
pub loop_analysis: LoopAnalysis,
}
impl Context {
pub fn new() -> Self {
Self::for_function(Function::new())
}
pub fn for_function(func: Function) -> Self {
Self {
func,
cfg: ControlFlowGraph::new(),
domtree: DominatorTree::new(),
regalloc: regalloc::Context::new(),
loop_analysis: LoopAnalysis::new(),
}
}
pub fn clear(&mut self) {
self.func.clear();
self.cfg.clear();
self.domtree.clear();
self.regalloc.clear();
self.loop_analysis.clear();
}
pub fn compile_and_emit(
&mut self,
isa: &TargetIsa,
mem: &mut Vec<u8>,
relocs: &mut RelocSink,
traps: &mut TrapSink,
) -> CodegenResult<()> {
let code_size = self.compile(isa)?;
let old_len = mem.len();
mem.resize(old_len + code_size as usize, 0);
unsafe {
self.emit_to_memory(
isa,
mem.as_mut_ptr().offset(old_len as isize),
relocs,
traps,
)
};
Ok(())
}
pub fn compile(&mut self, isa: &TargetIsa) -> CodegenResult<CodeOffset> {
let _tt = timing::compile();
self.verify_if(isa)?;
self.compute_cfg();
if isa.flags().opt_level() != OptLevel::Fastest {
self.preopt(isa)?;
}
if isa.flags().enable_nan_canonicalization() {
self.canonicalize_nans(isa)?;
}
self.legalize(isa)?;
if isa.flags().opt_level() != OptLevel::Fastest {
self.postopt(isa)?;
}
if isa.flags().opt_level() == OptLevel::Best {
self.compute_domtree();
self.compute_loop_analysis();
self.licm(isa)?;
self.simple_gvn(isa)?;
}
self.compute_domtree();
self.eliminate_unreachable_code(isa)?;
if isa.flags().opt_level() != OptLevel::Fastest {
self.dce(isa)?;
}
self.regalloc(isa)?;
self.prologue_epilogue(isa)?;
if isa.flags().opt_level() == OptLevel::Best {
self.shrink_instructions(isa)?;
}
self.relax_branches(isa)
}
pub unsafe fn emit_to_memory(
&self,
isa: &TargetIsa,
mem: *mut u8,
relocs: &mut RelocSink,
traps: &mut TrapSink,
) {
let _tt = timing::binemit();
isa.emit_function_to_memory(&self.func, &mut MemoryCodeSink::new(mem, relocs, traps));
}
pub fn verify<'a, FOI: Into<FlagsOrIsa<'a>>>(&self, fisa: FOI) -> VerifierResult<()> {
verify_context(&self.func, &self.cfg, &self.domtree, fisa)
}
pub fn verify_if<'a, FOI: Into<FlagsOrIsa<'a>>>(&self, fisa: FOI) -> CodegenResult<()> {
let fisa = fisa.into();
if fisa.flags.enable_verifier() {
self.verify(fisa).map_err(Into::into)
} else {
Ok(())
}
}
pub fn verify_locations(&self, isa: &TargetIsa) -> VerifierResult<()> {
verify_locations(isa, &self.func, None)
}
pub fn verify_locations_if(&self, isa: &TargetIsa) -> CodegenResult<()> {
if isa.flags().enable_verifier() {
self.verify_locations(isa).map_err(Into::into)
} else {
Ok(())
}
}
pub fn dce<'a, FOI: Into<FlagsOrIsa<'a>>>(&mut self, fisa: FOI) -> CodegenResult<()> {
do_dce(&mut self.func, &mut self.domtree);
self.verify_if(fisa)?;
Ok(())
}
pub fn preopt(&mut self, isa: &TargetIsa) -> CodegenResult<()> {
do_preopt(&mut self.func);
self.verify_if(isa)?;
Ok(())
}
pub fn canonicalize_nans(&mut self, isa: &TargetIsa) -> CodegenResult<()> {
do_nan_canonicalization(&mut self.func);
self.verify_if(isa)
}
pub fn legalize(&mut self, isa: &TargetIsa) -> CodegenResult<()> {
self.domtree.clear();
self.loop_analysis.clear();
legalize_function(&mut self.func, &mut self.cfg, isa);
self.verify_if(isa)
}
pub fn postopt(&mut self, isa: &TargetIsa) -> CodegenResult<()> {
do_postopt(&mut self.func, isa);
self.verify_if(isa)?;
Ok(())
}
pub fn compute_cfg(&mut self) {
self.cfg.compute(&self.func)
}
pub fn compute_domtree(&mut self) {
self.domtree.compute(&self.func, &self.cfg)
}
pub fn compute_loop_analysis(&mut self) {
self.loop_analysis
.compute(&self.func, &self.cfg, &self.domtree)
}
pub fn flowgraph(&mut self) {
self.compute_cfg();
self.compute_domtree()
}
pub fn simple_gvn<'a, FOI: Into<FlagsOrIsa<'a>>>(&mut self, fisa: FOI) -> CodegenResult<()> {
do_simple_gvn(&mut self.func, &mut self.domtree);
self.verify_if(fisa)
}
pub fn licm<'a, FOI: Into<FlagsOrIsa<'a>>>(&mut self, fisa: FOI) -> CodegenResult<()> {
do_licm(
&mut self.func,
&mut self.cfg,
&mut self.domtree,
&mut self.loop_analysis,
);
self.verify_if(fisa)
}
pub fn eliminate_unreachable_code<'a, FOI>(&mut self, fisa: FOI) -> CodegenResult<()>
where
FOI: Into<FlagsOrIsa<'a>>,
{
eliminate_unreachable_code(&mut self.func, &mut self.cfg, &self.domtree);
self.verify_if(fisa)
}
pub fn regalloc(&mut self, isa: &TargetIsa) -> CodegenResult<()> {
self.regalloc
.run(isa, &mut self.func, &self.cfg, &mut self.domtree)
}
pub fn prologue_epilogue(&mut self, isa: &TargetIsa) -> CodegenResult<()> {
assert!(
self.func.stack_limit.is_none(),
"stack_limit isn't implemented yet"
);
isa.prologue_epilogue(&mut self.func)?;
self.verify_if(isa)?;
self.verify_locations_if(isa)?;
Ok(())
}
pub fn shrink_instructions(&mut self, isa: &TargetIsa) -> CodegenResult<()> {
shrink_instructions(&mut self.func, isa);
self.verify_if(isa)?;
self.verify_locations_if(isa)?;
Ok(())
}
pub fn relax_branches(&mut self, isa: &TargetIsa) -> CodegenResult<CodeOffset> {
let code_size = relax_branches(&mut self.func, isa)?;
self.verify_if(isa)?;
self.verify_locations_if(isa)?;
Ok(code_size)
}
}