js/src/jit/AliasAnalysis.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/js/src/jit/AliasAnalysis.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,296 @@
     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/AliasAnalysis.h"
    1.11 +
    1.12 +#include <stdio.h>
    1.13 +
    1.14 +#include "jit/Ion.h"
    1.15 +#include "jit/IonBuilder.h"
    1.16 +#include "jit/IonSpewer.h"
    1.17 +#include "jit/MIR.h"
    1.18 +#include "jit/MIRGraph.h"
    1.19 +
    1.20 +using namespace js;
    1.21 +using namespace js::jit;
    1.22 +
    1.23 +using mozilla::Array;
    1.24 +
    1.25 +namespace js {
    1.26 +namespace jit {
    1.27 +
    1.28 +class LoopAliasInfo : public TempObject
    1.29 +{
    1.30 +  private:
    1.31 +    LoopAliasInfo *outer_;
    1.32 +    MBasicBlock *loopHeader_;
    1.33 +    MDefinitionVector invariantLoads_;
    1.34 +
    1.35 +  public:
    1.36 +    LoopAliasInfo(TempAllocator &alloc, LoopAliasInfo *outer, MBasicBlock *loopHeader)
    1.37 +      : outer_(outer), loopHeader_(loopHeader), invariantLoads_(alloc)
    1.38 +    { }
    1.39 +
    1.40 +    MBasicBlock *loopHeader() const {
    1.41 +        return loopHeader_;
    1.42 +    }
    1.43 +    LoopAliasInfo *outer() const {
    1.44 +        return outer_;
    1.45 +    }
    1.46 +    bool addInvariantLoad(MDefinition *ins) {
    1.47 +        return invariantLoads_.append(ins);
    1.48 +    }
    1.49 +    const MDefinitionVector& invariantLoads() const {
    1.50 +        return invariantLoads_;
    1.51 +    }
    1.52 +    MDefinition *firstInstruction() const {
    1.53 +        return *loopHeader_->begin();
    1.54 +    }
    1.55 +};
    1.56 +
    1.57 +} // namespace jit
    1.58 +} // namespace js
    1.59 +
    1.60 +namespace {
    1.61 +
    1.62 +// Iterates over the flags in an AliasSet.
    1.63 +class AliasSetIterator
    1.64 +{
    1.65 +  private:
    1.66 +    uint32_t flags;
    1.67 +    unsigned pos;
    1.68 +
    1.69 +  public:
    1.70 +    AliasSetIterator(AliasSet set)
    1.71 +      : flags(set.flags()), pos(0)
    1.72 +    {
    1.73 +        while (flags && (flags & 1) == 0) {
    1.74 +            flags >>= 1;
    1.75 +            pos++;
    1.76 +        }
    1.77 +    }
    1.78 +    AliasSetIterator& operator ++(int) {
    1.79 +        do {
    1.80 +            flags >>= 1;
    1.81 +            pos++;
    1.82 +        } while (flags && (flags & 1) == 0);
    1.83 +        return *this;
    1.84 +    }
    1.85 +    operator bool() const {
    1.86 +        return !!flags;
    1.87 +    }
    1.88 +    unsigned operator *() const {
    1.89 +        JS_ASSERT(pos < AliasSet::NumCategories);
    1.90 +        return pos;
    1.91 +    }
    1.92 +};
    1.93 +
    1.94 +} /* anonymous namespace */
    1.95 +
    1.96 +AliasAnalysis::AliasAnalysis(MIRGenerator *mir, MIRGraph &graph)
    1.97 +  : mir(mir),
    1.98 +    graph_(graph),
    1.99 +    loop_(nullptr)
   1.100 +{
   1.101 +}
   1.102 +
   1.103 +// Whether there might be a path from src to dest, excluding loop backedges. This is
   1.104 +// approximate and really ought to depend on precomputed reachability information.
   1.105 +static inline bool
   1.106 +BlockMightReach(MBasicBlock *src, MBasicBlock *dest)
   1.107 +{
   1.108 +    while (src->id() <= dest->id()) {
   1.109 +        if (src == dest)
   1.110 +            return true;
   1.111 +        switch (src->numSuccessors()) {
   1.112 +          case 0:
   1.113 +            return false;
   1.114 +          case 1: {
   1.115 +            MBasicBlock *successor = src->getSuccessor(0);
   1.116 +            if (successor->id() <= src->id())
   1.117 +                return true; // Don't iloop.
   1.118 +            src = successor;
   1.119 +            break;
   1.120 +          }
   1.121 +          default:
   1.122 +            return true;
   1.123 +        }
   1.124 +    }
   1.125 +    return false;
   1.126 +}
   1.127 +
   1.128 +static void
   1.129 +IonSpewDependency(MDefinition *load, MDefinition *store, const char *verb, const char *reason)
   1.130 +{
   1.131 +    if (!IonSpewEnabled(IonSpew_Alias))
   1.132 +        return;
   1.133 +
   1.134 +    fprintf(IonSpewFile, "Load ");
   1.135 +    load->printName(IonSpewFile);
   1.136 +    fprintf(IonSpewFile, " %s on store ", verb);
   1.137 +    store->printName(IonSpewFile);
   1.138 +    fprintf(IonSpewFile, " (%s)\n", reason);
   1.139 +}
   1.140 +
   1.141 +static void
   1.142 +IonSpewAliasInfo(const char *pre, MDefinition *ins, const char *post)
   1.143 +{
   1.144 +    if (!IonSpewEnabled(IonSpew_Alias))
   1.145 +        return;
   1.146 +
   1.147 +    fprintf(IonSpewFile, "%s ", pre);
   1.148 +    ins->printName(IonSpewFile);
   1.149 +    fprintf(IonSpewFile, " %s\n", post);
   1.150 +}
   1.151 +
   1.152 +// This pass annotates every load instruction with the last store instruction
   1.153 +// on which it depends. The algorithm is optimistic in that it ignores explicit
   1.154 +// dependencies and only considers loads and stores.
   1.155 +//
   1.156 +// Loads inside loops only have an implicit dependency on a store before the
   1.157 +// loop header if no instruction inside the loop body aliases it. To calculate
   1.158 +// this efficiently, we maintain a list of maybe-invariant loads and the combined
   1.159 +// alias set for all stores inside the loop. When we see the loop's backedge, this
   1.160 +// information is used to mark every load we wrongly assumed to be loop invaraint as
   1.161 +// having an implicit dependency on the last instruction of the loop header, so that
   1.162 +// it's never moved before the loop header.
   1.163 +//
   1.164 +// The algorithm depends on the invariant that both control instructions and effectful
   1.165 +// instructions (stores) are never hoisted.
   1.166 +bool
   1.167 +AliasAnalysis::analyze()
   1.168 +{
   1.169 +    Vector<MDefinitionVector, AliasSet::NumCategories, IonAllocPolicy> stores(alloc());
   1.170 +
   1.171 +    // Initialize to the first instruction.
   1.172 +    MDefinition *firstIns = *graph_.begin()->begin();
   1.173 +    for (unsigned i = 0; i < AliasSet::NumCategories; i++) {
   1.174 +        MDefinitionVector defs(alloc());
   1.175 +        if (!defs.append(firstIns))
   1.176 +            return false;
   1.177 +        if (!stores.append(Move(defs)))
   1.178 +            return false;
   1.179 +    }
   1.180 +
   1.181 +    // Type analysis may have inserted new instructions. Since this pass depends
   1.182 +    // on the instruction number ordering, all instructions are renumbered.
   1.183 +    // We start with 1 because some passes use 0 to denote failure.
   1.184 +    uint32_t newId = 1;
   1.185 +
   1.186 +    for (ReversePostorderIterator block(graph_.rpoBegin()); block != graph_.rpoEnd(); block++) {
   1.187 +        if (mir->shouldCancel("Alias Analysis (main loop)"))
   1.188 +            return false;
   1.189 +
   1.190 +        if (block->isLoopHeader()) {
   1.191 +            IonSpew(IonSpew_Alias, "Processing loop header %d", block->id());
   1.192 +            loop_ = new(alloc()) LoopAliasInfo(alloc(), loop_, *block);
   1.193 +        }
   1.194 +
   1.195 +        for (MDefinitionIterator def(*block); def; def++) {
   1.196 +            def->setId(newId++);
   1.197 +
   1.198 +            AliasSet set = def->getAliasSet();
   1.199 +            if (set.isNone())
   1.200 +                continue;
   1.201 +
   1.202 +            if (set.isStore()) {
   1.203 +                for (AliasSetIterator iter(set); iter; iter++) {
   1.204 +                    if (!stores[*iter].append(*def))
   1.205 +                        return false;
   1.206 +                }
   1.207 +
   1.208 +                if (IonSpewEnabled(IonSpew_Alias)) {
   1.209 +                    fprintf(IonSpewFile, "Processing store ");
   1.210 +                    def->printName(IonSpewFile);
   1.211 +                    fprintf(IonSpewFile, " (flags %x)\n", set.flags());
   1.212 +                }
   1.213 +            } else {
   1.214 +                // Find the most recent store on which this instruction depends.
   1.215 +                MDefinition *lastStore = firstIns;
   1.216 +
   1.217 +                for (AliasSetIterator iter(set); iter; iter++) {
   1.218 +                    MDefinitionVector &aliasedStores = stores[*iter];
   1.219 +                    for (int i = aliasedStores.length() - 1; i >= 0; i--) {
   1.220 +                        MDefinition *store = aliasedStores[i];
   1.221 +                        if (def->mightAlias(store) && BlockMightReach(store->block(), *block)) {
   1.222 +                            if (lastStore->id() < store->id())
   1.223 +                                lastStore = store;
   1.224 +                            break;
   1.225 +                        }
   1.226 +                    }
   1.227 +                }
   1.228 +
   1.229 +                def->setDependency(lastStore);
   1.230 +                IonSpewDependency(*def, lastStore, "depends", "");
   1.231 +
   1.232 +                // If the last store was before the current loop, we assume this load
   1.233 +                // is loop invariant. If a later instruction writes to the same location,
   1.234 +                // we will fix this at the end of the loop.
   1.235 +                if (loop_ && lastStore->id() < loop_->firstInstruction()->id()) {
   1.236 +                    if (!loop_->addInvariantLoad(*def))
   1.237 +                        return false;
   1.238 +                }
   1.239 +            }
   1.240 +        }
   1.241 +
   1.242 +        // Renumber the last instruction, as the analysis depends on this and the order.
   1.243 +        block->lastIns()->setId(newId++);
   1.244 +
   1.245 +        if (block->isLoopBackedge()) {
   1.246 +            JS_ASSERT(loop_->loopHeader() == block->loopHeaderOfBackedge());
   1.247 +            IonSpew(IonSpew_Alias, "Processing loop backedge %d (header %d)", block->id(),
   1.248 +                    loop_->loopHeader()->id());
   1.249 +            LoopAliasInfo *outerLoop = loop_->outer();
   1.250 +            MInstruction *firstLoopIns = *loop_->loopHeader()->begin();
   1.251 +
   1.252 +            const MDefinitionVector &invariant = loop_->invariantLoads();
   1.253 +
   1.254 +            for (unsigned i = 0; i < invariant.length(); i++) {
   1.255 +                MDefinition *ins = invariant[i];
   1.256 +                AliasSet set = ins->getAliasSet();
   1.257 +                JS_ASSERT(set.isLoad());
   1.258 +
   1.259 +                bool hasAlias = false;
   1.260 +                for (AliasSetIterator iter(set); iter; iter++) {
   1.261 +                    MDefinitionVector &aliasedStores = stores[*iter];
   1.262 +                    for (int i = aliasedStores.length() - 1;; i--) {
   1.263 +                        MDefinition *store = aliasedStores[i];
   1.264 +                        if (store->id() < firstLoopIns->id())
   1.265 +                            break;
   1.266 +                        if (ins->mightAlias(store)) {
   1.267 +                            hasAlias = true;
   1.268 +                            IonSpewDependency(ins, store, "aliases", "store in loop body");
   1.269 +                            break;
   1.270 +                        }
   1.271 +                    }
   1.272 +                    if (hasAlias)
   1.273 +                        break;
   1.274 +                }
   1.275 +
   1.276 +                if (hasAlias) {
   1.277 +                    // This instruction depends on stores inside the loop body. Mark it as having a
   1.278 +                    // dependency on the last instruction of the loop header. The last instruction is a
   1.279 +                    // control instruction and these are never hoisted.
   1.280 +                    MControlInstruction *controlIns = loop_->loopHeader()->lastIns();
   1.281 +                    IonSpewDependency(ins, controlIns, "depends", "due to stores in loop body");
   1.282 +                    ins->setDependency(controlIns);
   1.283 +                } else {
   1.284 +                    IonSpewAliasInfo("Load", ins, "does not depend on any stores in this loop");
   1.285 +
   1.286 +                    if (outerLoop && ins->dependency()->id() < outerLoop->firstInstruction()->id()) {
   1.287 +                        IonSpewAliasInfo("Load", ins, "may be invariant in outer loop");
   1.288 +                        if (!outerLoop->addInvariantLoad(ins))
   1.289 +                            return false;
   1.290 +                    }
   1.291 +                }
   1.292 +            }
   1.293 +            loop_ = loop_->outer();
   1.294 +        }
   1.295 +    }
   1.296 +
   1.297 +    JS_ASSERT(loop_ == nullptr);
   1.298 +    return true;
   1.299 +}

mercurial