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"
13 #include "jit/MIRGenerator.h"
14 #include "jit/MIRGraph.h"
16 #include "js/Printer.h"
19 using namespace js::jit
;
24 class LoopAliasInfo
: public TempObject
{
26 LoopAliasInfo
* outer_
;
27 MBasicBlock
* loopHeader_
;
28 MInstructionVector invariantLoads_
;
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(); }
47 void AliasAnalysis::spewDependencyList() {
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()));
59 if (!def
->dependency()) {
62 if (!def
->getAliasSet().isLoad()) {
66 JitSpewHeader(JitSpew_AliasSummaries
);
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());
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
81 static inline bool BlockMightReach(MBasicBlock
* src
, MBasicBlock
* dest
) {
82 while (src
->id() <= dest
->id()) {
86 switch (src
->numSuccessors()) {
90 MBasicBlock
* successor
= src
->getSuccessor(0);
91 if (successor
->id() <= src
->id()) {
92 return true; // Don't iloop.
104 static void IonSpewDependency(MInstruction
* load
, MInstruction
* store
,
105 const char* verb
, const char* reason
) {
107 if (!JitSpewEnabled(JitSpew_Alias
)) {
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
);
121 static void IonSpewAliasInfo(const char* pre
, MInstruction
* ins
,
124 if (!JitSpewEnabled(JitSpew_Alias
)) {
128 JitSpewHeader(JitSpew_Alias
);
129 Fprinter
& out
= JitSpewPrinter();
130 out
.printf(" %s ", pre
);
132 out
.printf(" %s\n", post
);
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(
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
)) {
164 if (!stores
.append(std::move(defs
))) {
169 // Type analysis may have inserted new instructions. Since this pass depends
170 // on the instruction number ordering, all instructions are renumbered.
173 for (ReversePostorderIterator
block(graph_
.rpoBegin());
174 block
!= graph_
.rpoEnd(); block
++) {
175 if (mir
->shouldCancel("Alias Analysis (main loop)")) {
179 if (block
->isLoopHeader()) {
180 JitSpew(JitSpew_Alias
, "Processing loop header %u", block
->id());
181 loop_
= new (alloc().fallible()) LoopAliasInfo(alloc(), loop_
, *block
);
187 for (MPhiIterator
def(block
->phisBegin()), end(block
->phisEnd());
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.
206 mozilla::DebugOnly
<MControlInstruction
*> lastIns
= block
->lastIns();
208 !lastIns
->isWasmCallCatchable() && !lastIns
->isWasmReturnCall(),
209 lastIns
->getAliasSet().isNone());
212 for (MInstructionIterator
def(block
->begin()), end(block
->end());
216 AliasSet set
= def
->getAliasSet();
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()) {
229 for (AliasSetIterator
iter(set
); iter
; iter
++) {
230 if (!stores
[*iter
].append(*def
)) {
236 if (JitSpewEnabled(JitSpew_Alias
)) {
237 JitSpewHeader(JitSpew_Alias
);
238 Fprinter
& out
= JitSpewPrinter();
239 out
.printf("Processing store ");
241 out
.printf(" (flags %x)\n", set
.flags());
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()) {
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
)) {
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()) {
298 if (ins
->mightAlias(store
) != MDefinition::AliasType::NoAlias
) {
300 IonSpewDependency(ins
, store
, "aliases", "store in loop body");
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
314 MControlInstruction
* controlIns
= loop_
->loopHeader()->lastIns();
315 IonSpewDependency(ins
, controlIns
, "depends",
316 "due to stores in loop body");
317 ins
->setDependency(controlIns
);
319 IonSpewAliasInfo("Load", ins
,
320 "does not depend on any stores in this loop");
323 ins
->dependency()->id() < outerLoop
->firstInstruction()->id()) {
324 IonSpewAliasInfo("Load", ins
, "may be invariant in outer loop");
325 if (!outerLoop
->addInvariantLoad(ins
)) {
331 loop_
= loop_
->outer();
335 spewDependencyList();
337 MOZ_ASSERT(loop_
== nullptr);