|
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- |
|
2 * vim: set ts=8 sts=4 et sw=4 tw=99: |
|
3 * This Source Code Form is subject to the terms of the Mozilla Public |
|
4 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
6 |
|
7 #include "jit/AliasAnalysis.h" |
|
8 |
|
9 #include <stdio.h> |
|
10 |
|
11 #include "jit/Ion.h" |
|
12 #include "jit/IonBuilder.h" |
|
13 #include "jit/IonSpewer.h" |
|
14 #include "jit/MIR.h" |
|
15 #include "jit/MIRGraph.h" |
|
16 |
|
17 using namespace js; |
|
18 using namespace js::jit; |
|
19 |
|
20 using mozilla::Array; |
|
21 |
|
22 namespace js { |
|
23 namespace jit { |
|
24 |
|
25 class LoopAliasInfo : public TempObject |
|
26 { |
|
27 private: |
|
28 LoopAliasInfo *outer_; |
|
29 MBasicBlock *loopHeader_; |
|
30 MDefinitionVector invariantLoads_; |
|
31 |
|
32 public: |
|
33 LoopAliasInfo(TempAllocator &alloc, LoopAliasInfo *outer, MBasicBlock *loopHeader) |
|
34 : outer_(outer), loopHeader_(loopHeader), invariantLoads_(alloc) |
|
35 { } |
|
36 |
|
37 MBasicBlock *loopHeader() const { |
|
38 return loopHeader_; |
|
39 } |
|
40 LoopAliasInfo *outer() const { |
|
41 return outer_; |
|
42 } |
|
43 bool addInvariantLoad(MDefinition *ins) { |
|
44 return invariantLoads_.append(ins); |
|
45 } |
|
46 const MDefinitionVector& invariantLoads() const { |
|
47 return invariantLoads_; |
|
48 } |
|
49 MDefinition *firstInstruction() const { |
|
50 return *loopHeader_->begin(); |
|
51 } |
|
52 }; |
|
53 |
|
54 } // namespace jit |
|
55 } // namespace js |
|
56 |
|
57 namespace { |
|
58 |
|
59 // Iterates over the flags in an AliasSet. |
|
60 class AliasSetIterator |
|
61 { |
|
62 private: |
|
63 uint32_t flags; |
|
64 unsigned pos; |
|
65 |
|
66 public: |
|
67 AliasSetIterator(AliasSet set) |
|
68 : flags(set.flags()), pos(0) |
|
69 { |
|
70 while (flags && (flags & 1) == 0) { |
|
71 flags >>= 1; |
|
72 pos++; |
|
73 } |
|
74 } |
|
75 AliasSetIterator& operator ++(int) { |
|
76 do { |
|
77 flags >>= 1; |
|
78 pos++; |
|
79 } while (flags && (flags & 1) == 0); |
|
80 return *this; |
|
81 } |
|
82 operator bool() const { |
|
83 return !!flags; |
|
84 } |
|
85 unsigned operator *() const { |
|
86 JS_ASSERT(pos < AliasSet::NumCategories); |
|
87 return pos; |
|
88 } |
|
89 }; |
|
90 |
|
91 } /* anonymous namespace */ |
|
92 |
|
93 AliasAnalysis::AliasAnalysis(MIRGenerator *mir, MIRGraph &graph) |
|
94 : mir(mir), |
|
95 graph_(graph), |
|
96 loop_(nullptr) |
|
97 { |
|
98 } |
|
99 |
|
100 // Whether there might be a path from src to dest, excluding loop backedges. This is |
|
101 // approximate and really ought to depend on precomputed reachability information. |
|
102 static inline bool |
|
103 BlockMightReach(MBasicBlock *src, MBasicBlock *dest) |
|
104 { |
|
105 while (src->id() <= dest->id()) { |
|
106 if (src == dest) |
|
107 return true; |
|
108 switch (src->numSuccessors()) { |
|
109 case 0: |
|
110 return false; |
|
111 case 1: { |
|
112 MBasicBlock *successor = src->getSuccessor(0); |
|
113 if (successor->id() <= src->id()) |
|
114 return true; // Don't iloop. |
|
115 src = successor; |
|
116 break; |
|
117 } |
|
118 default: |
|
119 return true; |
|
120 } |
|
121 } |
|
122 return false; |
|
123 } |
|
124 |
|
125 static void |
|
126 IonSpewDependency(MDefinition *load, MDefinition *store, const char *verb, const char *reason) |
|
127 { |
|
128 if (!IonSpewEnabled(IonSpew_Alias)) |
|
129 return; |
|
130 |
|
131 fprintf(IonSpewFile, "Load "); |
|
132 load->printName(IonSpewFile); |
|
133 fprintf(IonSpewFile, " %s on store ", verb); |
|
134 store->printName(IonSpewFile); |
|
135 fprintf(IonSpewFile, " (%s)\n", reason); |
|
136 } |
|
137 |
|
138 static void |
|
139 IonSpewAliasInfo(const char *pre, MDefinition *ins, const char *post) |
|
140 { |
|
141 if (!IonSpewEnabled(IonSpew_Alias)) |
|
142 return; |
|
143 |
|
144 fprintf(IonSpewFile, "%s ", pre); |
|
145 ins->printName(IonSpewFile); |
|
146 fprintf(IonSpewFile, " %s\n", post); |
|
147 } |
|
148 |
|
149 // This pass annotates every load instruction with the last store instruction |
|
150 // on which it depends. The algorithm is optimistic in that it ignores explicit |
|
151 // dependencies and only considers loads and stores. |
|
152 // |
|
153 // Loads inside loops only have an implicit dependency on a store before the |
|
154 // loop header if no instruction inside the loop body aliases it. To calculate |
|
155 // this efficiently, we maintain a list of maybe-invariant loads and the combined |
|
156 // alias set for all stores inside the loop. When we see the loop's backedge, this |
|
157 // information is used to mark every load we wrongly assumed to be loop invaraint as |
|
158 // having an implicit dependency on the last instruction of the loop header, so that |
|
159 // it's never moved before the loop header. |
|
160 // |
|
161 // The algorithm depends on the invariant that both control instructions and effectful |
|
162 // instructions (stores) are never hoisted. |
|
163 bool |
|
164 AliasAnalysis::analyze() |
|
165 { |
|
166 Vector<MDefinitionVector, AliasSet::NumCategories, IonAllocPolicy> stores(alloc()); |
|
167 |
|
168 // Initialize to the first instruction. |
|
169 MDefinition *firstIns = *graph_.begin()->begin(); |
|
170 for (unsigned i = 0; i < AliasSet::NumCategories; i++) { |
|
171 MDefinitionVector defs(alloc()); |
|
172 if (!defs.append(firstIns)) |
|
173 return false; |
|
174 if (!stores.append(Move(defs))) |
|
175 return false; |
|
176 } |
|
177 |
|
178 // Type analysis may have inserted new instructions. Since this pass depends |
|
179 // on the instruction number ordering, all instructions are renumbered. |
|
180 // We start with 1 because some passes use 0 to denote failure. |
|
181 uint32_t newId = 1; |
|
182 |
|
183 for (ReversePostorderIterator block(graph_.rpoBegin()); block != graph_.rpoEnd(); block++) { |
|
184 if (mir->shouldCancel("Alias Analysis (main loop)")) |
|
185 return false; |
|
186 |
|
187 if (block->isLoopHeader()) { |
|
188 IonSpew(IonSpew_Alias, "Processing loop header %d", block->id()); |
|
189 loop_ = new(alloc()) LoopAliasInfo(alloc(), loop_, *block); |
|
190 } |
|
191 |
|
192 for (MDefinitionIterator def(*block); def; def++) { |
|
193 def->setId(newId++); |
|
194 |
|
195 AliasSet set = def->getAliasSet(); |
|
196 if (set.isNone()) |
|
197 continue; |
|
198 |
|
199 if (set.isStore()) { |
|
200 for (AliasSetIterator iter(set); iter; iter++) { |
|
201 if (!stores[*iter].append(*def)) |
|
202 return false; |
|
203 } |
|
204 |
|
205 if (IonSpewEnabled(IonSpew_Alias)) { |
|
206 fprintf(IonSpewFile, "Processing store "); |
|
207 def->printName(IonSpewFile); |
|
208 fprintf(IonSpewFile, " (flags %x)\n", set.flags()); |
|
209 } |
|
210 } else { |
|
211 // Find the most recent store on which this instruction depends. |
|
212 MDefinition *lastStore = firstIns; |
|
213 |
|
214 for (AliasSetIterator iter(set); iter; iter++) { |
|
215 MDefinitionVector &aliasedStores = stores[*iter]; |
|
216 for (int i = aliasedStores.length() - 1; i >= 0; i--) { |
|
217 MDefinition *store = aliasedStores[i]; |
|
218 if (def->mightAlias(store) && BlockMightReach(store->block(), *block)) { |
|
219 if (lastStore->id() < store->id()) |
|
220 lastStore = store; |
|
221 break; |
|
222 } |
|
223 } |
|
224 } |
|
225 |
|
226 def->setDependency(lastStore); |
|
227 IonSpewDependency(*def, lastStore, "depends", ""); |
|
228 |
|
229 // If the last store was before the current loop, we assume this load |
|
230 // is loop invariant. If a later instruction writes to the same location, |
|
231 // we will fix this at the end of the loop. |
|
232 if (loop_ && lastStore->id() < loop_->firstInstruction()->id()) { |
|
233 if (!loop_->addInvariantLoad(*def)) |
|
234 return false; |
|
235 } |
|
236 } |
|
237 } |
|
238 |
|
239 // Renumber the last instruction, as the analysis depends on this and the order. |
|
240 block->lastIns()->setId(newId++); |
|
241 |
|
242 if (block->isLoopBackedge()) { |
|
243 JS_ASSERT(loop_->loopHeader() == block->loopHeaderOfBackedge()); |
|
244 IonSpew(IonSpew_Alias, "Processing loop backedge %d (header %d)", block->id(), |
|
245 loop_->loopHeader()->id()); |
|
246 LoopAliasInfo *outerLoop = loop_->outer(); |
|
247 MInstruction *firstLoopIns = *loop_->loopHeader()->begin(); |
|
248 |
|
249 const MDefinitionVector &invariant = loop_->invariantLoads(); |
|
250 |
|
251 for (unsigned i = 0; i < invariant.length(); i++) { |
|
252 MDefinition *ins = invariant[i]; |
|
253 AliasSet set = ins->getAliasSet(); |
|
254 JS_ASSERT(set.isLoad()); |
|
255 |
|
256 bool hasAlias = false; |
|
257 for (AliasSetIterator iter(set); iter; iter++) { |
|
258 MDefinitionVector &aliasedStores = stores[*iter]; |
|
259 for (int i = aliasedStores.length() - 1;; i--) { |
|
260 MDefinition *store = aliasedStores[i]; |
|
261 if (store->id() < firstLoopIns->id()) |
|
262 break; |
|
263 if (ins->mightAlias(store)) { |
|
264 hasAlias = true; |
|
265 IonSpewDependency(ins, store, "aliases", "store in loop body"); |
|
266 break; |
|
267 } |
|
268 } |
|
269 if (hasAlias) |
|
270 break; |
|
271 } |
|
272 |
|
273 if (hasAlias) { |
|
274 // This instruction depends on stores inside the loop body. Mark it as having a |
|
275 // dependency on the last instruction of the loop header. The last instruction is a |
|
276 // control instruction and these are never hoisted. |
|
277 MControlInstruction *controlIns = loop_->loopHeader()->lastIns(); |
|
278 IonSpewDependency(ins, controlIns, "depends", "due to stores in loop body"); |
|
279 ins->setDependency(controlIns); |
|
280 } else { |
|
281 IonSpewAliasInfo("Load", ins, "does not depend on any stores in this loop"); |
|
282 |
|
283 if (outerLoop && ins->dependency()->id() < outerLoop->firstInstruction()->id()) { |
|
284 IonSpewAliasInfo("Load", ins, "may be invariant in outer loop"); |
|
285 if (!outerLoop->addInvariantLoad(ins)) |
|
286 return false; |
|
287 } |
|
288 } |
|
289 } |
|
290 loop_ = loop_->outer(); |
|
291 } |
|
292 } |
|
293 |
|
294 JS_ASSERT(loop_ == nullptr); |
|
295 return true; |
|
296 } |