sway_ir/optimize.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
//! A collection of optimization passes.
//!
//! Each of these modules are a collection of typical code optimisation passes.
//!
//! Currently there is no pass manager, as there are only a couple of passes, but this is something
//! which will be added in the future.
//!
//! So each of the functions under this module will return a boolean indicating whether a
//! modification to the IR was made. Typically the passes will be just re-run until they no longer
//! make any such modifications, implying they've optimized as much possible.
//!
//! When writing passes one should keep in mind that when a modification is made then any iterators
//! over blocks or instructions can be invalidated, and starting over is a safer option than trying
//! to attempt multiple changes at once.
pub mod arg_demotion;
pub use arg_demotion::*;
pub mod const_demotion;
pub use const_demotion::*;
pub mod constants;
pub use constants::*;
pub mod conditional_constprop;
pub use conditional_constprop::*;
pub mod cse;
pub use cse::*;
pub mod dce;
pub use dce::*;
pub mod inline;
pub use inline::*;
pub mod mem2reg;
pub use mem2reg::*;
pub mod memcpyopt;
pub use memcpyopt::*;
pub mod misc_demotion;
pub use misc_demotion::*;
pub mod ret_demotion;
pub use ret_demotion::*;
pub mod simplify_cfg;
pub use simplify_cfg::*;
pub mod sroa;
pub use sroa::*;
pub mod fn_dedup;
pub use fn_dedup::*;
mod target_fuel;
#[cfg(test)]
pub mod tests {
use crate::{PassGroup, PassManager};
use sway_features::ExperimentalFeatures;
use sway_types::SourceEngine;
/// This function parses the IR text representation and run the specified optimizers passes.
/// Then, depending on the `expected` parameter it checks if the IR was optimized or not.
///
/// This comparison is done by capturing all instructions with metadata "!0".
///
/// For example:
///
/// ```rust, ignore
/// assert_optimization(
/// &[CONST_FOLDING_NAME],
/// "entry fn main() -> u64 {
/// entry():
/// l = const u64 1
/// r = const u64 2
/// result = add l, r, !0
/// ret u64 result
/// }",
/// ["const u64 3"],
/// );
/// ```
pub(crate) fn assert_optimization<'a>(
passes: &[&'static str],
body: &str,
expected: Option<impl IntoIterator<Item = &'a str>>,
) {
let source_engine = SourceEngine::default();
let mut context = crate::parse(
&format!(
"script {{
{body}
}}
!0 = \"a.sw\"
"
),
&source_engine,
ExperimentalFeatures::default(),
)
.unwrap();
let mut pass_manager = PassManager::default();
crate::register_known_passes(&mut pass_manager);
let mut group = PassGroup::default();
for pass in passes {
group.append_pass(pass);
}
let before = context.to_string();
let modified = pass_manager.run(&mut context, &group).unwrap();
let after = context.to_string();
// print diff to help debug
if std::env::args().any(|x| x == "--nocapture") {
println!("{}", prettydiff::diff_lines(&before, &after));
}
assert_eq!(expected.is_some(), modified);
let Some(expected) = expected else {
return;
};
let actual = context
.to_string()
.lines()
.filter_map(|x| {
if x.contains(", !") {
Some(format!("{}\n", x.trim()))
} else {
None
}
})
.collect::<Vec<String>>();
assert!(!actual.is_empty());
let mut expected_matches = actual.len();
for (actual, expected) in actual.iter().zip(expected) {
if !actual.contains(expected) {
panic!("Actual: {actual:?} does not contains expected: {expected:?}. (Run with --nocapture to see a diff)");
} else {
expected_matches -= 1;
}
}
assert_eq!(expected_matches, 0);
}
}