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
use log::trace;
use std::collections::HashSet;
use std::hash::Hash;

use super::errors::SSAResult;

pub trait SSAConfig: Sized {
    /// The type used to track variable versions.
    type Version;

    /// The type of a variable.
    type Variable: PartialEq + Eq + Hash + Clone;

    /// An environment type used to track version across the CFG.
    type Environment: SSAEnvironment;

    /// The type of a statement.
    type Statement: SSAStatement<Self>;

    /// The type of a basic block.
    type BasicBlock: SSABasicBlock<Self> + DirectedGraphNode;
}

/// An environment used to track variable versions across a CFG.
pub trait SSAEnvironment {
    /// Enter variable scope.
    fn add_variable_scope(&mut self);

    /// Leave variable scope.
    fn remove_variable_scope(&mut self);
}

/// A basic block containing a (possibly empty) list of statements.
pub trait SSABasicBlock<Cfg: SSAConfig>: DirectedGraphNode {
    /// Add the given statement to the front of the basic block.
    fn prepend_statement(&mut self, stmt: Cfg::Statement);

    /// Returns an iterator over the statements of the basic block.
    ///
    /// Note: We have to use dynamic dispatch here because returning `impl
    /// Trait` from trait methods is not a thing yet. For details, see
    /// rust-lang.github.io/impl-trait-initiative/RFCs/rpit-in-traits.html)
    fn statements<'a>(&'a self) -> Box<dyn Iterator<Item = &'a Cfg::Statement> + 'a>;

    /// Returns an iterator over mutable references to the statements of the
    /// basic block.
    ///
    /// Note: We have to use dynamic dispatch here because returning `impl
    /// Trait` from trait methods is not a thing yet. For details, see
    /// rust-lang.github.io/impl-trait-initiative/RFCs/rpit-in-traits.html)
    fn statements_mut<'a>(&'a mut self) -> Box<dyn Iterator<Item = &'a mut Cfg::Statement> + 'a>;

    /// Returns the set of variables written by the basic block.
    fn variables_written(&self) -> HashSet<Cfg::Variable> {
        self.statements().fold(HashSet::new(), |mut vars, stmt| {
            vars.extend(stmt.variables_written());
            vars
        })
    }

    /// Returns true if the basic block has a phi statement for the given
    /// variable.
    fn has_phi_statement(&self, var: &Cfg::Variable) -> bool {
        self.statements().any(|stmt| stmt.is_phi_statement_for(var))
    }

    /// Inserts a new phi statement for the given variable at the top of the basic
    /// block.
    fn insert_phi_statement(&mut self, var: &Cfg::Variable, env: &Cfg::Environment) {
        self.prepend_statement(SSAStatement::new_phi_statement(var, env));
    }

    /// Updates the RHS of each phi statement in the basic block with the SSA
    /// variable versions from the given environment.
    fn update_phi_statements(&mut self, env: &Cfg::Environment) {
        trace!("updating phi expression arguments in block {}", self.index());
        for stmt in self.statements_mut() {
            if stmt.is_phi_statement() {
                stmt.ensure_phi_argument(env);
            } else {
                // Since phi statements proceed all other statements we are done
                // here.
                break;
            }
        }
    }

    /// Updates each variable to the corresponding SSA variable, in each
    /// statement in the basic block.
    fn insert_ssa_variables(&mut self, env: &mut Cfg::Environment) -> SSAResult<()> {
        trace!("inserting SSA variables in block {}", self.index());
        for stmt in self.statements_mut() {
            stmt.insert_ssa_variables(env)?;
        }
        Ok(())
    }
}

/// A statement in the language.
pub trait SSAStatement<Cfg: SSAConfig>: Clone {
    /// Returns the set of variables written by statement.
    fn variables_written(&self) -> HashSet<Cfg::Variable>;

    /// Returns a new phi statement (with empty RHS) for the given variable.
    fn new_phi_statement(name: &Cfg::Variable, env: &Cfg::Environment) -> Self;

    /// Returns true iff the statement is a phi statement.
    fn is_phi_statement(&self) -> bool;

    /// Returns true iff the statement is a phi statement for the given variable.
    fn is_phi_statement_for(&self, var: &Cfg::Variable) -> bool;

    /// Ensure that the phi expression argument list of a phi statement contains the
    /// current version of the variable, according to the given environment.
    ///
    /// Panics if the statement is not a phi statement.
    fn ensure_phi_argument(&mut self, env: &Cfg::Environment);

    /// Replace each variable occurring in the statement by the corresponding
    /// versioned SSA variable.
    fn insert_ssa_variables(&mut self, env: &mut Cfg::Environment) -> SSAResult<()>;
}

pub type Index = usize;
pub type IndexSet = HashSet<Index>;

/// This trait is used to make graph algorithms (like dominator tree and dominator
/// frontier generation) generic over the graph node type for unit testing purposes.
pub trait DirectedGraphNode {
    fn index(&self) -> Index;

    fn predecessors(&self) -> &IndexSet;

    fn successors(&self) -> &IndexSet;
}