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 arg_mutability_tagger;
19pub use arg_mutability_tagger::*;
20pub mod const_demotion;
21pub use const_demotion::*;
22pub mod constants;
23pub use constants::*;
24pub mod conditional_constprop;
25pub use conditional_constprop::*;
26pub mod cse;
27pub use cse::*;
28pub mod dce;
29pub use dce::*;
30pub mod inline;
31pub use inline::*;
32pub mod mem2reg;
33pub use mem2reg::*;
34pub mod memcpyopt;
35pub use memcpyopt::*;
36pub mod misc_demotion;
37pub use misc_demotion::*;
38pub mod ret_demotion;
39pub use ret_demotion::*;
40pub mod simplify_cfg;
41pub use simplify_cfg::*;
42pub mod sroa;
43pub use sroa::*;
44pub mod fn_dedup;
45pub use fn_dedup::*;
46
47mod target_fuel;
48
49#[cfg(test)]
50pub mod tests {
51    use crate::{PassGroup, PassManager};
52    use sway_features::ExperimentalFeatures;
53    use sway_types::SourceEngine;
54
55    /// This function parses the IR text representation and run the specified optimizers passes.
56    /// Then, depending on the `expected` parameter it checks if the IR was optimized or not.
57    ///
58    /// This comparison is done by capturing all instructions with metadata "!0".
59    ///
60    /// For example:
61    ///
62    /// ```rust, ignore
63    /// assert_optimization(
64    ///     &[CONST_FOLDING_NAME],
65    ///     "entry fn main() -> u64 {
66    ///        entry():
67    ///             l = const u64 1
68    ///             r = const u64 2
69    ///             result = add l, r, !0
70    ///             ret u64 result
71    ///     }",
72    ///     ["const u64 3"],
73    /// );
74    /// ```
75    pub(crate) fn assert_optimization<'a>(
76        passes: &[&'static str],
77        body: &str,
78        expected: Option<impl IntoIterator<Item = &'a str>>,
79    ) {
80        let source_engine = SourceEngine::default();
81        let mut context = crate::parse(
82            &format!(
83                "script {{
84                {body}
85            }}
86
87            !0 = \"a.sw\"
88            "
89            ),
90            &source_engine,
91            ExperimentalFeatures::default(),
92        )
93        .unwrap();
94
95        let mut pass_manager = PassManager::default();
96        crate::register_known_passes(&mut pass_manager);
97
98        let mut group = PassGroup::default();
99        for pass in passes {
100            group.append_pass(pass);
101        }
102
103        let before = context.to_string();
104        let modified = pass_manager.run(&mut context, &group).unwrap();
105        let after = context.to_string();
106
107        // print diff to help debug
108        if std::env::args().any(|x| x == "--nocapture") {
109            println!("{}", prettydiff::diff_lines(&before, &after));
110        }
111
112        assert_eq!(expected.is_some(), modified);
113
114        let Some(expected) = expected else {
115            return;
116        };
117
118        let actual = context
119            .to_string()
120            .lines()
121            .filter_map(|x| {
122                if x.contains(", !") {
123                    Some(format!("{}\n", x.trim()))
124                } else {
125                    None
126                }
127            })
128            .collect::<Vec<String>>();
129
130        assert!(!actual.is_empty());
131
132        let mut expected_matches = actual.len();
133
134        for (actual, expected) in actual.iter().zip(expected) {
135            if !actual.contains(expected) {
136                panic!("Actual: {actual:?} does not contains expected: {expected:?}. (Run with --nocapture to see a diff)");
137            } else {
138                expected_matches -= 1;
139            }
140        }
141
142        assert_eq!(expected_matches, 0);
143    }
144}