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
//! 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 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::*;

mod target_fuel;

#[cfg(test)]
pub mod tests {
    use crate::{PassGroup, PassManager};
    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(
    ///     &["constcombine"],
    ///     "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,
        )
        .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 modified = pass_manager.run(&mut context, &group).unwrap();
        assert_eq!(expected.is_some(), modified);

        let Some(expected) = expected else {
            return;
        };

        let actual = context
            .to_string()
            .lines()
            .filter_map(|x| {
                if x.contains(", !0") {
                    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!("error: {actual:?} {expected:?}");
            } else {
                expected_matches -= 1;
            }
        }

        assert_eq!(expected_matches, 0);
    }
}