sway_ir/
optimize.rs

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