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
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim: set ts=8 sts=2 et sw=2 tw=80:
* 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 "mozilla/DebugOnly.h"
#include "jit/JitSpewer.h"
#include "jit/MIR.h"
#include "jit/MIRGenerator.h"
#include "jit/MIRGraph.h"
#include "js/Printer.h"
using namespace js;
using namespace js::jit;
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
void AliasAnalysis::spewDependencyList() {
#ifdef JS_JITSPEW
if (JitSpewEnabled(JitSpew_AliasSummaries)) {
Fprinter& print = JitSpewPrinter();
JitSpewHeader(JitSpew_AliasSummaries);
print.printf("Dependency list for other passes:\n");
for (ReversePostorderIterator block(graph_.rpoBegin());
block != graph_.rpoEnd(); block++) {
for (MInstructionIterator def(block->begin()),
end(block->begin(block->lastIns()));
def != end; ++def) {
if (!def->dependency()) {
continue;
}
if (!def->getAliasSet().isLoad()) {
continue;
}
JitSpewHeader(JitSpew_AliasSummaries);
print.printf(" ");
MDefinition::PrintOpcodeName(print, def->op());
print.printf("%u marked depending on ", def->id());
MDefinition::PrintOpcodeName(print, def->dependency()->op());
print.printf("%u\n", def->dependency()->id());
}
}
}
#endif
}
// 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) {
#ifdef JS_JITSPEW
if (!JitSpewEnabled(JitSpew_Alias)) {
return;
}
JitSpewHeader(JitSpew_Alias);
Fprinter& out = JitSpewPrinter();
out.printf(" Load ");
load->printName(out);
out.printf(" %s on store ", verb);
store->printName(out);
out.printf(" (%s)\n", reason);
#endif
}
static void IonSpewAliasInfo(const char* pre, MInstruction* ins,
const char* post) {
#ifdef JS_JITSPEW
if (!JitSpewEnabled(JitSpew_Alias)) {
return;
}
JitSpewHeader(JitSpew_Alias);
Fprinter& out = JitSpewPrinter();
out.printf(" %s ", pre);
ins->printName(out);
out.printf(" %s\n", post);
#endif
}
// [SMDOC] IonMonkey Alias Analysis
//
// 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() {
JitSpew(JitSpew_Alias, "Begin");
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(std::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 %u", block->id());
loop_ = new (alloc().fallible()) LoopAliasInfo(alloc(), loop_, *block);
if (!loop_) {
return false;
}
}
for (MPhiIterator def(block->phisBegin()), end(block->phisEnd());
def != end; ++def) {
def->setId(newId++);
}
{
// "The block has one or more instructions"
MOZ_ASSERT(block->hasAnyIns());
// "The last instruction is a control instruction"
MOZ_ASSERT(block->hasLastIns());
// "The only control instructions that can have a non-empty alias set
// are MWasmCallCatchable and MWasmReturnCall".
// Note, this isn't a requirement that is intrinsic to MIR. In
// principle, any control instruction can have a non-empty alias set,
// and that should be correctly handled by this routine. The assertion
// merely reflects the current state of usage of MIR, in which
// MWasmCallCatchable and MWasmReturnCall are the only control
// instructions we generate that have non-empty alias sets.
// See bug 1837686.
mozilla::DebugOnly<MControlInstruction*> lastIns = block->lastIns();
MOZ_ASSERT_IF(
!lastIns->isWasmCallCatchable() && !lastIns->isWasmReturnCall(),
lastIns->getAliasSet().isNone());
}
for (MInstructionIterator def(block->begin()), end(block->end());
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;
}
}
#ifdef JS_JITSPEW
if (JitSpewEnabled(JitSpew_Alias)) {
JitSpewHeader(JitSpew_Alias);
Fprinter& out = JitSpewPrinter();
out.printf("Processing store ");
def->printName(out);
out.printf(" (flags %x)\n", set.flags());
}
#endif
} 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 (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;
}
}
}
}
if (block->isLoopBackedge()) {
MOZ_ASSERT(loop_->loopHeader() == block->loopHeaderOfBackedge());
JitSpew(JitSpew_Alias, "Processing loop backedge %u (header %u)",
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 (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;
}
|