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