summaryrefslogtreecommitdiffstats
path: root/js/src/jit/AliasAnalysis.cpp
blob: ad26a890ed16415e76202d00e91d6ed814360b37 (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
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 * vim: set ts=8 sts=4 et sw=4 tw=99:
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "jit/AliasAnalysis.h"

#include <stdio.h>

#include "jit/AliasAnalysisShared.h"
#include "jit/Ion.h"
#include "jit/IonBuilder.h"
#include "jit/JitSpewer.h"
#include "jit/MIR.h"
#include "jit/MIRGraph.h"

#include "vm/Printer.h"

using namespace js;
using namespace js::jit;

using mozilla::Array;

namespace js {
namespace jit {

class LoopAliasInfo : public TempObject
{
  private:
    LoopAliasInfo* outer_;
    MBasicBlock* loopHeader_;
    MInstructionVector invariantLoads_;

  public:
    LoopAliasInfo(TempAllocator& alloc, LoopAliasInfo* outer, MBasicBlock* loopHeader)
      : outer_(outer), loopHeader_(loopHeader), invariantLoads_(alloc)
    { }

    MBasicBlock* loopHeader() const {
        return loopHeader_;
    }
    LoopAliasInfo* outer() const {
        return outer_;
    }
    bool addInvariantLoad(MInstruction* ins) {
        return invariantLoads_.append(ins);
    }
    const MInstructionVector& invariantLoads() const {
        return invariantLoads_;
    }
    MInstruction* firstInstruction() const {
        return *loopHeader_->begin();
    }
};

} // namespace jit
} // namespace js

AliasAnalysis::AliasAnalysis(MIRGenerator* mir, MIRGraph& graph)
  : AliasAnalysisShared(mir, graph),
    loop_(nullptr)
{
}

// Whether there might be a path from src to dest, excluding loop backedges. This is
// approximate and really ought to depend on precomputed reachability information.
static inline bool
BlockMightReach(MBasicBlock* src, MBasicBlock* dest)
{
    while (src->id() <= dest->id()) {
        if (src == dest)
            return true;
        switch (src->numSuccessors()) {
          case 0:
            return false;
          case 1: {
            MBasicBlock* successor = src->getSuccessor(0);
            if (successor->id() <= src->id())
                return true; // Don't iloop.
            src = successor;
            break;
          }
          default:
            return true;
        }
    }
    return false;
}

static void
IonSpewDependency(MInstruction* load, MInstruction* store, const char* verb, const char* reason)
{
    if (!JitSpewEnabled(JitSpew_Alias))
        return;

    Fprinter& out = JitSpewPrinter();
    out.printf("Load ");
    load->printName(out);
    out.printf(" %s on store ", verb);
    store->printName(out);
    out.printf(" (%s)\n", reason);
}

static void
IonSpewAliasInfo(const char* pre, MInstruction* ins, const char* post)
{
    if (!JitSpewEnabled(JitSpew_Alias))
        return;

    Fprinter& out = JitSpewPrinter();
    out.printf("%s ", pre);
    ins->printName(out);
    out.printf(" %s\n", post);
}

// This pass annotates every load instruction with the last store instruction
// on which it depends. The algorithm is optimistic in that it ignores explicit
// dependencies and only considers loads and stores.
//
// Loads inside loops only have an implicit dependency on a store before the
// loop header if no instruction inside the loop body aliases it. To calculate
// this efficiently, we maintain a list of maybe-invariant loads and the combined
// alias set for all stores inside the loop. When we see the loop's backedge, this
// information is used to mark every load we wrongly assumed to be loop invariant as
// having an implicit dependency on the last instruction of the loop header, so that
// it's never moved before the loop header.
//
// The algorithm depends on the invariant that both control instructions and effectful
// instructions (stores) are never hoisted.
bool
AliasAnalysis::analyze()
{
    Vector<MInstructionVector, AliasSet::NumCategories, JitAllocPolicy> stores(alloc());

    // Initialize to the first instruction.
    MInstruction* firstIns = *graph_.entryBlock()->begin();
    for (unsigned i = 0; i < AliasSet::NumCategories; i++) {
        MInstructionVector defs(alloc());
        if (!defs.append(firstIns))
            return false;
        if (!stores.append(Move(defs)))
            return false;
    }

    // Type analysis may have inserted new instructions. Since this pass depends
    // on the instruction number ordering, all instructions are renumbered.
    uint32_t newId = 0;

    for (ReversePostorderIterator block(graph_.rpoBegin()); block != graph_.rpoEnd(); block++) {
        if (mir->shouldCancel("Alias Analysis (main loop)"))
            return false;

        if (block->isLoopHeader()) {
            JitSpew(JitSpew_Alias, "Processing loop header %d", block->id());
            loop_ = new(alloc()) LoopAliasInfo(alloc(), loop_, *block);
        }

        for (MPhiIterator def(block->phisBegin()), end(block->phisEnd()); def != end; ++def)
            def->setId(newId++);

        for (MInstructionIterator def(block->begin()), end(block->begin(block->lastIns()));
             def != end;
             ++def)
        {
            def->setId(newId++);

            AliasSet set = def->getAliasSet();
            if (set.isNone())
                continue;

            // For the purposes of alias analysis, all recoverable operations
            // are treated as effect free as the memory represented by these
            // operations cannot be aliased by others.
            if (def->canRecoverOnBailout())
                continue;

            if (set.isStore()) {
                for (AliasSetIterator iter(set); iter; iter++) {
                    if (!stores[*iter].append(*def))
                        return false;
                }

                if (JitSpewEnabled(JitSpew_Alias)) {
                    Fprinter& out = JitSpewPrinter();
                    out.printf("Processing store ");
                    def->printName(out);
                    out.printf(" (flags %x)\n", set.flags());
                }
            } else {
                // Find the most recent store on which this instruction depends.
                MInstruction* lastStore = firstIns;

                for (AliasSetIterator iter(set); iter; iter++) {
                    MInstructionVector& aliasedStores = stores[*iter];
                    for (int i = aliasedStores.length() - 1; i >= 0; i--) {
                        MInstruction* store = aliasedStores[i];
                        if (genericMightAlias(*def, store) != MDefinition::AliasType::NoAlias &&
                            def->mightAlias(store) != MDefinition::AliasType::NoAlias &&
                            BlockMightReach(store->block(), *block))
                        {
                            if (lastStore->id() < store->id())
                                lastStore = store;
                            break;
                        }
                    }
                }

                def->setDependency(lastStore);
                IonSpewDependency(*def, lastStore, "depends", "");

                // If the last store was before the current loop, we assume this load
                // is loop invariant. If a later instruction writes to the same location,
                // we will fix this at the end of the loop.
                if (loop_ && lastStore->id() < loop_->firstInstruction()->id()) {
                    if (!loop_->addInvariantLoad(*def))
                        return false;
                }
            }
        }

        // Renumber the last instruction, as the analysis depends on this and the order.
        block->lastIns()->setId(newId++);

        if (block->isLoopBackedge()) {
            MOZ_ASSERT(loop_->loopHeader() == block->loopHeaderOfBackedge());
            JitSpew(JitSpew_Alias, "Processing loop backedge %d (header %d)", block->id(),
                    loop_->loopHeader()->id());
            LoopAliasInfo* outerLoop = loop_->outer();
            MInstruction* firstLoopIns = *loop_->loopHeader()->begin();

            const MInstructionVector& invariant = loop_->invariantLoads();

            for (unsigned i = 0; i < invariant.length(); i++) {
                MInstruction* ins = invariant[i];
                AliasSet set = ins->getAliasSet();
                MOZ_ASSERT(set.isLoad());

                bool hasAlias = false;
                for (AliasSetIterator iter(set); iter; iter++) {
                    MInstructionVector& aliasedStores = stores[*iter];
                    for (int i = aliasedStores.length() - 1;; i--) {
                        MInstruction* store = aliasedStores[i];
                        if (store->id() < firstLoopIns->id())
                            break;
                        if (genericMightAlias(ins, store) != MDefinition::AliasType::NoAlias &&
                            ins->mightAlias(store) != MDefinition::AliasType::NoAlias)
                        {
                            hasAlias = true;
                            IonSpewDependency(ins, store, "aliases", "store in loop body");
                            break;
                        }
                    }
                    if (hasAlias)
                        break;
                }

                if (hasAlias) {
                    // This instruction depends on stores inside the loop body. Mark it as having a
                    // dependency on the last instruction of the loop header. The last instruction is a
                    // control instruction and these are never hoisted.
                    MControlInstruction* controlIns = loop_->loopHeader()->lastIns();
                    IonSpewDependency(ins, controlIns, "depends", "due to stores in loop body");
                    ins->setDependency(controlIns);
                } else {
                    IonSpewAliasInfo("Load", ins, "does not depend on any stores in this loop");

                    if (outerLoop && ins->dependency()->id() < outerLoop->firstInstruction()->id()) {
                        IonSpewAliasInfo("Load", ins, "may be invariant in outer loop");
                        if (!outerLoop->addInvariantLoad(ins))
                            return false;
                    }
                }
            }
            loop_ = loop_->outer();
        }
    }

    spewDependencyList();

    MOZ_ASSERT(loop_ == nullptr);
    return true;
}