Mypal/js/src/jit/FlowAliasAnalysis.cpp
2019-03-11 13:26:37 +03:00

950 lines
28 KiB
C++

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sts=4 et sw=4 tw=99:
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "jit/FlowAliasAnalysis.h"
#include <stdio.h>
#include "jit/AliasAnalysisShared.h"
#include "jit/Ion.h"
#include "jit/IonBuilder.h"
#include "jit/JitSpewer.h"
#include "jit/MIR.h"
#include "jit/MIRGraph.h"
#include "vm/Printer.h"
using namespace js;
using namespace js::jit;
using mozilla::Array;
namespace js {
namespace jit {
class LoopInfo : public TempObject
{
private:
LoopInfo* outer_;
MBasicBlock* loopHeader_;
MDefinitionVector loopinvariant_;
public:
LoopInfo(TempAllocator& alloc, LoopInfo* outer, MBasicBlock* loopHeader)
: outer_(outer), loopHeader_(loopHeader), loopinvariant_(alloc)
{ }
MBasicBlock* loopHeader() const {
return loopHeader_;
}
LoopInfo* outer() const {
return outer_;
}
MDefinitionVector& loopinvariant() {
return loopinvariant_;
}
};
static bool
KeepBlock(MBasicBlock *block)
{
// Any block that is predecessor to a loopheader need to be kept.
// We need it to process possible loop invariant loads.
if (block->numSuccessors() == 1 && block->getSuccessor(0)->isLoopHeader())
return true;
#ifdef DEBUG
// We assume a predecessor to a loopheader has one successor.
for (size_t i = 0; i < block->numSuccessors(); i++)
MOZ_ASSERT(!block->getSuccessor(i)->isLoopHeader());
#endif
return false;
}
class GraphStoreInfo : public TempObject
{
// The current BlockStoreInfo while iterating the block untill,
// it contains the store info at the end of the block.
BlockStoreInfo* current_;
// Vector with pointer to BlockStoreInfo at the end of the block for every block.
// Only keeping the info during iteration if needed, else contains nullptr.
GraphStoreVector stores_;
// All BlockStoreInfo's that aren't needed anymore and can be reused.
GraphStoreVector empty_;
public:
explicit GraphStoreInfo(TempAllocator& alloc)
: current_(nullptr),
stores_(alloc),
empty_(alloc)
{ }
bool reserve(size_t num) {
return stores_.appendN(nullptr, num);
}
BlockStoreInfo& current() {
return *current_;
}
void unsetCurrent() {
current_ = nullptr;
}
BlockStoreInfo* newCurrent(TempAllocator& alloc, MBasicBlock* block) {
BlockStoreInfo *info = nullptr;
if (empty_.length() != 0) {
info = empty_.popCopy();
} else {
info = (BlockStoreInfo*) alloc.allocate(sizeof(BlockStoreInfo));
if (!info)
return nullptr;
new(info) BlockStoreInfo(alloc);
}
stores_[block->id()] = info;
current_ = info;
return current_;
}
void swap(MBasicBlock* block1, MBasicBlock* block2) {
BlockStoreInfo* info = stores_[block1->id()];
stores_[block1->id()] = stores_[block2->id()];
stores_[block2->id()] = info;
if (stores_[block1->id()] == current_)
current_ = stores_[block2->id()];
else if (stores_[block2->id()] == current_)
current_ = stores_[block1->id()];
}
bool maybeFreePredecessorBlocks(MBasicBlock* block) {
for (size_t i=0; i < block->numPredecessors(); i++) {
// For some blocks we cannot free the store info.
if (KeepBlock(block->getPredecessor(i)))
continue;
// Check the given block is the last successor.
bool release = true;
for (size_t j = 0; j < block->getPredecessor(i)->numSuccessors(); j++) {
if (block->getPredecessor(i)->getSuccessor(j)->id() > block->id()) {
release = false;
break;
}
}
if (release) {
BlockStoreInfo *info = stores_[block->getPredecessor(i)->id()];
if (!empty_.append(info))
return false;
info->clear();
stores_[block->getPredecessor(i)->id()] = nullptr;
}
}
return true;
}
BlockStoreInfo& get(MBasicBlock* block) {
MOZ_ASSERT(stores_[block->id()] != current_);
return *stores_[block->id()];
}
};
} // namespace jit
} // namespace js
FlowAliasAnalysis::FlowAliasAnalysis(MIRGenerator* mir, MIRGraph& graph)
: AliasAnalysisShared(mir, graph),
loop_(nullptr),
output_(graph_.alloc()),
worklist_(graph_.alloc())
{
stores_ = new(graph_.alloc()) GraphStoreInfo(graph_.alloc());
}
template <typename T>
static bool
AppendToWorklist(MDefinitionVector& worklist, T& stores)
{
if (!worklist.reserve(worklist.length() + stores.length()))
return false;
for (size_t j = 0; j < stores.length(); j++) {
MOZ_ASSERT(stores[j]);
if (stores[j]->isInWorklist())
continue;
worklist.infallibleAppend(stores[j]);
stores[j]->setInWorklist();
}
return true;
}
static void
SetNotInWorkList(MDefinitionVector& worklist)
{
for (size_t item = 0; item < worklist.length(); item++)
worklist[item]->setNotInWorklistUnchecked();
}
static bool
LoadAliasesStore(MDefinition* load, MDefinition* store)
{
// Always alias first instruction of graph.
if (store->id() == 0)
return true;
// Default to alias control instructions which indicates loops.
// Control instructions are special, since we need to determine
// if it aliases anything in the full loop. Which we do lateron.
if (store->isControlInstruction())
return true;
// Check if the alias categories alias eachother.
if ((load->getAliasSet() & store->getAliasSet()).isNone())
return false;
// On any operation that has a specific alias category we can use TI to know
// the objects operating on don't intersect.
MDefinition::AliasType mightAlias = AliasAnalysisShared::genericMightAlias(load, store);
if (mightAlias == MDefinition::AliasType::NoAlias)
return false;
// Check if the instruction might alias eachother.
mightAlias = load->mightAlias(store);
if (mightAlias == MDefinition::AliasType::NoAlias)
return false;
return true;
}
#ifdef JS_JITSPEW
static void
DumpAliasSet(AliasSet set)
{
Fprinter &print = JitSpewPrinter();
if (set.flags() == AliasSet::Any) {
print.printf("Any");
return;
}
bool first = true;
for (AliasSetIterator iter(set); iter; iter++) {
if (!first)
print.printf(", ");
print.printf("%s", AliasSet::Name(*iter));
first = false;
}
}
#endif
#ifdef JS_JITSPEW
static void
DumpStoreList(BlockStoreInfo& stores)
{
Fprinter &print = JitSpewPrinter();
if (stores.length() == 0) {
print.printf("empty");
return;
}
bool first = true;
for (size_t i = 0; i < stores.length(); i++) {
if (!first)
print.printf(", ");
if (!stores[i]) {
print.printf("nullptr");
continue;
}
MOZ_ASSERT(stores[i]->isControlInstruction() ||
stores[i]->getAliasSet().isStore() ||
stores[i]->id() == 0);
MDefinition::PrintOpcodeName(print, stores[i]->op());
print.printf("%d", stores[i]->id());
first = false;
}
}
#endif
static void
DumpAnalyzeStart()
{
#ifdef JS_JITSPEW
if (JitSpewEnabled(JitSpew_Alias) || JitSpewEnabled(JitSpew_AliasSummaries)) {
Fprinter &print = JitSpewPrinter();
JitSpewHeader(JitSpewEnabled(JitSpew_Alias) ? JitSpew_Alias : JitSpew_AliasSummaries);
print.printf("Running Alias Analysis on graph\n");
}
#endif
}
static void
DumpBlockStart(MBasicBlock* block)
{
#ifdef JS_JITSPEW
if (JitSpewEnabled(JitSpew_Alias) || JitSpewEnabled(JitSpew_AliasSummaries)) {
Fprinter &print = JitSpewPrinter();
JitSpewHeader(JitSpewEnabled(JitSpew_Alias)?JitSpew_Alias:JitSpew_AliasSummaries);
if (block->isLoopHeader())
print.printf(" Visiting block %d (loopheader)\n", block->id());
else
print.printf(" Visiting block %d\n", block->id());
}
#endif
}
static void
DumpProcessingDeferredLoads(MBasicBlock* loopHeader)
{
#ifdef JS_JITSPEW
if (JitSpewEnabled(JitSpew_Alias)) {
Fprinter &print = JitSpewPrinter();
JitSpewHeader(JitSpew_Alias);
print.printf(" Process deferred loads of loop %d\n", loopHeader->id());
}
#endif
}
static void
DumpBlockSummary(MBasicBlock* block, BlockStoreInfo& blockInfo)
{
#ifdef JS_JITSPEW
if (JitSpewEnabled(JitSpew_AliasSummaries)) {
Fprinter &print = JitSpewPrinter();
JitSpewHeader(JitSpew_AliasSummaries);
print.printf(" Store at end of block: ");
DumpStoreList(blockInfo);
print.printf("\n");
}
#endif
}
static void
DumpStore(MDefinition* store)
{
#ifdef JS_JITSPEW
if (JitSpewEnabled(JitSpew_Alias)) {
Fprinter &print = JitSpewPrinter();
JitSpewHeader(JitSpew_Alias);
print.printf(" Store ");
store->PrintOpcodeName(print, store->op());
print.printf("%d with flags (", store->id());
DumpAliasSet(store->getAliasSet());
print.printf(")\n");
}
#endif
}
static void
DumpLoad(MDefinition* load)
{
#ifdef JS_JITSPEW
if (JitSpewEnabled(JitSpew_Alias)) {
Fprinter &print = JitSpewPrinter();
JitSpewHeader(JitSpew_Alias);
print.printf(" Load ");
load->PrintOpcodeName(print, load->op());
print.printf("%d", load->id());
print.printf(" with flag (");
DumpAliasSet(load->getAliasSet());
print.printf(")\n");
}
#endif
}
static void
DumpLoadOutcome(MDefinition* load, MDefinitionVector& stores, bool defer)
{
#ifdef JS_JITSPEW
// Spew what we did.
if (JitSpewEnabled(JitSpew_Alias)) {
Fprinter &print = JitSpewPrinter();
JitSpewHeader(JitSpew_Alias);
print.printf(" Marked depending on ");
DumpStoreList(stores);
if (defer)
print.printf(" deferred");
print.printf("\n");
}
#endif
}
static void
DumpLoopInvariant(MDefinition* load, MBasicBlock* loopheader, bool loopinvariant,
MDefinitionVector& loopInvariantDependency)
{
#ifdef JS_JITSPEW
if (JitSpewEnabled(JitSpew_Alias)) {
Fprinter &print = JitSpewPrinter();
JitSpewHeader(JitSpew_Alias);
if (!loopinvariant) {
print.printf(" Determine not loop invariant to loop %d.\n", loopheader->id());
} else {
print.printf(" Determine loop invariant to loop %d. Dependendy is now: ", loopheader->id());
DumpStoreList(loopInvariantDependency);
print.printf("\n");
}
}
#endif
}
static void
DumpImprovement(MDefinition *load, MDefinitionVector& input, MDefinitionVector& output)
{
#ifdef JS_JITSPEW
if (JitSpewEnabled(JitSpew_Alias)) {
Fprinter &print = JitSpewPrinter();
JitSpewHeader(JitSpew_Alias);
print.printf(" Improve dependency from %d", load->id());
DumpStoreList(input);
print.printf(" to ");
DumpStoreList(output);
print.printf("\n");
}
#endif
}
// Flow Sensitive Alias Analysis.
//
// This pass annotates every load instructions with the last store instruction
// on which it depends in their dependency_ field. For loop variant instructions
// this will depend on the control instruction in the specific loop it cannot
// get hoisted out (if there is no store between start loopheader and
// instruction).
//
// We visit the graph in RPO and keep track of the last stores in that block.
// Upon entering a block we merge the stores information of the predecessors.
// Only loopheaders are different, since we eagerly make it depend on the
// control instruction of the loopheader.
//
// During the iteration of a block we keep a running store dependeny list.
// At the end of the iteration, this will contain the last stores
// (which we keep for successors).
//
// When encountering a store or load we do:
// - Store: we update the current block store info and put a StoreDependency
// to create a store-chain.
//
// - Load: we take the current block store dependency info and improve that by
// following the store-chain when encountering not aliasing store. Upon
// encountering a control instruction (indicates loop) it solely depends on
// we defer until the loop has been examined.
//
// The algorithm depends on the invariant that both control instructions and effectful
// instructions (stores) are never hoisted.
bool
FlowAliasAnalysis::analyze()
{
DumpAnalyzeStart();
// Type analysis may have inserted new instructions. Since this pass depends
// on the instruction number ordering, all instructions are renumbered.
uint32_t newId = 0;
if (!stores_->reserve(graph_.numBlocks()))
return false;
for (ReversePostorderIterator block(graph_.rpoBegin()); block != graph_.rpoEnd(); block++) {
if (mir->shouldCancel("Alias Analysis (main loop)"))
return false;
DumpBlockStart(*block);
if (!computeBlockStores(*block))
return false;
if (!stores_->maybeFreePredecessorBlocks(*block))
return false;
if (block->isLoopHeader())
loop_ = new(alloc()) LoopInfo(alloc(), loop_, *block);
for (MPhiIterator def(block->phisBegin()), end(block->phisEnd()); def != end; ++def)
def->setId(newId++);
BlockStoreInfo& blockInfo = stores_->current();
for (MInstructionIterator def(block->begin()), end(block->begin(block->lastIns()));
def != end;
++def)
{
def->setId(newId++);
// For the purposes of alias analysis, all recoverable operations
// are treated as effect free as the memory represented by these
// operations cannot be aliased by others.
if (def->canRecoverOnBailout())
continue;
AliasSet set = def->getAliasSet();
if (set.isStore()) {
if (!processStore(blockInfo, *def))
return false;
} else if (set.isLoad()) {
if (!processLoad(blockInfo, *def))
return false;
}
}
block->lastIns()->setId(newId++);
if (block->isLoopBackedge()) {
stores_->unsetCurrent();
LoopInfo* info = loop_;
loop_ = loop_->outer();
if (!processDeferredLoads(info))
return false;
}
DumpBlockSummary(*block, blockInfo);
}
spewDependencyList();
return true;
}
bool
FlowAliasAnalysis::processStore(BlockStoreInfo& blockInfo, MDefinition* store)
{
// Compute and set dependency information.
if (!saveStoreDependency(store, blockInfo))
return false;
// Update the block store dependency vector.
blockInfo.clear();
if (!blockInfo.append(store))
return false;
// Spew what we did.
DumpStore(store);
return true;
}
bool
FlowAliasAnalysis::processLoad(BlockStoreInfo& blockInfo, MDefinition* load)
{
DumpLoad(load);
// Compute dependency information.
MDefinitionVector& dependencies = blockInfo;
if (!improveDependency(load, dependencies, output_))
return false;
saveLoadDependency(load, output_);
// If possible defer when better loop information is present.
if (deferImproveDependency(output_)) {
if (!loop_->loopinvariant().append(load))
return false;
DumpLoadOutcome(load, output_, /* defer = */ true);
return true;
}
DumpLoadOutcome(load, output_, /* defer = */ false);
return true;
}
bool
FlowAliasAnalysis::processDeferredLoads(LoopInfo* info)
{
DumpProcessingDeferredLoads(info->loopHeader());
MOZ_ASSERT(loopIsFinished(info->loopHeader()));
for (size_t i = 0; i < info->loopinvariant().length(); i++) {
MDefinition* load = info->loopinvariant()[i];
DumpLoad(load);
// Defer load again when this loop still isn't finished yet.
if (!loopIsFinished(load->dependency()->block())) {
MOZ_ASSERT(loop_);
if (!loop_->loopinvariant().append(load))
return false;
DumpLoadOutcome(load, output_, /* defer = */ true);
continue;
}
MOZ_ASSERT(load->dependency()->block() == info->loopHeader());
MDefinition* store = load->dependency();
load->setDependency(nullptr);
// Test if this load is loop invariant and if it is,
// take the dependencies of non-backedge predecessors of the loop header.
bool loopinvariant;
if (!isLoopInvariant(load, store, &loopinvariant))
return false;
MDefinitionVector &loopInvariantDependency =
stores_->get(store->block()->loopPredecessor());
DumpLoopInvariant(load, info->loopHeader(), /* loopinvariant = */ loopinvariant,
loopInvariantDependency);
if (loopinvariant) {
if (!improveDependency(load, loopInvariantDependency, output_))
return false;
saveLoadDependency(load, output_);
// If possible defer when better loop information is present.
if (deferImproveDependency(output_)) {
if (!loop_->loopinvariant().append(load))
return false;
DumpLoadOutcome(load, output_, /* defer = */ true);
} else {
DumpLoadOutcome(load, output_, /* defer = */ false);
}
} else {
load->setDependency(store);
#ifdef JS_JITSPEW
output_.clear();
if (!output_.append(store))
return false;
DumpLoadOutcome(load, output_, /* defer = */ false);
#endif
}
}
return true;
}
// Given a load instruction and an initial store dependency list,
// find the most accurate store dependency list.
bool
FlowAliasAnalysis::improveDependency(MDefinition* load, MDefinitionVector& inputStores,
MDefinitionVector& outputStores)
{
MOZ_ASSERT(inputStores.length() > 0);
outputStores.clear();
if (!outputStores.appendAll(inputStores))
return false;
bool improved = false;
bool adjusted = true;
while (adjusted) {
adjusted = false;
if (!improveNonAliasedStores(load, outputStores, outputStores, &improved))
return false;
MOZ_ASSERT(outputStores.length() != 0);
if (!improveStoresInFinishedLoops(load, outputStores, &adjusted))
return false;
if (adjusted)
improved = true;
}
if (improved)
DumpImprovement(load, inputStores, outputStores);
return true;
}
// For every real store dependencies, follow the chain of stores to find the
// unique set of 'might alias' store dependencies.
bool
FlowAliasAnalysis::improveNonAliasedStores(MDefinition* load, MDefinitionVector& inputStores,
MDefinitionVector& outputStores, bool* improved,
bool onlyControlInstructions)
{
MOZ_ASSERT(worklist_.length() == 0);
if (!AppendToWorklist(worklist_, inputStores))
return false;
outputStores.clear();
for (size_t i = 0; i < worklist_.length(); i++) {
MOZ_ASSERT(worklist_[i]);
if (!LoadAliasesStore(load, worklist_[i])) {
StoreDependency* dep = worklist_[i]->storeDependency();
MOZ_ASSERT(dep);
MOZ_ASSERT(dep->get().length() > 0);
if (!AppendToWorklist(worklist_, dep->get()))
return false;
*improved = true;
continue;
}
if (onlyControlInstructions && !worklist_[i]->isControlInstruction()) {
outputStores.clear();
break;
}
if (!outputStores.append(worklist_[i]))
return false;
}
SetNotInWorkList(worklist_);
worklist_.clear();
return true;
}
// Given a load instruction and an initial store dependency list,
// find the most accurate store dependency list with only control instructions.
// Returns an empty output list, when there was a non control instructions
// that couldn't get improved to a control instruction.
bool
FlowAliasAnalysis::improveLoopDependency(MDefinition* load, MDefinitionVector& inputStores,
MDefinitionVector& outputStores)
{
outputStores.clear();
if (!outputStores.appendAll(inputStores))
return false;
bool improved = false;
bool adjusted = true;
while (adjusted) {
adjusted = false;
if (!improveNonAliasedStores(load, outputStores, outputStores, &improved,
/* onlyControlInstructions = */ true))
{
return false;
}
if (outputStores.length() == 0)
return true;
if (!improveStoresInFinishedLoops(load, outputStores, &adjusted))
return false;
if (adjusted)
improved = true;
}
if (improved)
DumpImprovement(load, inputStores, outputStores);
return true;
}
// For every control instruction in the output we find out if the load is loop
// invariant to that loop. When it is, improve the output dependency store,
// by pointing to the stores before the loop.
bool
FlowAliasAnalysis::improveStoresInFinishedLoops(MDefinition* load, MDefinitionVector& stores,
bool* improved)
{
for (size_t i = 0; i < stores.length(); i++) {
if (!stores[i]->isControlInstruction())
continue;
if (!stores[i]->block()->isLoopHeader())
continue;
MOZ_ASSERT(!stores[i]->storeDependency());
if (!loopIsFinished(stores[i]->block()))
continue;
if (load->dependency() == stores[i])
continue;
bool loopinvariant;
if (!isLoopInvariant(load, stores[i], &loopinvariant))
return false;
if (!loopinvariant)
continue;
MBasicBlock* pred = stores[i]->block()->loopPredecessor();
BlockStoreInfo& predInfo = stores_->get(pred);
MOZ_ASSERT(predInfo.length() > 0);
stores[i] = predInfo[0];
for (size_t j = 1; j < predInfo.length(); j++) {
if (!stores.append(predInfo[j]))
return false;
}
*improved = true;
}
return true;
}
bool
FlowAliasAnalysis::deferImproveDependency(MDefinitionVector& stores)
{
// Look if the store depends only on 1 non finished loop.
// In that case we will defer until that loop has finished.
return loop_ && stores.length() == 1 &&
stores[0]->isControlInstruction() &&
stores[0]->block()->isLoopHeader() &&
!loopIsFinished(stores[0]->block());
}
void
FlowAliasAnalysis::saveLoadDependency(MDefinition* load, MDefinitionVector& dependencies)
{
MOZ_ASSERT(dependencies.length() > 0);
// For now we only save the last store before the load for other passes.
// That means the store with the maximum id.
MDefinition* max = dependencies[0];
MDefinition* maxNonControl = nullptr;
for (size_t i = 0; i < dependencies.length(); i++) {
MDefinition* ins = dependencies[i];
if (max->id() < ins->id())
max = ins;
if (!ins->isControlInstruction()) {
if (!maxNonControl || maxNonControl->id() < ins->id())
maxNonControl = ins;
}
}
// For loop variant loads with no stores between loopheader and the load,
// the control instruction of the loop header is returned.
// That id is higher than any store inside the loopheader itself.
// Fix for dependency on item in loopheader, but before the "test".
// Which would assume it depends on the loop itself.
if (maxNonControl != max && maxNonControl) {
if (maxNonControl->block() == max->block())
max = maxNonControl;
}
load->setDependency(max);
}
bool
FlowAliasAnalysis::saveStoreDependency(MDefinition* ins, BlockStoreInfo& prevStores)
{
// To form a store dependency chain, we store the previous last dependencies
// in the current store.
StoreDependency* dependency = new(alloc()) StoreDependency(alloc());
if (!dependency)
return false;
if (!dependency->init(prevStores))
return false;
ins->setStoreDependency(dependency);
return true;
}
// Returns if loop has been processed
// and has complete backedge stores information.
bool
FlowAliasAnalysis::loopIsFinished(MBasicBlock* loopheader)
{
MOZ_ASSERT(loopheader->isLoopHeader());
if (!loop_)
return true;
return loopheader->backedge()->id() <
loop_->loopHeader()->backedge()->id();
}
// Determines if a load is loop invariant.
//
// Get the last store dependencies of the backedge of the loop and follow
// the store chain until finding the aliased stores. Make sure the computed
// aliased stores is only the loop control instruction or control instructions
// of loops it is also loop invariant. Only in that case the load is
// definitely loop invariant.
bool
FlowAliasAnalysis::isLoopInvariant(MDefinition* load, MDefinition* store, bool* loopinvariant)
{
MOZ_ASSERT(store->isControlInstruction());
MOZ_ASSERT(!store->storeDependency());
MOZ_ASSERT(store->block()->isLoopHeader());
MOZ_ASSERT(loopIsFinished(store->block()));
*loopinvariant = false;
MBasicBlock* backedge = store->block()->backedge();
MDefinitionVector output(alloc());
// To make sure the improve dependency stops at this loop,
// set the loop control instruction as dependency.
MDefinition* olddep = load->dependency();
load->setDependency(store);
if (!improveLoopDependency(load, stores_->get(backedge), output))
return false;
load->setDependency(olddep);
if (output.length() == 0)
return true;
for (size_t i = 0; i < output.length(); i++) {
if (output[i]->storeDependency())
return true;
if (!output[i]->isControlInstruction())
return true;
if (!output[i]->block()->isLoopHeader())
return true;
if (output[i] == store)
continue;
return true;
}
*loopinvariant = true;
return true;
}
// Compute the store dependencies at the start of this MBasicBlock.
bool
FlowAliasAnalysis::computeBlockStores(MBasicBlock* block)
{
BlockStoreInfo* blockInfo = stores_->newCurrent(alloc(), block);
if (!blockInfo)
return false;
// First block depends on the first instruction.
if (block->id() == 0) {
MDefinition* firstIns = *graph_.entryBlock()->begin();
if (!blockInfo->append(firstIns))
return false;
return true;
}
// For loopheaders we take the loopheaders control instruction.
// That is not moveable and easy is to detect.
if (block->isLoopHeader()) {
if (!blockInfo->append(block->lastIns()))
return false;
return true;
}
// Optimization for consecutive blocks.
if (block->numPredecessors() == 1) {
MBasicBlock* pred = block->getPredecessor(0);
if (pred->numSuccessors() == 1) {
stores_->swap(block, pred);
return true;
}
MOZ_ASSERT (pred->numSuccessors() > 1);
BlockStoreInfo& predInfo = stores_->get(pred);
return blockInfo->appendAll(predInfo);
}
// Heuristic: in most cases having more than 5 predecessors,
// increases the number of dependencies too much to still be able
// to do an optimization. Therefore don't do the merge work.
// For simplicity we take an non-dominant always existing instruction.
// That way we cannot accidentally move instructions depending on it.
if (block->numPredecessors() > 5) {
if (!blockInfo->append(block->getPredecessor(0)->lastIns()))
return false;
return true;
}
// Merging of multiple predecessors.
for (size_t pred = 0; pred < block->numPredecessors(); pred++) {
BlockStoreInfo& predInfo = stores_->get(block->getPredecessor(pred));
if (!AppendToWorklist(*blockInfo, predInfo))
return false;
}
SetNotInWorkList(*blockInfo);
return true;
}