summaryrefslogtreecommitdiff
path: root/Source/JavaScriptCore/dfg/DFGSSACalculator.h
blob: 66e305fa8411038ff87ad81f200524ac28d89c6a (plain)
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
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
/*
 * Copyright (C) 2014 Apple Inc. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
 */

#ifndef DFGSSACalculator_h
#define DFGSSACalculator_h

#if ENABLE(DFG_JIT)

#include "DFGDominators.h"
#include "DFGGraph.h"

namespace JSC { namespace DFG {

// SSACalculator provides a reusable tool for using the Cytron, Ferrante, Rosen, Wegman, and
// Zadeck "Efficiently Computing Static Single Assignment Form and the Control Dependence Graph"
// (TOPLAS'91) algorithm for computing SSA. SSACalculator doesn't magically do everything for you
// but it maintains the major data structures and handles most of the non-local reasoning. Here's
// the workflow of using SSACalculator to execute this algorithm:
//
// 0) Create a fresh SSACalculator instance. You will need this instance only for as long as
//    you're not yet done computing SSA.
//
// 1) Create an SSACalculator::Variable for every variable that you want to do Phi insertion
//    on. SSACalculator::Variable::index() is a dense indexing of the Variables that you
//    created, so you can easily use a Vector to map the SSACalculator::Variables to your
//    variables.
//
// 2) Create a SSACalculator::Def for every assignment to those variables. A Def knows about the
//    variable, the block, and the DFG::Node* that has the value being put into the variable.
//    Note that creating a Def in block B for variable V if block B already has a def for variable
//    V will overwrite the previous Def's DFG::Node* value. This enables you to create Defs by
//    processing basic blocks in forward order. If a block has multiple Defs of a variable, this
//    "just works" because each block will then remember the last Def of each variable.
//
// 3) Call SSACalculator::computePhis(). This takes a functor that will create the Phi nodes. The
//    functor returns either the Phi node it created, or nullptr, if it chooses to prune. (As an
//    aside, it's always sound not to prune, and the safest reason for pruning is liveness.) The
//    computePhis() code will record the created Phi nodes as Defs, and it will separately record
//    the list of Phis inserted at each block. It's OK for the functor you pass here to modify the
//    DFG::Graph on the fly, but the easiest way to write this is to just create the Phi nodes by
//    doing Graph::addNode() and return them. It's then best to insert all Phi nodes for a block
//    in bulk as part of the pass you do below, in step (4).
//
// 4) Modify the graph to create the SSA data flow. For each block, this should:
//
//    4.0) Compute the set of reaching defs (aka available values) for each variable by calling
//         SSACalculator::reachingDefAtHead() for each variable. Record this in a local table that
//         will be incrementally updated as you proceed through the block in forward order in the
//         next steps:
//
//         FIXME: It might be better to compute reaching defs for all live variables in one go, to
//         avoid doing repeated dom tree traversals.
//         https://bugs.webkit.org/show_bug.cgi?id=136610
//
//    4.1) Insert all of the Phi nodes for the block by using SSACalculator::phisForBlock(), and
//         record those Phi nodes as being available values.
//
//    4.2) Process the block in forward order. For each load from a variable, replace it with the
//         available SSA value for that variable. For each store, delete it and record the stored
//         value as being available.
//
//         Note that you have two options of how to replace loads with SSA values. You can replace
//         the load with an Identity node; this will end up working fairly naturally so long as
//         you run GCSE after your phase. Or, you can replace all uses of the load with the SSA
//         value yourself (using the Graph::performSubstitution() idiom), but that requires that
//         your loop over basic blocks proceeds in the appropriate graph order, for example
//         preorder.
//
//         FIXME: Make it easier to do this, that doesn't involve rerunning GCSE.
//         https://bugs.webkit.org/show_bug.cgi?id=136639
//
//    4.3) Insert Upsilons at the end of the current block for the corresponding Phis in each successor block. 
//         Use the available values table to decide the source value for each Phi's variable. Note that 
//         you could also use SSACalculator::reachingDefAtTail() instead of the available values table, 
//         though your local available values table is likely to be more efficient.
//
// The most obvious use of SSACalculator is for the CPS->SSA conversion itself, but it's meant to
// also be used for SSA update and for things like the promotion of heap fields to local SSA
// variables.

class SSACalculator {
public:
    SSACalculator(Graph&);
    ~SSACalculator();
    
    void reset();
    
    class Variable {
    public:
        unsigned index() const { return m_index; }
        
        void dump(PrintStream&) const;
        void dumpVerbose(PrintStream&) const;
        
    private:
        friend class SSACalculator;
        
        Variable()
            : m_index(UINT_MAX)
        {
        }
        
        Variable(unsigned index)
            : m_index(index)
        {
        }

        BlockList m_blocksWithDefs;
        unsigned m_index;
    };
    
    class Def {
    public:
        Variable* variable() const { return m_variable; }
        BasicBlock* block() const { return m_block; }
        
        Node* value() const { return m_value; }
        
        void dump(PrintStream&) const;
        
    private:
        friend class SSACalculator;
        
        Def()
            : m_variable(nullptr)
            , m_block(nullptr)
            , m_value(nullptr)
        {
        }
        
        Def(Variable* variable, BasicBlock* block, Node* value)
            : m_variable(variable)
            , m_block(block)
            , m_value(value)
        {
        }
        
        Variable* m_variable;
        BasicBlock* m_block;
        Node* m_value;
    };
    
    Variable* newVariable();
    Def* newDef(Variable*, BasicBlock*, Node*);
    
    Variable* variable(unsigned index) { return &m_variables[index]; }
    
    // The PhiInsertionFunctor takes a Variable and a BasicBlock and either inserts a Phi and
    // returns the Node for that Phi, or it decides that it's not worth it to insert a Phi at that
    // block because of some additional pruning condition (typically liveness) and returns
    // nullptr. If a non-null Node* is returned, a new Def is created, so that
    // nonLocalReachingDef() will find it later. Note that it is generally always sound to not
    // prune any Phis (that is, to always have the functor insert a Phi and never return nullptr).
    template<typename PhiInsertionFunctor>
    void computePhis(const PhiInsertionFunctor& functor)
    {
        DFG_ASSERT(m_graph, nullptr, m_graph.m_dominators);
        
        for (Variable& variable : m_variables) {
            m_graph.m_dominators->forAllBlocksInPrunedIteratedDominanceFrontierOf(
                variable.m_blocksWithDefs,
                [&] (BasicBlock* block) -> bool {
                    Node* phiNode = functor(&variable, block);
                    if (!phiNode)
                        return false;
                    
                    BlockData& data = m_data[block];
                    Def* phiDef = m_phis.add(Def(&variable, block, phiNode));
                    data.m_phis.append(phiDef);
                    
                    // Note that it's possible to have a block that looks like this before SSA
                    // conversion:
                    //
                    // label:
                    //     print(x);
                    //     ...
                    //     x = 42;
                    //     goto label;
                    //
                    // And it may look like this after SSA conversion:
                    //
                    // label:
                    //     x1: Phi()
                    //     ...
                    //     Upsilon(42, ^x1)
                    //     goto label;
                    //
                    // In this case, we will want to insert a Phi in this block, and the block
                    // will already have a Def for the variable. When this happens, we don't want
                    // the Phi to override the original Def, since the Phi is at the top, the
                    // original Def in the m_defs table would have been at the bottom, and we want
                    // m_defs to tell us about defs at tail.
                    //
                    // So, we rely on the fact that HashMap::add() does nothing if the key was
                    // already present.
                    data.m_defs.add(&variable, phiDef);
                    return true;
                });
        }
    }
    
    const Vector<Def*>& phisForBlock(BasicBlock* block)
    {
        return m_data[block].m_phis;
    }
    
    // Ignores defs within the given block; it assumes that you've taken care of those
    // yourself.
    Def* nonLocalReachingDef(BasicBlock*, Variable*);
    Def* reachingDefAtHead(BasicBlock* block, Variable* variable)
    {
        return nonLocalReachingDef(block, variable);
    }
    
    // Considers the def within the given block, but only works at the tail of the block.
    Def* reachingDefAtTail(BasicBlock*, Variable*);
    
    void dump(PrintStream&) const;
    
private:
    SegmentedVector<Variable> m_variables;
    Bag<Def> m_defs;
    
    Bag<Def> m_phis;
    
    struct BlockData {
        HashMap<Variable*, Def*> m_defs;
        Vector<Def*> m_phis;
    };
    
    BlockMap<BlockData> m_data;
    
    Graph& m_graph;
};

} } // namespace JSC::DFG

#endif // ENABLE(DFG_JIT)

#endif // DFGSSACalculator_h