Bug 1874684 - Part 31: Correctly reject invalid durations in some RoundDuration calls...
[gecko.git] / js / src / jit / AliasAnalysis.cpp
blob00b7829e11fbaa222f69eb329132a89990827cdb
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: set ts=8 sts=2 et sw=2 tw=80:
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/. */
7 #include "jit/AliasAnalysis.h"
9 #include "mozilla/DebugOnly.h"
11 #include "jit/JitSpewer.h"
12 #include "jit/MIR.h"
13 #include "jit/MIRGenerator.h"
14 #include "jit/MIRGraph.h"
16 #include "js/Printer.h"
18 using namespace js;
19 using namespace js::jit;
21 namespace js {
22 namespace jit {
24 class LoopAliasInfo : public TempObject {
25 private:
26 LoopAliasInfo* outer_;
27 MBasicBlock* loopHeader_;
28 MInstructionVector invariantLoads_;
30 public:
31 LoopAliasInfo(TempAllocator& alloc, LoopAliasInfo* outer,
32 MBasicBlock* loopHeader)
33 : outer_(outer), loopHeader_(loopHeader), invariantLoads_(alloc) {}
35 MBasicBlock* loopHeader() const { return loopHeader_; }
36 LoopAliasInfo* outer() const { return outer_; }
37 bool addInvariantLoad(MInstruction* ins) {
38 return invariantLoads_.append(ins);
40 const MInstructionVector& invariantLoads() const { return invariantLoads_; }
41 MInstruction* firstInstruction() const { return *loopHeader_->begin(); }
44 } // namespace jit
45 } // namespace js
47 void AliasAnalysis::spewDependencyList() {
48 #ifdef JS_JITSPEW
49 if (JitSpewEnabled(JitSpew_AliasSummaries)) {
50 Fprinter& print = JitSpewPrinter();
51 JitSpewHeader(JitSpew_AliasSummaries);
52 print.printf("Dependency list for other passes:\n");
54 for (ReversePostorderIterator block(graph_.rpoBegin());
55 block != graph_.rpoEnd(); block++) {
56 for (MInstructionIterator def(block->begin()),
57 end(block->begin(block->lastIns()));
58 def != end; ++def) {
59 if (!def->dependency()) {
60 continue;
62 if (!def->getAliasSet().isLoad()) {
63 continue;
66 JitSpewHeader(JitSpew_AliasSummaries);
67 print.printf(" ");
68 MDefinition::PrintOpcodeName(print, def->op());
69 print.printf("%u marked depending on ", def->id());
70 MDefinition::PrintOpcodeName(print, def->dependency()->op());
71 print.printf("%u\n", def->dependency()->id());
75 #endif
78 // Whether there might be a path from src to dest, excluding loop backedges.
79 // This is approximate and really ought to depend on precomputed reachability
80 // information.
81 static inline bool BlockMightReach(MBasicBlock* src, MBasicBlock* dest) {
82 while (src->id() <= dest->id()) {
83 if (src == dest) {
84 return true;
86 switch (src->numSuccessors()) {
87 case 0:
88 return false;
89 case 1: {
90 MBasicBlock* successor = src->getSuccessor(0);
91 if (successor->id() <= src->id()) {
92 return true; // Don't iloop.
94 src = successor;
95 break;
97 default:
98 return true;
101 return false;
104 static void IonSpewDependency(MInstruction* load, MInstruction* store,
105 const char* verb, const char* reason) {
106 #ifdef JS_JITSPEW
107 if (!JitSpewEnabled(JitSpew_Alias)) {
108 return;
111 JitSpewHeader(JitSpew_Alias);
112 Fprinter& out = JitSpewPrinter();
113 out.printf(" Load ");
114 load->printName(out);
115 out.printf(" %s on store ", verb);
116 store->printName(out);
117 out.printf(" (%s)\n", reason);
118 #endif
121 static void IonSpewAliasInfo(const char* pre, MInstruction* ins,
122 const char* post) {
123 #ifdef JS_JITSPEW
124 if (!JitSpewEnabled(JitSpew_Alias)) {
125 return;
128 JitSpewHeader(JitSpew_Alias);
129 Fprinter& out = JitSpewPrinter();
130 out.printf(" %s ", pre);
131 ins->printName(out);
132 out.printf(" %s\n", post);
133 #endif
136 // [SMDOC] IonMonkey Alias Analysis
138 // This pass annotates every load instruction with the last store instruction
139 // on which it depends. The algorithm is optimistic in that it ignores explicit
140 // dependencies and only considers loads and stores.
142 // Loads inside loops only have an implicit dependency on a store before the
143 // loop header if no instruction inside the loop body aliases it. To calculate
144 // this efficiently, we maintain a list of maybe-invariant loads and the
145 // combined alias set for all stores inside the loop. When we see the loop's
146 // backedge, this information is used to mark every load we wrongly assumed to
147 // be loop invariant as having an implicit dependency on the last instruction of
148 // the loop header, so that it's never moved before the loop header.
150 // The algorithm depends on the invariant that both control instructions and
151 // effectful instructions (stores) are never hoisted.
152 bool AliasAnalysis::analyze() {
153 JitSpew(JitSpew_Alias, "Begin");
154 Vector<MInstructionVector, AliasSet::NumCategories, JitAllocPolicy> stores(
155 alloc());
157 // Initialize to the first instruction.
158 MInstruction* firstIns = *graph_.entryBlock()->begin();
159 for (unsigned i = 0; i < AliasSet::NumCategories; i++) {
160 MInstructionVector defs(alloc());
161 if (!defs.append(firstIns)) {
162 return false;
164 if (!stores.append(std::move(defs))) {
165 return false;
169 // Type analysis may have inserted new instructions. Since this pass depends
170 // on the instruction number ordering, all instructions are renumbered.
171 uint32_t newId = 0;
173 for (ReversePostorderIterator block(graph_.rpoBegin());
174 block != graph_.rpoEnd(); block++) {
175 if (mir->shouldCancel("Alias Analysis (main loop)")) {
176 return false;
179 if (block->isLoopHeader()) {
180 JitSpew(JitSpew_Alias, "Processing loop header %u", block->id());
181 loop_ = new (alloc().fallible()) LoopAliasInfo(alloc(), loop_, *block);
182 if (!loop_) {
183 return false;
187 for (MPhiIterator def(block->phisBegin()), end(block->phisEnd());
188 def != end; ++def) {
189 def->setId(newId++);
193 // "The block has one or more instructions"
194 MOZ_ASSERT(block->hasAnyIns());
195 // "The last instruction is a control instruction"
196 MOZ_ASSERT(block->hasLastIns());
197 // "The only control instructions that can have a non-empty alias set
198 // are MWasmCallCatchable and MWasmReturnCall".
199 // Note, this isn't a requirement that is intrinsic to MIR. In
200 // principle, any control instruction can have a non-empty alias set,
201 // and that should be correctly handled by this routine. The assertion
202 // merely reflects the current state of usage of MIR, in which
203 // MWasmCallCatchable and MWasmReturnCall are the only control
204 // instructions we generate that have non-empty alias sets.
205 // See bug 1837686.
206 mozilla::DebugOnly<MControlInstruction*> lastIns = block->lastIns();
207 MOZ_ASSERT_IF(
208 !lastIns->isWasmCallCatchable() && !lastIns->isWasmReturnCall(),
209 lastIns->getAliasSet().isNone());
212 for (MInstructionIterator def(block->begin()), end(block->end());
213 def != end; ++def) {
214 def->setId(newId++);
216 AliasSet set = def->getAliasSet();
217 if (set.isNone()) {
218 continue;
221 // For the purposes of alias analysis, all recoverable operations
222 // are treated as effect free as the memory represented by these
223 // operations cannot be aliased by others.
224 if (def->canRecoverOnBailout()) {
225 continue;
228 if (set.isStore()) {
229 for (AliasSetIterator iter(set); iter; iter++) {
230 if (!stores[*iter].append(*def)) {
231 return false;
235 #ifdef JS_JITSPEW
236 if (JitSpewEnabled(JitSpew_Alias)) {
237 JitSpewHeader(JitSpew_Alias);
238 Fprinter& out = JitSpewPrinter();
239 out.printf("Processing store ");
240 def->printName(out);
241 out.printf(" (flags %x)\n", set.flags());
243 #endif
244 } else {
245 // Find the most recent store on which this instruction depends.
246 MInstruction* lastStore = firstIns;
248 for (AliasSetIterator iter(set); iter; iter++) {
249 MInstructionVector& aliasedStores = stores[*iter];
250 for (int i = aliasedStores.length() - 1; i >= 0; i--) {
251 MInstruction* store = aliasedStores[i];
252 if (def->mightAlias(store) != MDefinition::AliasType::NoAlias &&
253 BlockMightReach(store->block(), *block)) {
254 if (lastStore->id() < store->id()) {
255 lastStore = store;
257 break;
262 def->setDependency(lastStore);
263 IonSpewDependency(*def, lastStore, "depends", "");
265 // If the last store was before the current loop, we assume this load
266 // is loop invariant. If a later instruction writes to the same
267 // location, we will fix this at the end of the loop.
268 if (loop_ && lastStore->id() < loop_->firstInstruction()->id()) {
269 if (!loop_->addInvariantLoad(*def)) {
270 return false;
276 if (block->isLoopBackedge()) {
277 MOZ_ASSERT(loop_->loopHeader() == block->loopHeaderOfBackedge());
278 JitSpew(JitSpew_Alias, "Processing loop backedge %u (header %u)",
279 block->id(), loop_->loopHeader()->id());
280 LoopAliasInfo* outerLoop = loop_->outer();
281 MInstruction* firstLoopIns = *loop_->loopHeader()->begin();
283 const MInstructionVector& invariant = loop_->invariantLoads();
285 for (unsigned i = 0; i < invariant.length(); i++) {
286 MInstruction* ins = invariant[i];
287 AliasSet set = ins->getAliasSet();
288 MOZ_ASSERT(set.isLoad());
290 bool hasAlias = false;
291 for (AliasSetIterator iter(set); iter; iter++) {
292 MInstructionVector& aliasedStores = stores[*iter];
293 for (int i = aliasedStores.length() - 1;; i--) {
294 MInstruction* store = aliasedStores[i];
295 if (store->id() < firstLoopIns->id()) {
296 break;
298 if (ins->mightAlias(store) != MDefinition::AliasType::NoAlias) {
299 hasAlias = true;
300 IonSpewDependency(ins, store, "aliases", "store in loop body");
301 break;
304 if (hasAlias) {
305 break;
309 if (hasAlias) {
310 // This instruction depends on stores inside the loop body. Mark it as
311 // having a dependency on the last instruction of the loop header. The
312 // last instruction is a control instruction and these are never
313 // hoisted.
314 MControlInstruction* controlIns = loop_->loopHeader()->lastIns();
315 IonSpewDependency(ins, controlIns, "depends",
316 "due to stores in loop body");
317 ins->setDependency(controlIns);
318 } else {
319 IonSpewAliasInfo("Load", ins,
320 "does not depend on any stores in this loop");
322 if (outerLoop &&
323 ins->dependency()->id() < outerLoop->firstInstruction()->id()) {
324 IonSpewAliasInfo("Load", ins, "may be invariant in outer loop");
325 if (!outerLoop->addInvariantLoad(ins)) {
326 return false;
331 loop_ = loop_->outer();
335 spewDependencyList();
337 MOZ_ASSERT(loop_ == nullptr);
338 return true;