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 "jit/JitSpewer.h"
11 #include "jit/MIRGenerator.h"
12 #include "jit/MIRGraph.h"
14 #include "js/Printer.h"
17 using namespace js::jit
;
22 class LoopAliasInfo
: public TempObject
{
24 LoopAliasInfo
* outer_
;
25 MBasicBlock
* loopHeader_
;
26 MInstructionVector invariantLoads_
;
29 LoopAliasInfo(TempAllocator
& alloc
, LoopAliasInfo
* outer
,
30 MBasicBlock
* loopHeader
)
31 : outer_(outer
), loopHeader_(loopHeader
), invariantLoads_(alloc
) {}
33 MBasicBlock
* loopHeader() const { return loopHeader_
; }
34 LoopAliasInfo
* outer() const { return outer_
; }
35 bool addInvariantLoad(MInstruction
* ins
) {
36 return invariantLoads_
.append(ins
);
38 const MInstructionVector
& invariantLoads() const { return invariantLoads_
; }
39 MInstruction
* firstInstruction() const { return *loopHeader_
->begin(); }
45 void AliasAnalysis::spewDependencyList() {
47 if (JitSpewEnabled(JitSpew_AliasSummaries
)) {
48 Fprinter
& print
= JitSpewPrinter();
49 JitSpewHeader(JitSpew_AliasSummaries
);
50 print
.printf("Dependency list for other passes:\n");
52 for (ReversePostorderIterator
block(graph_
.rpoBegin());
53 block
!= graph_
.rpoEnd(); block
++) {
54 for (MInstructionIterator
def(block
->begin()),
55 end(block
->begin(block
->lastIns()));
57 if (!def
->dependency()) {
60 if (!def
->getAliasSet().isLoad()) {
64 JitSpewHeader(JitSpew_AliasSummaries
);
66 MDefinition::PrintOpcodeName(print
, def
->op());
67 print
.printf("%u marked depending on ", def
->id());
68 MDefinition::PrintOpcodeName(print
, def
->dependency()->op());
69 print
.printf("%u\n", def
->dependency()->id());
76 // Whether there might be a path from src to dest, excluding loop backedges.
77 // This is approximate and really ought to depend on precomputed reachability
79 static inline bool BlockMightReach(MBasicBlock
* src
, MBasicBlock
* dest
) {
80 while (src
->id() <= dest
->id()) {
84 switch (src
->numSuccessors()) {
88 MBasicBlock
* successor
= src
->getSuccessor(0);
89 if (successor
->id() <= src
->id()) {
90 return true; // Don't iloop.
102 static void IonSpewDependency(MInstruction
* load
, MInstruction
* store
,
103 const char* verb
, const char* reason
) {
105 if (!JitSpewEnabled(JitSpew_Alias
)) {
109 JitSpewHeader(JitSpew_Alias
);
110 Fprinter
& out
= JitSpewPrinter();
111 out
.printf(" Load ");
112 load
->printName(out
);
113 out
.printf(" %s on store ", verb
);
114 store
->printName(out
);
115 out
.printf(" (%s)\n", reason
);
119 static void IonSpewAliasInfo(const char* pre
, MInstruction
* ins
,
122 if (!JitSpewEnabled(JitSpew_Alias
)) {
126 JitSpewHeader(JitSpew_Alias
);
127 Fprinter
& out
= JitSpewPrinter();
128 out
.printf(" %s ", pre
);
130 out
.printf(" %s\n", post
);
134 // [SMDOC] IonMonkey Alias Analysis
136 // This pass annotates every load instruction with the last store instruction
137 // on which it depends. The algorithm is optimistic in that it ignores explicit
138 // dependencies and only considers loads and stores.
140 // Loads inside loops only have an implicit dependency on a store before the
141 // loop header if no instruction inside the loop body aliases it. To calculate
142 // this efficiently, we maintain a list of maybe-invariant loads and the
143 // combined alias set for all stores inside the loop. When we see the loop's
144 // backedge, this information is used to mark every load we wrongly assumed to
145 // be loop invariant as having an implicit dependency on the last instruction of
146 // the loop header, so that it's never moved before the loop header.
148 // The algorithm depends on the invariant that both control instructions and
149 // effectful instructions (stores) are never hoisted.
150 bool AliasAnalysis::analyze() {
151 JitSpew(JitSpew_Alias
, "Begin");
152 Vector
<MInstructionVector
, AliasSet::NumCategories
, JitAllocPolicy
> stores(
155 // Initialize to the first instruction.
156 MInstruction
* firstIns
= *graph_
.entryBlock()->begin();
157 for (unsigned i
= 0; i
< AliasSet::NumCategories
; i
++) {
158 MInstructionVector
defs(alloc());
159 if (!defs
.append(firstIns
)) {
162 if (!stores
.append(std::move(defs
))) {
167 // Type analysis may have inserted new instructions. Since this pass depends
168 // on the instruction number ordering, all instructions are renumbered.
171 for (ReversePostorderIterator
block(graph_
.rpoBegin());
172 block
!= graph_
.rpoEnd(); block
++) {
173 if (mir
->shouldCancel("Alias Analysis (main loop)")) {
177 if (block
->isLoopHeader()) {
178 JitSpew(JitSpew_Alias
, "Processing loop header %u", block
->id());
179 loop_
= new (alloc().fallible()) LoopAliasInfo(alloc(), loop_
, *block
);
185 for (MPhiIterator
def(block
->phisBegin()), end(block
->phisEnd());
190 for (MInstructionIterator
def(block
->begin()), end(block
->end());
194 AliasSet set
= def
->getAliasSet();
199 // For the purposes of alias analysis, all recoverable operations
200 // are treated as effect free as the memory represented by these
201 // operations cannot be aliased by others.
202 if (def
->canRecoverOnBailout()) {
207 for (AliasSetIterator
iter(set
); iter
; iter
++) {
208 if (!stores
[*iter
].append(*def
)) {
214 if (JitSpewEnabled(JitSpew_Alias
)) {
215 JitSpewHeader(JitSpew_Alias
);
216 Fprinter
& out
= JitSpewPrinter();
217 out
.printf("Processing store ");
219 out
.printf(" (flags %x)\n", set
.flags());
223 // Find the most recent store on which this instruction depends.
224 MInstruction
* lastStore
= firstIns
;
226 for (AliasSetIterator
iter(set
); iter
; iter
++) {
227 MInstructionVector
& aliasedStores
= stores
[*iter
];
228 for (int i
= aliasedStores
.length() - 1; i
>= 0; i
--) {
229 MInstruction
* store
= aliasedStores
[i
];
230 if (def
->mightAlias(store
) != MDefinition::AliasType::NoAlias
&&
231 BlockMightReach(store
->block(), *block
)) {
232 if (lastStore
->id() < store
->id()) {
240 def
->setDependency(lastStore
);
241 IonSpewDependency(*def
, lastStore
, "depends", "");
243 // If the last store was before the current loop, we assume this load
244 // is loop invariant. If a later instruction writes to the same
245 // location, we will fix this at the end of the loop.
246 if (loop_
&& lastStore
->id() < loop_
->firstInstruction()->id()) {
247 if (!loop_
->addInvariantLoad(*def
)) {
254 if (block
->isLoopBackedge()) {
255 MOZ_ASSERT(loop_
->loopHeader() == block
->loopHeaderOfBackedge());
256 JitSpew(JitSpew_Alias
, "Processing loop backedge %u (header %u)",
257 block
->id(), loop_
->loopHeader()->id());
258 LoopAliasInfo
* outerLoop
= loop_
->outer();
259 MInstruction
* firstLoopIns
= *loop_
->loopHeader()->begin();
261 const MInstructionVector
& invariant
= loop_
->invariantLoads();
263 for (unsigned i
= 0; i
< invariant
.length(); i
++) {
264 MInstruction
* ins
= invariant
[i
];
265 AliasSet set
= ins
->getAliasSet();
266 MOZ_ASSERT(set
.isLoad());
268 bool hasAlias
= false;
269 for (AliasSetIterator
iter(set
); iter
; iter
++) {
270 MInstructionVector
& aliasedStores
= stores
[*iter
];
271 for (int i
= aliasedStores
.length() - 1;; i
--) {
272 MInstruction
* store
= aliasedStores
[i
];
273 if (store
->id() < firstLoopIns
->id()) {
276 if (ins
->mightAlias(store
) != MDefinition::AliasType::NoAlias
) {
278 IonSpewDependency(ins
, store
, "aliases", "store in loop body");
288 // This instruction depends on stores inside the loop body. Mark it as
289 // having a dependency on the last instruction of the loop header. The
290 // last instruction is a control instruction and these are never
292 MControlInstruction
* controlIns
= loop_
->loopHeader()->lastIns();
293 IonSpewDependency(ins
, controlIns
, "depends",
294 "due to stores in loop body");
295 ins
->setDependency(controlIns
);
297 IonSpewAliasInfo("Load", ins
,
298 "does not depend on any stores in this loop");
301 ins
->dependency()->id() < outerLoop
->firstInstruction()->id()) {
302 IonSpewAliasInfo("Load", ins
, "may be invariant in outer loop");
303 if (!outerLoop
->addInvariantLoad(ins
)) {
309 loop_
= loop_
->outer();
313 spewDependencyList();
315 MOZ_ASSERT(loop_
== nullptr);