Skip to content

Commit

Permalink
PassManager: Refactor / separate into a module (FuelLabs#3788)
Browse files Browse the repository at this point in the history
This PR moves code in preparation for a full fledged pass manager for
the IR.

Reference: FuelLabs#3596
  • Loading branch information
vaivaswatha authored Jan 16, 2023
1 parent 24d74e3 commit bb607e1
Show file tree
Hide file tree
Showing 8 changed files with 151 additions and 159 deletions.
155 changes: 1 addition & 154 deletions sway-ir/src/bin/opt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use std::{
};

use anyhow::anyhow;
use sway_ir::{error::IrError, function::Function, optimize, Context};
use sway_ir::{ConstCombinePass, DCEPass, InlinePass, Mem2RegPass, PassManager, SimplifyCfgPass};

// -------------------------------------------------------------------------------------------------

Expand Down Expand Up @@ -67,159 +67,6 @@ fn write_to_output<S: Into<String>>(ir_str: S, path_str: &Option<String>) -> std
}
}

// -------------------------------------------------------------------------------------------------

trait NamedPass {
fn name() -> &'static str;
fn descr() -> &'static str;
fn run(ir: &mut Context) -> Result<bool, IrError>;

fn run_on_all_fns<F: FnMut(&mut Context, &Function) -> Result<bool, IrError>>(
ir: &mut Context,
mut run_on_fn: F,
) -> Result<bool, IrError> {
let funcs = ir
.module_iter()
.flat_map(|module| module.function_iter(ir))
.collect::<Vec<_>>();
let mut modified = false;
for func in funcs {
if run_on_fn(ir, &func)? {
modified = true;
}
}
Ok(modified)
}
}

type NamePassPair = (&'static str, fn(&mut Context) -> Result<bool, IrError>);

#[derive(Default)]
struct PassManager {
passes: HashMap<&'static str, NamePassPair>,
}

impl PassManager {
fn register<T: NamedPass>(&mut self) {
self.passes.insert(T::name(), (T::descr(), T::run));
}

fn run(&self, name: &str, ir: &mut Context) -> Result<bool, IrError> {
self.passes.get(name).expect("Unknown pass name!").1(ir)
}

fn contains(&self, name: &str) -> bool {
self.passes.contains_key(name)
}

fn help_text(&self) -> String {
let summary = self
.passes
.iter()
.map(|(name, (descr, _))| format!(" {name:16} - {descr}"))
.collect::<Vec<_>>()
.join("\n");

format!("Valid pass names are:\n\n{summary}",)
}
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

struct InlinePass;

impl NamedPass for InlinePass {
fn name() -> &'static str {
"inline"
}

fn descr() -> &'static str {
"inline function calls."
}

fn run(ir: &mut Context) -> Result<bool, IrError> {
// For now we inline everything into `main()`. Eventually we can be more selective.
let main_fn = ir
.module_iter()
.flat_map(|module| module.function_iter(ir))
.find(|f| f.get_name(ir) == "main")
.unwrap();
optimize::inline_all_function_calls(ir, &main_fn)
}
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

struct ConstCombinePass;

impl NamedPass for ConstCombinePass {
fn name() -> &'static str {
"constcombine"
}

fn descr() -> &'static str {
"constant folding."
}

fn run(ir: &mut Context) -> Result<bool, IrError> {
Self::run_on_all_fns(ir, optimize::combine_constants)
}
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

struct SimplifyCfgPass;

impl NamedPass for SimplifyCfgPass {
fn name() -> &'static str {
"simplifycfg"
}

fn descr() -> &'static str {
"merge or remove redundant blocks."
}

fn run(ir: &mut Context) -> Result<bool, IrError> {
Self::run_on_all_fns(ir, optimize::simplify_cfg)
}
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

struct DCEPass;

impl NamedPass for DCEPass {
fn name() -> &'static str {
"dce"
}

fn descr() -> &'static str {
"Dead code elimination."
}

fn run(ir: &mut Context) -> Result<bool, IrError> {
Self::run_on_all_fns(ir, optimize::dce)
}
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

struct Mem2RegPass;

impl NamedPass for Mem2RegPass {
fn name() -> &'static str {
"mem2reg"
}

fn descr() -> &'static str {
"Promote local memory to SSA registers."
}

fn run(ir: &mut Context) -> Result<bool, IrError> {
Self::run_on_all_fns(ir, optimize::promote_to_registers)
}
}

// -------------------------------------------------------------------------------------------------
// Using a bespoke CLI parser since the order in which passes are specified is important.

Expand Down
2 changes: 2 additions & 0 deletions sway-ir/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ pub mod parser;
pub use parser::*;
pub mod local_var;
pub use local_var::*;
pub mod pass_manager;
pub use pass_manager::*;
pub mod pretty;
pub use pretty::*;
pub mod printer;
Expand Down
18 changes: 17 additions & 1 deletion sway-ir/src/optimize/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,25 @@ use crate::{
function::Function,
instruction::Instruction,
value::{Value, ValueContent, ValueDatum},
BranchToWithArgs, Predicate,
BranchToWithArgs, NamedPass, Predicate,
};

pub struct ConstCombinePass;

impl NamedPass for ConstCombinePass {
fn name() -> &'static str {
"constcombine"
}

fn descr() -> &'static str {
"constant folding."
}

fn run(ir: &mut Context) -> Result<bool, IrError> {
Self::run_on_all_fns(ir, combine_constants)
}
}

/// Find constant expressions which can be reduced to fewer opterations.
pub fn combine_constants(context: &mut Context, function: &Function) -> Result<bool, IrError> {
let mut modified = false;
Expand Down
18 changes: 17 additions & 1 deletion sway-ir/src/optimize/dce.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,26 @@
//! 2. At the time of inspecting a definition, if it has no uses, it is removed.
//! This pass does not do CFG transformations. That is handled by simplify_cfg.
use crate::{Block, Context, Function, Instruction, IrError, Module, Value, ValueDatum};
use crate::{Block, Context, Function, Instruction, IrError, Module, NamedPass, Value, ValueDatum};

use std::collections::{HashMap, HashSet};

pub struct DCEPass;

impl NamedPass for DCEPass {
fn name() -> &'static str {
"dce"
}

fn descr() -> &'static str {
"Dead code elimination."
}

fn run(ir: &mut Context) -> Result<bool, IrError> {
Self::run_on_all_fns(ir, dce)
}
}

fn can_eliminate_instruction(context: &Context, val: Value) -> bool {
let inst = val.get_instruction(context).unwrap();
!inst.is_terminator() && !inst.may_have_side_effect()
Expand Down
24 changes: 23 additions & 1 deletion sway-ir/src/optimize/inline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,31 @@ use crate::{
local_var::LocalVar,
metadata::{combine, MetadataIndex},
value::{Value, ValueContent, ValueDatum},
BlockArgument,
BlockArgument, NamedPass,
};

pub struct InlinePass;

impl NamedPass for InlinePass {
fn name() -> &'static str {
"inline"
}

fn descr() -> &'static str {
"inline function calls."
}

fn run(ir: &mut Context) -> Result<bool, IrError> {
// For now we inline everything into `main()`. Eventually we can be more selective.
let main_fn = ir
.module_iter()
.flat_map(|module| module.function_iter(ir))
.find(|f| f.get_name(ir) == "main")
.unwrap();
inline_all_function_calls(ir, &main_fn)
}
}

/// Inline all calls made from a specific function, effectively removing all `Call` instructions.
///
/// e.g., If this is applied to main() then all calls in the program are removed. This is
Expand Down
18 changes: 17 additions & 1 deletion sway-ir/src/optimize/mem2reg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,25 @@ use rustc_hash::FxHashMap;
use std::collections::{HashMap, HashSet};
use sway_utils::mapped_stack::MappedStack;

pub struct Mem2RegPass;

impl NamedPass for Mem2RegPass {
fn name() -> &'static str {
"mem2reg"
}

fn descr() -> &'static str {
"Promote local memory to SSA registers."
}

fn run(ir: &mut Context) -> Result<bool, IrError> {
Self::run_on_all_fns(ir, promote_to_registers)
}
}

use crate::{
compute_dom_fronts, dominator::compute_dom_tree, Block, BranchToWithArgs, Context, DomTree,
Function, Instruction, IrError, LocalVar, PostOrder, Type, Value, ValueDatum,
Function, Instruction, IrError, LocalVar, NamedPass, PostOrder, Type, Value, ValueDatum,
};

// Check if a value is a valid (for our optimization) local pointer
Expand Down
18 changes: 17 additions & 1 deletion sway-ir/src/optimize/simplify_cfg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,25 @@
use crate::{
block::Block, context::Context, error::IrError, function::Function, instruction::Instruction,
value::ValueDatum, BranchToWithArgs,
value::ValueDatum, BranchToWithArgs, NamedPass,
};

pub struct SimplifyCfgPass;

impl NamedPass for SimplifyCfgPass {
fn name() -> &'static str {
"simplifycfg"
}

fn descr() -> &'static str {
"merge or remove redundant blocks."
}

fn run(ir: &mut Context) -> Result<bool, IrError> {
Self::run_on_all_fns(ir, simplify_cfg)
}
}

pub fn simplify_cfg(context: &mut Context, function: &Function) -> Result<bool, IrError> {
let mut modified = false;
modified |= remove_dead_blocks(context, function)?;
Expand Down
57 changes: 57 additions & 0 deletions sway-ir/src/pass_manager.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
use crate::{Context, Function, IrError};
use std::collections::HashMap;

pub trait NamedPass {
fn name() -> &'static str;
fn descr() -> &'static str;
fn run(ir: &mut Context) -> Result<bool, IrError>;

fn run_on_all_fns<F: FnMut(&mut Context, &Function) -> Result<bool, IrError>>(
ir: &mut Context,
mut run_on_fn: F,
) -> Result<bool, IrError> {
let funcs = ir
.module_iter()
.flat_map(|module| module.function_iter(ir))
.collect::<Vec<_>>();
let mut modified = false;
for func in funcs {
if run_on_fn(ir, &func)? {
modified = true;
}
}
Ok(modified)
}
}

pub type NamePassPair = (&'static str, fn(&mut Context) -> Result<bool, IrError>);

#[derive(Default)]
pub struct PassManager {
passes: HashMap<&'static str, NamePassPair>,
}

impl PassManager {
pub fn register<T: NamedPass>(&mut self) {
self.passes.insert(T::name(), (T::descr(), T::run));
}

pub fn run(&self, name: &str, ir: &mut Context) -> Result<bool, IrError> {
self.passes.get(name).expect("Unknown pass name!").1(ir)
}

pub fn contains(&self, name: &str) -> bool {
self.passes.contains_key(name)
}

pub fn help_text(&self) -> String {
let summary = self
.passes
.iter()
.map(|(name, (descr, _))| format!(" {name:16} - {descr}"))
.collect::<Vec<_>>()
.join("\n");

format!("Valid pass names are:\n\n{summary}",)
}
}

0 comments on commit bb607e1

Please sign in to comment.