#![allow(clippy::single_match)]
#![feature(try_trait)]
#![warn(
clippy::print_stdout,
clippy::unimplemented,
clippy::doc_markdown,
clippy::items_after_statements,
clippy::match_same_arms,
clippy::single_match_else,
clippy::use_self
)]
use crate::{firm::FirmProgram, timing::Measurement};
use libfirm_rs::{bindings, Graph};
use std::ffi::CString;
#[macro_use]
extern crate debugging;
#[macro_use]
extern crate derive_more;
pub(crate) use compiler_shared::timing;
pub(crate) use debugging::dot;
pub(crate) use firm_construction as firm;
pub(crate) use utils::ref_eq;
pub(crate) mod optimization {
pub(crate) use super::*;
}
pub(crate) mod analysis;
mod inlining;
use self::inlining::Inlining;
mod constant_folding_load_store;
use self::constant_folding_load_store::ConstantFoldingWithLoadStore;
mod constant_folding;
use self::constant_folding::ConstantFolding;
mod control_flow;
use self::control_flow::ControlFlow;
mod code_placement;
use self::code_placement::{CodePlacement, CostMinimizingPlacement, EarliestPlacement};
mod remove_critical_edges;
pub use self::remove_critical_edges::RemoveCriticalEdges;
mod common_subexpr_elim;
pub use self::common_subexpr_elim::CommonSubExpr;
pub mod compile_time_assertions;
pub use self::compile_time_assertions::{CompileTimeAssertions, Phase};
mod node_local;
pub use self::node_local::NodeLocal;
mod lattices;
pub trait Interprocedural {
fn optimize(program: &mut FirmProgram<'_, '_>) -> Outcome;
}
pub trait Local {
fn optimize_function(graph: Graph) -> Outcome;
}
impl<T> Interprocedural for T
where
T: Local,
{
fn optimize(program: &mut FirmProgram<'_, '_>) -> Outcome {
let mut collector = OutcomeCollector::new();
for method in program.methods.values() {
if let Some(graph) = method.borrow().graph {
collector.push(Self::optimize_function(graph));
}
}
collector.result()
}
}
#[derive(
strum_macros::EnumString,
serde_derive::Deserialize,
serde_derive::Serialize,
Debug,
Copy,
Clone,
Display,
)]
pub enum Kind {
ConstantFolding,
ConstantFoldingWithLoadStore,
Inline,
ControlFlow,
RemoveCriticalEdges,
EarliestPlacement,
CostMinimizingPlacement,
CodePlacement,
CommonSubExprElim,
NodeLocal,
}
impl Kind {
fn run(self, program: &mut FirmProgram<'_, '_>) -> Outcome {
match self {
Kind::ConstantFolding => ConstantFolding::optimize(program),
Kind::ConstantFoldingWithLoadStore => ConstantFoldingWithLoadStore::optimize(program),
Kind::Inline => Inlining::optimize(program),
Kind::ControlFlow => ControlFlow::optimize(program),
Kind::RemoveCriticalEdges => RemoveCriticalEdges::optimize(program),
Kind::EarliestPlacement => EarliestPlacement::optimize(program),
Kind::CostMinimizingPlacement => CostMinimizingPlacement::optimize(program),
Kind::CodePlacement => CodePlacement::optimize(program),
Kind::CommonSubExprElim => CommonSubExpr::optimize(program),
Kind::NodeLocal => NodeLocal::optimize(program),
}
}
}
#[derive(Debug, Clone)]
pub enum Level {
None,
Moderate,
Aggressive,
Custom(Vec<Optimization>),
}
impl Default for Level {
fn default() -> Self {
Level::None
}
}
impl Level {
fn sequence(&self) -> Vec<Optimization> {
match self {
Level::None => vec![],
Level::Moderate => vec![
Optimization::new(Kind::Inline),
Optimization::new(Kind::ConstantFolding),
Optimization::new(Kind::ControlFlow),
],
Level::Aggressive => vec![
Optimization::new(Kind::Inline),
Optimization::new(Kind::ConstantFoldingWithLoadStore),
Optimization::new(Kind::ControlFlow),
Optimization::new(Kind::NodeLocal),
],
Level::Custom(list) => list.clone(),
}
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum Flag {
DumpVcg,
Gui,
}
#[derive(Clone, Debug)]
pub struct Optimization {
pub kind: Kind,
pub flags: Vec<Flag>,
}
impl Optimization {
fn new(kind: Kind) -> Self {
Self {
kind,
flags: vec![],
}
}
fn has_flag(&self, flag: Flag) -> bool {
self.flags.iter().any(|f| *f == flag)
}
fn run(&self, program: &mut FirmProgram<'_, '_>) -> Outcome {
let outcome = self.kind.run(program);
self.apply_flags(program);
outcome
}
fn apply_flags(&self, program: &FirmProgram<'_, '_>) {
if self.has_flag(Flag::DumpVcg) {
unsafe {
let suffix = CString::new(format!("-{}", self.kind)).unwrap();
bindings::dump_all_ir_graphs(suffix.as_ptr());
}
}
if self.has_flag(Flag::Gui) {
let label = format!("After running optimization '{}'", self.kind);
breakpoint!(label, program);
}
}
}
#[derive(Clone, Copy, PartialEq, Debug)]
pub enum Outcome {
Unchanged,
Changed,
}
#[derive(Default)]
pub struct OutcomeCollector {
results: Vec<Outcome>,
}
impl OutcomeCollector {
pub fn new() -> Self {
Self::default()
}
pub fn push(&mut self, res: Outcome) {
self.results.push(res);
}
pub fn result(&self) -> Outcome {
if self.results.iter().any(|x| *x == Outcome::Changed) {
Outcome::Changed
} else {
Outcome::Unchanged
}
}
}
impl Level {
pub fn run_all(&self, program: &mut FirmProgram<'_, '_>) {
breakpoint!("before optimization sequence".to_string(), program);
let measurement_all = Measurement::start("optimization phase");
let compile_time_assertions = CompileTimeAssertions::new();
for (i, optimization) in self.sequence().iter().enumerate() {
log::info!("Running optimization #{}: {:?}", i, optimization);
compile_time_assertions.check_program(
program,
Phase {
opt_idx: i,
opt_kind: optimization.kind,
is_already_applied: false,
},
);
let measurement = Measurement::start(&format!("opt #{}: {}", i, optimization.kind));
optimization.run(program);
measurement.stop();
compile_time_assertions.check_program(
program,
Phase {
opt_idx: i,
opt_kind: optimization.kind,
is_already_applied: true,
},
);
log::debug!("Finished optimization #{}: {:?}", i, optimization.kind);
}
measurement_all.stop();
breakpoint!("after optimization sequence".to_string(), program);
}
}