js/src/jit/LICM.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/js/src/jit/LICM.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,382 @@
     1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
     1.5 + * vim: set ts=8 sts=4 et sw=4 tw=99:
     1.6 + * This Source Code Form is subject to the terms of the Mozilla Public
     1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.9 +
    1.10 +#include "jit/LICM.h"
    1.11 +
    1.12 +#include <stdio.h>
    1.13 +
    1.14 +#include "jit/IonSpewer.h"
    1.15 +#include "jit/MIR.h"
    1.16 +#include "jit/MIRGenerator.h"
    1.17 +#include "jit/MIRGraph.h"
    1.18 +
    1.19 +using namespace js;
    1.20 +using namespace js::jit;
    1.21 +
    1.22 +namespace {
    1.23 +
    1.24 +typedef Vector<MInstruction*, 1, IonAllocPolicy> InstructionQueue;
    1.25 +
    1.26 +class Loop
    1.27 +{
    1.28 +    MIRGenerator *mir;
    1.29 +
    1.30 +  public:
    1.31 +    // Loop code may return three values:
    1.32 +    enum LoopReturn {
    1.33 +        LoopReturn_Success,
    1.34 +        LoopReturn_Error, // Compilation failure.
    1.35 +        LoopReturn_Skip   // The loop is not suitable for LICM, but there is no error.
    1.36 +    };
    1.37 +
    1.38 +  public:
    1.39 +    // A loop is constructed on a backedge found in the control flow graph.
    1.40 +    Loop(MIRGenerator *mir, MBasicBlock *footer);
    1.41 +
    1.42 +    // Initializes the loop, finds all blocks and instructions contained in the loop.
    1.43 +    LoopReturn init();
    1.44 +
    1.45 +    // Identifies hoistable loop invariant instructions and moves them out of the loop.
    1.46 +    bool optimize();
    1.47 +
    1.48 +  private:
    1.49 +    // These blocks define the loop.  header_ points to the loop header
    1.50 +    MBasicBlock *header_;
    1.51 +
    1.52 +    // The pre-loop block is the first predecessor of the loop header.  It is where
    1.53 +    // the loop is first entered and where hoisted instructions will be placed.
    1.54 +    MBasicBlock* preLoop_;
    1.55 +
    1.56 +    // This indicates whether the loop contains calls or other things which
    1.57 +    // clobber most or all floating-point registers. In such loops,
    1.58 +    // floating-point constants should not be hoisted unless it enables further
    1.59 +    // hoisting.
    1.60 +    bool containsPossibleCall_;
    1.61 +
    1.62 +    TempAllocator &alloc() const {
    1.63 +        return mir->alloc();
    1.64 +    }
    1.65 +
    1.66 +    bool hoistInstructions(InstructionQueue &toHoist);
    1.67 +
    1.68 +    // Utility methods for invariance testing and instruction hoisting.
    1.69 +    bool isInLoop(MDefinition *ins);
    1.70 +    bool isBeforeLoop(MDefinition *ins);
    1.71 +    bool isLoopInvariant(MInstruction *ins);
    1.72 +
    1.73 +    // This method determines if this block hot within a loop.  That is, if it's
    1.74 +    // always or usually run when the loop executes
    1.75 +    bool checkHotness(MBasicBlock *block);
    1.76 +
    1.77 +    // Worklist and worklist usage methods
    1.78 +    InstructionQueue worklist_;
    1.79 +    bool insertInWorklist(MInstruction *ins);
    1.80 +    MInstruction* popFromWorklist();
    1.81 +
    1.82 +    inline bool isHoistable(const MDefinition *ins) const {
    1.83 +        return ins->isMovable() && !ins->isEffectful() && !ins->neverHoist();
    1.84 +    }
    1.85 +
    1.86 +    bool requiresHoistedUse(const MDefinition *ins) const;
    1.87 +};
    1.88 +
    1.89 +} /* namespace anonymous */
    1.90 +
    1.91 +LICM::LICM(MIRGenerator *mir, MIRGraph &graph)
    1.92 +  : mir(mir), graph(graph)
    1.93 +{
    1.94 +}
    1.95 +
    1.96 +bool
    1.97 +LICM::analyze()
    1.98 +{
    1.99 +    IonSpew(IonSpew_LICM, "Beginning LICM pass.");
   1.100 +
   1.101 +    // Iterate in RPO to visit outer loops before inner loops.
   1.102 +    for (ReversePostorderIterator i(graph.rpoBegin()); i != graph.rpoEnd(); i++) {
   1.103 +        MBasicBlock *header = *i;
   1.104 +
   1.105 +        // Skip non-headers and self-loops.
   1.106 +        if (!header->isLoopHeader() || header->numPredecessors() < 2)
   1.107 +            continue;
   1.108 +
   1.109 +        // Attempt to optimize loop.
   1.110 +        Loop loop(mir, header);
   1.111 +
   1.112 +        Loop::LoopReturn lr = loop.init();
   1.113 +        if (lr == Loop::LoopReturn_Error)
   1.114 +            return false;
   1.115 +        if (lr == Loop::LoopReturn_Skip) {
   1.116 +            graph.unmarkBlocks();
   1.117 +            continue;
   1.118 +        }
   1.119 +
   1.120 +        if (!loop.optimize())
   1.121 +            return false;
   1.122 +
   1.123 +        graph.unmarkBlocks();
   1.124 +    }
   1.125 +
   1.126 +    return true;
   1.127 +}
   1.128 +
   1.129 +Loop::Loop(MIRGenerator *mir, MBasicBlock *header)
   1.130 +  : mir(mir),
   1.131 +    header_(header),
   1.132 +    containsPossibleCall_(false),
   1.133 +    worklist_(mir->alloc())
   1.134 +{
   1.135 +    preLoop_ = header_->getPredecessor(0);
   1.136 +}
   1.137 +
   1.138 +Loop::LoopReturn
   1.139 +Loop::init()
   1.140 +{
   1.141 +    IonSpew(IonSpew_LICM, "Loop identified, headed by block %d", header_->id());
   1.142 +    IonSpew(IonSpew_LICM, "footer is block %d", header_->backedge()->id());
   1.143 +
   1.144 +    // The first predecessor of the loop header must dominate the header.
   1.145 +    JS_ASSERT(header_->id() > header_->getPredecessor(0)->id());
   1.146 +
   1.147 +    // Loops from backedge to header and marks all visited blocks
   1.148 +    // as part of the loop. At the same time add all hoistable instructions
   1.149 +    // (in RPO order) to the instruction worklist.
   1.150 +    Vector<MBasicBlock *, 1, IonAllocPolicy> inlooplist(alloc());
   1.151 +    if (!inlooplist.append(header_->backedge()))
   1.152 +        return LoopReturn_Error;
   1.153 +    header_->backedge()->mark();
   1.154 +
   1.155 +    while (!inlooplist.empty()) {
   1.156 +        MBasicBlock *block = inlooplist.back();
   1.157 +
   1.158 +        // Hoisting requires more finesse if the loop contains a block that
   1.159 +        // self-dominates: there exists control flow that may enter the loop
   1.160 +        // without passing through the loop preheader.
   1.161 +        //
   1.162 +        // Rather than perform a complicated analysis of the dominance graph,
   1.163 +        // just return a soft error to ignore this loop.
   1.164 +        if (block->immediateDominator() == block) {
   1.165 +            while (!worklist_.empty())
   1.166 +                popFromWorklist();
   1.167 +            return LoopReturn_Skip;
   1.168 +        }
   1.169 +
   1.170 +        // Add not yet visited predecessors to the inlooplist.
   1.171 +        if (block != header_) {
   1.172 +            for (size_t i = 0; i < block->numPredecessors(); i++) {
   1.173 +                MBasicBlock *pred = block->getPredecessor(i);
   1.174 +                if (pred->isMarked())
   1.175 +                    continue;
   1.176 +
   1.177 +                if (!inlooplist.append(pred))
   1.178 +                    return LoopReturn_Error;
   1.179 +                pred->mark();
   1.180 +            }
   1.181 +        }
   1.182 +
   1.183 +        // If any block was added, process them first.
   1.184 +        if (block != inlooplist.back())
   1.185 +            continue;
   1.186 +
   1.187 +        // Add all instructions in this block (but the control instruction) to the worklist
   1.188 +        for (MInstructionIterator i = block->begin(); i != block->end(); i++) {
   1.189 +            MInstruction *ins = *i;
   1.190 +
   1.191 +            // Remember whether this loop contains anything which clobbers most
   1.192 +            // or all floating-point registers. This is just a rough heuristic.
   1.193 +            if (ins->possiblyCalls())
   1.194 +                containsPossibleCall_ = true;
   1.195 +
   1.196 +            if (isHoistable(ins)) {
   1.197 +                if (!insertInWorklist(ins))
   1.198 +                    return LoopReturn_Error;
   1.199 +            }
   1.200 +        }
   1.201 +
   1.202 +        // All successors of this block are visited.
   1.203 +        inlooplist.popBack();
   1.204 +    }
   1.205 +
   1.206 +    return LoopReturn_Success;
   1.207 +}
   1.208 +
   1.209 +bool
   1.210 +Loop::optimize()
   1.211 +{
   1.212 +    InstructionQueue invariantInstructions(alloc());
   1.213 +
   1.214 +    IonSpew(IonSpew_LICM, "These instructions are in the loop: ");
   1.215 +
   1.216 +    while (!worklist_.empty()) {
   1.217 +        if (mir->shouldCancel("LICM (worklist)"))
   1.218 +            return false;
   1.219 +
   1.220 +        MInstruction *ins = popFromWorklist();
   1.221 +
   1.222 +        IonSpewHeader(IonSpew_LICM);
   1.223 +
   1.224 +        if (IonSpewEnabled(IonSpew_LICM)) {
   1.225 +            ins->printName(IonSpewFile);
   1.226 +            fprintf(IonSpewFile, " <- ");
   1.227 +            ins->printOpcode(IonSpewFile);
   1.228 +            fprintf(IonSpewFile, ":  ");
   1.229 +        }
   1.230 +
   1.231 +        if (isLoopInvariant(ins)) {
   1.232 +            // Flag this instruction as loop invariant.
   1.233 +            ins->setLoopInvariant();
   1.234 +            if (!invariantInstructions.append(ins))
   1.235 +                return false;
   1.236 +
   1.237 +            if (IonSpewEnabled(IonSpew_LICM))
   1.238 +                fprintf(IonSpewFile, " Loop Invariant!\n");
   1.239 +        }
   1.240 +    }
   1.241 +
   1.242 +    if (!hoistInstructions(invariantInstructions))
   1.243 +        return false;
   1.244 +    return true;
   1.245 +}
   1.246 +
   1.247 +bool
   1.248 +Loop::requiresHoistedUse(const MDefinition *ins) const
   1.249 +{
   1.250 +    if (ins->isConstantElements() || ins->isBox())
   1.251 +        return true;
   1.252 +
   1.253 +    // Integer constants can often be folded as immediates and aren't worth
   1.254 +    // hoisting on their own, in general. Floating-point constants typically
   1.255 +    // are worth hoisting, unless they'll end up being spilled (eg. due to a
   1.256 +    // call).
   1.257 +    if (ins->isConstant() && (IsFloatingPointType(ins->type()) || containsPossibleCall_))
   1.258 +        return true;
   1.259 +
   1.260 +    return false;
   1.261 +}
   1.262 +
   1.263 +bool
   1.264 +Loop::hoistInstructions(InstructionQueue &toHoist)
   1.265 +{
   1.266 +    // Iterate in post-order (uses before definitions)
   1.267 +    for (int32_t i = toHoist.length() - 1; i >= 0; i--) {
   1.268 +        MInstruction *ins = toHoist[i];
   1.269 +
   1.270 +        // Don't hoist a cheap constant if it doesn't enable us to hoist one of
   1.271 +        // its uses. We want those instructions as close as possible to their
   1.272 +        // use, to facilitate folding and minimize register pressure.
   1.273 +        if (requiresHoistedUse(ins)) {
   1.274 +            bool loopInvariantUse = false;
   1.275 +            for (MUseDefIterator use(ins); use; use++) {
   1.276 +                if (use.def()->isLoopInvariant()) {
   1.277 +                    loopInvariantUse = true;
   1.278 +                    break;
   1.279 +                }
   1.280 +            }
   1.281 +
   1.282 +            if (!loopInvariantUse)
   1.283 +                ins->setNotLoopInvariant();
   1.284 +        }
   1.285 +    }
   1.286 +
   1.287 +    // Move all instructions to the preLoop_ block just before the control instruction.
   1.288 +    for (size_t i = 0; i < toHoist.length(); i++) {
   1.289 +        MInstruction *ins = toHoist[i];
   1.290 +
   1.291 +        // Loads may have an implicit dependency on either stores (effectful instructions) or
   1.292 +        // control instructions so we should never move these.
   1.293 +        JS_ASSERT(!ins->isControlInstruction());
   1.294 +        JS_ASSERT(!ins->isEffectful());
   1.295 +        JS_ASSERT(ins->isMovable());
   1.296 +
   1.297 +        if (!ins->isLoopInvariant())
   1.298 +            continue;
   1.299 +
   1.300 +        if (checkHotness(ins->block())) {
   1.301 +            ins->block()->moveBefore(preLoop_->lastIns(), ins);
   1.302 +            ins->setNotLoopInvariant();
   1.303 +        }
   1.304 +    }
   1.305 +
   1.306 +    return true;
   1.307 +}
   1.308 +
   1.309 +bool
   1.310 +Loop::isInLoop(MDefinition *ins)
   1.311 +{
   1.312 +    return ins->block()->isMarked();
   1.313 +}
   1.314 +
   1.315 +bool
   1.316 +Loop::isBeforeLoop(MDefinition *ins)
   1.317 +{
   1.318 +    return ins->block()->id() < header_->id();
   1.319 +}
   1.320 +
   1.321 +bool
   1.322 +Loop::isLoopInvariant(MInstruction *ins)
   1.323 +{
   1.324 +    if (!isHoistable(ins)) {
   1.325 +        if (IonSpewEnabled(IonSpew_LICM))
   1.326 +            fprintf(IonSpewFile, "not hoistable\n");
   1.327 +        return false;
   1.328 +    }
   1.329 +
   1.330 +    // Don't hoist if this instruction depends on a store inside or after the loop.
   1.331 +    // Note: "after the loop" can sound strange, but Alias Analysis doesn't look
   1.332 +    // at the control flow. Therefore it doesn't match the definition here, that a block
   1.333 +    // is in the loop when there is a (directed) path from the block to the loop header.
   1.334 +    if (ins->dependency() && !isBeforeLoop(ins->dependency())) {
   1.335 +        if (IonSpewEnabled(IonSpew_LICM)) {
   1.336 +            fprintf(IonSpewFile, "depends on store inside or after loop: ");
   1.337 +            ins->dependency()->printName(IonSpewFile);
   1.338 +            fprintf(IonSpewFile, "\n");
   1.339 +        }
   1.340 +        return false;
   1.341 +    }
   1.342 +
   1.343 +    // An instruction is only loop invariant if it and all of its operands can
   1.344 +    // be safely hoisted into the loop preheader.
   1.345 +    for (size_t i = 0, e = ins->numOperands(); i < e; i++) {
   1.346 +        if (isInLoop(ins->getOperand(i)) &&
   1.347 +            !ins->getOperand(i)->isLoopInvariant()) {
   1.348 +
   1.349 +            if (IonSpewEnabled(IonSpew_LICM)) {
   1.350 +                ins->getOperand(i)->printName(IonSpewFile);
   1.351 +                fprintf(IonSpewFile, " is in the loop.\n");
   1.352 +            }
   1.353 +
   1.354 +            return false;
   1.355 +        }
   1.356 +    }
   1.357 +
   1.358 +    return true;
   1.359 +}
   1.360 +
   1.361 +bool
   1.362 +Loop::checkHotness(MBasicBlock *block)
   1.363 +{
   1.364 +    // TODO: determine if instructions within this block are worth hoisting.
   1.365 +    // They might not be if the block is cold enough within the loop.
   1.366 +    // BUG 669517
   1.367 +    return true;
   1.368 +}
   1.369 +
   1.370 +bool
   1.371 +Loop::insertInWorklist(MInstruction *ins)
   1.372 +{
   1.373 +    if (!worklist_.insert(worklist_.begin(), ins))
   1.374 +        return false;
   1.375 +    ins->setInWorklist();
   1.376 +    return true;
   1.377 +}
   1.378 +
   1.379 +MInstruction*
   1.380 +Loop::popFromWorklist()
   1.381 +{
   1.382 +    MInstruction* toReturn = worklist_.popCopy();
   1.383 +    toReturn->setNotInWorklist();
   1.384 +    return toReturn;
   1.385 +}

mercurial