js/src/jit/LICM.cpp

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

michael@0 1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
michael@0 2 * vim: set ts=8 sts=4 et sw=4 tw=99:
michael@0 3 * This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 #include "jit/LICM.h"
michael@0 8
michael@0 9 #include <stdio.h>
michael@0 10
michael@0 11 #include "jit/IonSpewer.h"
michael@0 12 #include "jit/MIR.h"
michael@0 13 #include "jit/MIRGenerator.h"
michael@0 14 #include "jit/MIRGraph.h"
michael@0 15
michael@0 16 using namespace js;
michael@0 17 using namespace js::jit;
michael@0 18
michael@0 19 namespace {
michael@0 20
michael@0 21 typedef Vector<MInstruction*, 1, IonAllocPolicy> InstructionQueue;
michael@0 22
michael@0 23 class Loop
michael@0 24 {
michael@0 25 MIRGenerator *mir;
michael@0 26
michael@0 27 public:
michael@0 28 // Loop code may return three values:
michael@0 29 enum LoopReturn {
michael@0 30 LoopReturn_Success,
michael@0 31 LoopReturn_Error, // Compilation failure.
michael@0 32 LoopReturn_Skip // The loop is not suitable for LICM, but there is no error.
michael@0 33 };
michael@0 34
michael@0 35 public:
michael@0 36 // A loop is constructed on a backedge found in the control flow graph.
michael@0 37 Loop(MIRGenerator *mir, MBasicBlock *footer);
michael@0 38
michael@0 39 // Initializes the loop, finds all blocks and instructions contained in the loop.
michael@0 40 LoopReturn init();
michael@0 41
michael@0 42 // Identifies hoistable loop invariant instructions and moves them out of the loop.
michael@0 43 bool optimize();
michael@0 44
michael@0 45 private:
michael@0 46 // These blocks define the loop. header_ points to the loop header
michael@0 47 MBasicBlock *header_;
michael@0 48
michael@0 49 // The pre-loop block is the first predecessor of the loop header. It is where
michael@0 50 // the loop is first entered and where hoisted instructions will be placed.
michael@0 51 MBasicBlock* preLoop_;
michael@0 52
michael@0 53 // This indicates whether the loop contains calls or other things which
michael@0 54 // clobber most or all floating-point registers. In such loops,
michael@0 55 // floating-point constants should not be hoisted unless it enables further
michael@0 56 // hoisting.
michael@0 57 bool containsPossibleCall_;
michael@0 58
michael@0 59 TempAllocator &alloc() const {
michael@0 60 return mir->alloc();
michael@0 61 }
michael@0 62
michael@0 63 bool hoistInstructions(InstructionQueue &toHoist);
michael@0 64
michael@0 65 // Utility methods for invariance testing and instruction hoisting.
michael@0 66 bool isInLoop(MDefinition *ins);
michael@0 67 bool isBeforeLoop(MDefinition *ins);
michael@0 68 bool isLoopInvariant(MInstruction *ins);
michael@0 69
michael@0 70 // This method determines if this block hot within a loop. That is, if it's
michael@0 71 // always or usually run when the loop executes
michael@0 72 bool checkHotness(MBasicBlock *block);
michael@0 73
michael@0 74 // Worklist and worklist usage methods
michael@0 75 InstructionQueue worklist_;
michael@0 76 bool insertInWorklist(MInstruction *ins);
michael@0 77 MInstruction* popFromWorklist();
michael@0 78
michael@0 79 inline bool isHoistable(const MDefinition *ins) const {
michael@0 80 return ins->isMovable() && !ins->isEffectful() && !ins->neverHoist();
michael@0 81 }
michael@0 82
michael@0 83 bool requiresHoistedUse(const MDefinition *ins) const;
michael@0 84 };
michael@0 85
michael@0 86 } /* namespace anonymous */
michael@0 87
michael@0 88 LICM::LICM(MIRGenerator *mir, MIRGraph &graph)
michael@0 89 : mir(mir), graph(graph)
michael@0 90 {
michael@0 91 }
michael@0 92
michael@0 93 bool
michael@0 94 LICM::analyze()
michael@0 95 {
michael@0 96 IonSpew(IonSpew_LICM, "Beginning LICM pass.");
michael@0 97
michael@0 98 // Iterate in RPO to visit outer loops before inner loops.
michael@0 99 for (ReversePostorderIterator i(graph.rpoBegin()); i != graph.rpoEnd(); i++) {
michael@0 100 MBasicBlock *header = *i;
michael@0 101
michael@0 102 // Skip non-headers and self-loops.
michael@0 103 if (!header->isLoopHeader() || header->numPredecessors() < 2)
michael@0 104 continue;
michael@0 105
michael@0 106 // Attempt to optimize loop.
michael@0 107 Loop loop(mir, header);
michael@0 108
michael@0 109 Loop::LoopReturn lr = loop.init();
michael@0 110 if (lr == Loop::LoopReturn_Error)
michael@0 111 return false;
michael@0 112 if (lr == Loop::LoopReturn_Skip) {
michael@0 113 graph.unmarkBlocks();
michael@0 114 continue;
michael@0 115 }
michael@0 116
michael@0 117 if (!loop.optimize())
michael@0 118 return false;
michael@0 119
michael@0 120 graph.unmarkBlocks();
michael@0 121 }
michael@0 122
michael@0 123 return true;
michael@0 124 }
michael@0 125
michael@0 126 Loop::Loop(MIRGenerator *mir, MBasicBlock *header)
michael@0 127 : mir(mir),
michael@0 128 header_(header),
michael@0 129 containsPossibleCall_(false),
michael@0 130 worklist_(mir->alloc())
michael@0 131 {
michael@0 132 preLoop_ = header_->getPredecessor(0);
michael@0 133 }
michael@0 134
michael@0 135 Loop::LoopReturn
michael@0 136 Loop::init()
michael@0 137 {
michael@0 138 IonSpew(IonSpew_LICM, "Loop identified, headed by block %d", header_->id());
michael@0 139 IonSpew(IonSpew_LICM, "footer is block %d", header_->backedge()->id());
michael@0 140
michael@0 141 // The first predecessor of the loop header must dominate the header.
michael@0 142 JS_ASSERT(header_->id() > header_->getPredecessor(0)->id());
michael@0 143
michael@0 144 // Loops from backedge to header and marks all visited blocks
michael@0 145 // as part of the loop. At the same time add all hoistable instructions
michael@0 146 // (in RPO order) to the instruction worklist.
michael@0 147 Vector<MBasicBlock *, 1, IonAllocPolicy> inlooplist(alloc());
michael@0 148 if (!inlooplist.append(header_->backedge()))
michael@0 149 return LoopReturn_Error;
michael@0 150 header_->backedge()->mark();
michael@0 151
michael@0 152 while (!inlooplist.empty()) {
michael@0 153 MBasicBlock *block = inlooplist.back();
michael@0 154
michael@0 155 // Hoisting requires more finesse if the loop contains a block that
michael@0 156 // self-dominates: there exists control flow that may enter the loop
michael@0 157 // without passing through the loop preheader.
michael@0 158 //
michael@0 159 // Rather than perform a complicated analysis of the dominance graph,
michael@0 160 // just return a soft error to ignore this loop.
michael@0 161 if (block->immediateDominator() == block) {
michael@0 162 while (!worklist_.empty())
michael@0 163 popFromWorklist();
michael@0 164 return LoopReturn_Skip;
michael@0 165 }
michael@0 166
michael@0 167 // Add not yet visited predecessors to the inlooplist.
michael@0 168 if (block != header_) {
michael@0 169 for (size_t i = 0; i < block->numPredecessors(); i++) {
michael@0 170 MBasicBlock *pred = block->getPredecessor(i);
michael@0 171 if (pred->isMarked())
michael@0 172 continue;
michael@0 173
michael@0 174 if (!inlooplist.append(pred))
michael@0 175 return LoopReturn_Error;
michael@0 176 pred->mark();
michael@0 177 }
michael@0 178 }
michael@0 179
michael@0 180 // If any block was added, process them first.
michael@0 181 if (block != inlooplist.back())
michael@0 182 continue;
michael@0 183
michael@0 184 // Add all instructions in this block (but the control instruction) to the worklist
michael@0 185 for (MInstructionIterator i = block->begin(); i != block->end(); i++) {
michael@0 186 MInstruction *ins = *i;
michael@0 187
michael@0 188 // Remember whether this loop contains anything which clobbers most
michael@0 189 // or all floating-point registers. This is just a rough heuristic.
michael@0 190 if (ins->possiblyCalls())
michael@0 191 containsPossibleCall_ = true;
michael@0 192
michael@0 193 if (isHoistable(ins)) {
michael@0 194 if (!insertInWorklist(ins))
michael@0 195 return LoopReturn_Error;
michael@0 196 }
michael@0 197 }
michael@0 198
michael@0 199 // All successors of this block are visited.
michael@0 200 inlooplist.popBack();
michael@0 201 }
michael@0 202
michael@0 203 return LoopReturn_Success;
michael@0 204 }
michael@0 205
michael@0 206 bool
michael@0 207 Loop::optimize()
michael@0 208 {
michael@0 209 InstructionQueue invariantInstructions(alloc());
michael@0 210
michael@0 211 IonSpew(IonSpew_LICM, "These instructions are in the loop: ");
michael@0 212
michael@0 213 while (!worklist_.empty()) {
michael@0 214 if (mir->shouldCancel("LICM (worklist)"))
michael@0 215 return false;
michael@0 216
michael@0 217 MInstruction *ins = popFromWorklist();
michael@0 218
michael@0 219 IonSpewHeader(IonSpew_LICM);
michael@0 220
michael@0 221 if (IonSpewEnabled(IonSpew_LICM)) {
michael@0 222 ins->printName(IonSpewFile);
michael@0 223 fprintf(IonSpewFile, " <- ");
michael@0 224 ins->printOpcode(IonSpewFile);
michael@0 225 fprintf(IonSpewFile, ": ");
michael@0 226 }
michael@0 227
michael@0 228 if (isLoopInvariant(ins)) {
michael@0 229 // Flag this instruction as loop invariant.
michael@0 230 ins->setLoopInvariant();
michael@0 231 if (!invariantInstructions.append(ins))
michael@0 232 return false;
michael@0 233
michael@0 234 if (IonSpewEnabled(IonSpew_LICM))
michael@0 235 fprintf(IonSpewFile, " Loop Invariant!\n");
michael@0 236 }
michael@0 237 }
michael@0 238
michael@0 239 if (!hoistInstructions(invariantInstructions))
michael@0 240 return false;
michael@0 241 return true;
michael@0 242 }
michael@0 243
michael@0 244 bool
michael@0 245 Loop::requiresHoistedUse(const MDefinition *ins) const
michael@0 246 {
michael@0 247 if (ins->isConstantElements() || ins->isBox())
michael@0 248 return true;
michael@0 249
michael@0 250 // Integer constants can often be folded as immediates and aren't worth
michael@0 251 // hoisting on their own, in general. Floating-point constants typically
michael@0 252 // are worth hoisting, unless they'll end up being spilled (eg. due to a
michael@0 253 // call).
michael@0 254 if (ins->isConstant() && (IsFloatingPointType(ins->type()) || containsPossibleCall_))
michael@0 255 return true;
michael@0 256
michael@0 257 return false;
michael@0 258 }
michael@0 259
michael@0 260 bool
michael@0 261 Loop::hoistInstructions(InstructionQueue &toHoist)
michael@0 262 {
michael@0 263 // Iterate in post-order (uses before definitions)
michael@0 264 for (int32_t i = toHoist.length() - 1; i >= 0; i--) {
michael@0 265 MInstruction *ins = toHoist[i];
michael@0 266
michael@0 267 // Don't hoist a cheap constant if it doesn't enable us to hoist one of
michael@0 268 // its uses. We want those instructions as close as possible to their
michael@0 269 // use, to facilitate folding and minimize register pressure.
michael@0 270 if (requiresHoistedUse(ins)) {
michael@0 271 bool loopInvariantUse = false;
michael@0 272 for (MUseDefIterator use(ins); use; use++) {
michael@0 273 if (use.def()->isLoopInvariant()) {
michael@0 274 loopInvariantUse = true;
michael@0 275 break;
michael@0 276 }
michael@0 277 }
michael@0 278
michael@0 279 if (!loopInvariantUse)
michael@0 280 ins->setNotLoopInvariant();
michael@0 281 }
michael@0 282 }
michael@0 283
michael@0 284 // Move all instructions to the preLoop_ block just before the control instruction.
michael@0 285 for (size_t i = 0; i < toHoist.length(); i++) {
michael@0 286 MInstruction *ins = toHoist[i];
michael@0 287
michael@0 288 // Loads may have an implicit dependency on either stores (effectful instructions) or
michael@0 289 // control instructions so we should never move these.
michael@0 290 JS_ASSERT(!ins->isControlInstruction());
michael@0 291 JS_ASSERT(!ins->isEffectful());
michael@0 292 JS_ASSERT(ins->isMovable());
michael@0 293
michael@0 294 if (!ins->isLoopInvariant())
michael@0 295 continue;
michael@0 296
michael@0 297 if (checkHotness(ins->block())) {
michael@0 298 ins->block()->moveBefore(preLoop_->lastIns(), ins);
michael@0 299 ins->setNotLoopInvariant();
michael@0 300 }
michael@0 301 }
michael@0 302
michael@0 303 return true;
michael@0 304 }
michael@0 305
michael@0 306 bool
michael@0 307 Loop::isInLoop(MDefinition *ins)
michael@0 308 {
michael@0 309 return ins->block()->isMarked();
michael@0 310 }
michael@0 311
michael@0 312 bool
michael@0 313 Loop::isBeforeLoop(MDefinition *ins)
michael@0 314 {
michael@0 315 return ins->block()->id() < header_->id();
michael@0 316 }
michael@0 317
michael@0 318 bool
michael@0 319 Loop::isLoopInvariant(MInstruction *ins)
michael@0 320 {
michael@0 321 if (!isHoistable(ins)) {
michael@0 322 if (IonSpewEnabled(IonSpew_LICM))
michael@0 323 fprintf(IonSpewFile, "not hoistable\n");
michael@0 324 return false;
michael@0 325 }
michael@0 326
michael@0 327 // Don't hoist if this instruction depends on a store inside or after the loop.
michael@0 328 // Note: "after the loop" can sound strange, but Alias Analysis doesn't look
michael@0 329 // at the control flow. Therefore it doesn't match the definition here, that a block
michael@0 330 // is in the loop when there is a (directed) path from the block to the loop header.
michael@0 331 if (ins->dependency() && !isBeforeLoop(ins->dependency())) {
michael@0 332 if (IonSpewEnabled(IonSpew_LICM)) {
michael@0 333 fprintf(IonSpewFile, "depends on store inside or after loop: ");
michael@0 334 ins->dependency()->printName(IonSpewFile);
michael@0 335 fprintf(IonSpewFile, "\n");
michael@0 336 }
michael@0 337 return false;
michael@0 338 }
michael@0 339
michael@0 340 // An instruction is only loop invariant if it and all of its operands can
michael@0 341 // be safely hoisted into the loop preheader.
michael@0 342 for (size_t i = 0, e = ins->numOperands(); i < e; i++) {
michael@0 343 if (isInLoop(ins->getOperand(i)) &&
michael@0 344 !ins->getOperand(i)->isLoopInvariant()) {
michael@0 345
michael@0 346 if (IonSpewEnabled(IonSpew_LICM)) {
michael@0 347 ins->getOperand(i)->printName(IonSpewFile);
michael@0 348 fprintf(IonSpewFile, " is in the loop.\n");
michael@0 349 }
michael@0 350
michael@0 351 return false;
michael@0 352 }
michael@0 353 }
michael@0 354
michael@0 355 return true;
michael@0 356 }
michael@0 357
michael@0 358 bool
michael@0 359 Loop::checkHotness(MBasicBlock *block)
michael@0 360 {
michael@0 361 // TODO: determine if instructions within this block are worth hoisting.
michael@0 362 // They might not be if the block is cold enough within the loop.
michael@0 363 // BUG 669517
michael@0 364 return true;
michael@0 365 }
michael@0 366
michael@0 367 bool
michael@0 368 Loop::insertInWorklist(MInstruction *ins)
michael@0 369 {
michael@0 370 if (!worklist_.insert(worklist_.begin(), ins))
michael@0 371 return false;
michael@0 372 ins->setInWorklist();
michael@0 373 return true;
michael@0 374 }
michael@0 375
michael@0 376 MInstruction*
michael@0 377 Loop::popFromWorklist()
michael@0 378 {
michael@0 379 MInstruction* toReturn = worklist_.popCopy();
michael@0 380 toReturn->setNotInWorklist();
michael@0 381 return toReturn;
michael@0 382 }

mercurial