mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-15 06:15:43 +00:00
Bug 1039458 - Add disabled loop unrolling optimization pass, r=jandem.
This commit is contained in:
parent
0a1e77e258
commit
585dac288d
@ -23,6 +23,8 @@ class InlineForwardListNode
|
||||
explicit InlineForwardListNode(InlineForwardListNode<T> *n) : next(n)
|
||||
{ }
|
||||
|
||||
InlineForwardListNode(const InlineForwardListNode<T> &) MOZ_DELETE;
|
||||
|
||||
protected:
|
||||
friend class InlineForwardList<T>;
|
||||
friend class InlineForwardListIterator<T>;
|
||||
@ -218,6 +220,8 @@ class InlineListNode : public InlineForwardListNode<T>
|
||||
prev(p)
|
||||
{ }
|
||||
|
||||
InlineListNode(const InlineListNode<T> &) MOZ_DELETE;
|
||||
|
||||
protected:
|
||||
friend class InlineList<T>;
|
||||
friend class InlineListIterator<T>;
|
||||
|
@ -32,6 +32,7 @@
|
||||
#include "jit/LICM.h"
|
||||
#include "jit/LinearScan.h"
|
||||
#include "jit/LIR.h"
|
||||
#include "jit/LoopUnroller.h"
|
||||
#include "jit/Lowering.h"
|
||||
#include "jit/ParallelSafetyAnalysis.h"
|
||||
#include "jit/PerfSpewer.h"
|
||||
@ -1512,6 +1513,16 @@ OptimizeMIR(MIRGenerator *mir)
|
||||
if (mir->shouldCancel("Truncate Doubles"))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mir->optimizationInfo().loopUnrollingEnabled()) {
|
||||
AutoTraceLog log(logger, TraceLogger::LoopUnrolling);
|
||||
|
||||
if (!UnrollLoops(graph, r.loopIterationBounds))
|
||||
return false;
|
||||
|
||||
IonSpewPass("Unroll Loops");
|
||||
AssertExtendedGraphCoherency(graph);
|
||||
}
|
||||
}
|
||||
|
||||
if (mir->optimizationInfo().eaaEnabled()) {
|
||||
@ -1537,10 +1548,8 @@ OptimizeMIR(MIRGenerator *mir)
|
||||
return false;
|
||||
}
|
||||
|
||||
// Make loops contiguious. We do this after GVN/UCE and range analysis,
|
||||
// Make loops contiguous. We do this after GVN/UCE and range analysis,
|
||||
// which can remove CFG edges, exposing more blocks that can be moved.
|
||||
// We also disable this when profiling, since reordering blocks appears
|
||||
// to make the profiler unhappy.
|
||||
{
|
||||
AutoTraceLog log(logger, TraceLogger::MakeLoopsContiguous);
|
||||
if (!MakeLoopsContiguous(graph))
|
||||
|
@ -1543,6 +1543,13 @@ IntersectDominators(MBasicBlock *block1, MBasicBlock *block2)
|
||||
return finger1;
|
||||
}
|
||||
|
||||
void
|
||||
jit::ClearDominatorTree(MIRGraph &graph)
|
||||
{
|
||||
for (MBasicBlockIterator iter = graph.begin(); iter != graph.end(); iter++)
|
||||
iter->clearDominatorInfo();
|
||||
}
|
||||
|
||||
static void
|
||||
ComputeImmediateDominators(MIRGraph &graph)
|
||||
{
|
||||
@ -2347,13 +2354,19 @@ LinearSum::multiply(int32_t scale)
|
||||
}
|
||||
|
||||
bool
|
||||
LinearSum::add(const LinearSum &other)
|
||||
LinearSum::add(const LinearSum &other, int32_t scale /* = 1 */)
|
||||
{
|
||||
for (size_t i = 0; i < other.terms_.length(); i++) {
|
||||
if (!add(other.terms_[i].term, other.terms_[i].scale))
|
||||
int32_t newScale = scale;
|
||||
if (!SafeMul(scale, other.terms_[i].scale, &newScale))
|
||||
return false;
|
||||
if (!add(other.terms_[i].term, newScale))
|
||||
return false;
|
||||
}
|
||||
return add(other.constant_);
|
||||
int32_t newConstant = scale;
|
||||
if (!SafeMul(scale, other.constant_, &newConstant))
|
||||
return false;
|
||||
return add(newConstant);
|
||||
}
|
||||
|
||||
bool
|
||||
@ -2434,6 +2447,129 @@ LinearSum::dump() const
|
||||
dump(stderr);
|
||||
}
|
||||
|
||||
MDefinition *
|
||||
jit::ConvertLinearSum(TempAllocator &alloc, MBasicBlock *block, const LinearSum &sum)
|
||||
{
|
||||
MDefinition *def = nullptr;
|
||||
|
||||
for (size_t i = 0; i < sum.numTerms(); i++) {
|
||||
LinearTerm term = sum.term(i);
|
||||
JS_ASSERT(!term.term->isConstant());
|
||||
if (term.scale == 1) {
|
||||
if (def) {
|
||||
def = MAdd::New(alloc, def, term.term);
|
||||
def->toAdd()->setInt32();
|
||||
block->insertAtEnd(def->toInstruction());
|
||||
def->computeRange(alloc);
|
||||
} else {
|
||||
def = term.term;
|
||||
}
|
||||
} else if (term.scale == -1) {
|
||||
if (!def) {
|
||||
def = MConstant::New(alloc, Int32Value(0));
|
||||
block->insertAtEnd(def->toInstruction());
|
||||
def->computeRange(alloc);
|
||||
}
|
||||
def = MSub::New(alloc, def, term.term);
|
||||
def->toSub()->setInt32();
|
||||
block->insertAtEnd(def->toInstruction());
|
||||
def->computeRange(alloc);
|
||||
} else {
|
||||
JS_ASSERT(term.scale != 0);
|
||||
MConstant *factor = MConstant::New(alloc, Int32Value(term.scale));
|
||||
block->insertAtEnd(factor);
|
||||
MMul *mul = MMul::New(alloc, term.term, factor);
|
||||
mul->setInt32();
|
||||
block->insertAtEnd(mul);
|
||||
mul->computeRange(alloc);
|
||||
if (def) {
|
||||
def = MAdd::New(alloc, def, mul);
|
||||
def->toAdd()->setInt32();
|
||||
block->insertAtEnd(def->toInstruction());
|
||||
def->computeRange(alloc);
|
||||
} else {
|
||||
def = mul;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Note: The constant component of the term is not converted.
|
||||
if (!def) {
|
||||
def = MConstant::New(alloc, Int32Value(0));
|
||||
block->insertAtEnd(def->toInstruction());
|
||||
def->computeRange(alloc);
|
||||
}
|
||||
|
||||
return def;
|
||||
}
|
||||
|
||||
MCompare *
|
||||
jit::ConvertLinearInequality(TempAllocator &alloc, MBasicBlock *block, const LinearSum &sum)
|
||||
{
|
||||
LinearSum lhs(sum);
|
||||
|
||||
// Look for a term with a -1 scale which we can use for the rhs.
|
||||
MDefinition *rhsDef = nullptr;
|
||||
for (size_t i = 0; i < lhs.numTerms(); i++) {
|
||||
if (lhs.term(i).scale == -1) {
|
||||
rhsDef = lhs.term(i).term;
|
||||
lhs.add(rhsDef, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
MDefinition *lhsDef = nullptr;
|
||||
JSOp op = JSOP_GE;
|
||||
|
||||
do {
|
||||
if (!lhs.numTerms()) {
|
||||
lhsDef = MConstant::New(alloc, Int32Value(lhs.constant()));
|
||||
block->insertAtEnd(lhsDef->toInstruction());
|
||||
lhsDef->computeRange(alloc);
|
||||
break;
|
||||
}
|
||||
|
||||
lhsDef = ConvertLinearSum(alloc, block, lhs);
|
||||
if (lhs.constant() == 0)
|
||||
break;
|
||||
|
||||
if (lhs.constant() == -1) {
|
||||
op = JSOP_GT;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!rhsDef) {
|
||||
int32_t constant = lhs.constant();
|
||||
if (SafeMul(constant, -1, &constant)) {
|
||||
rhsDef = MConstant::New(alloc, Int32Value(constant));
|
||||
block->insertAtEnd(rhsDef->toInstruction());
|
||||
rhsDef->computeRange(alloc);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
MDefinition *constant = MConstant::New(alloc, Int32Value(lhs.constant()));
|
||||
block->insertAtEnd(constant->toInstruction());
|
||||
constant->computeRange(alloc);
|
||||
lhsDef = MAdd::New(alloc, lhsDef, constant);
|
||||
lhsDef->toAdd()->setInt32();
|
||||
block->insertAtEnd(lhsDef->toInstruction());
|
||||
lhsDef->computeRange(alloc);
|
||||
} while (false);
|
||||
|
||||
if (!rhsDef) {
|
||||
rhsDef = MConstant::New(alloc, Int32Value(0));
|
||||
block->insertAtEnd(rhsDef->toInstruction());
|
||||
rhsDef->computeRange(alloc);
|
||||
}
|
||||
|
||||
MCompare *compare = MCompare::New(alloc, lhsDef, rhsDef, op);
|
||||
block->insertAtEnd(compare);
|
||||
compare->setCompareType(MCompare::Compare_Int32);
|
||||
|
||||
return compare;
|
||||
}
|
||||
|
||||
static bool
|
||||
AnalyzePoppedThis(JSContext *cx, types::TypeObject *type,
|
||||
MDefinition *thisValue, MInstruction *ins, bool definitelyExecuted,
|
||||
|
@ -62,6 +62,9 @@ AccountForCFGChanges(MIRGenerator *mir, MIRGraph &graph, bool updateAliasAnalysi
|
||||
bool
|
||||
RemoveUnmarkedBlocks(MIRGenerator *mir, MIRGraph &graph, uint32_t numMarkedBlocks);
|
||||
|
||||
void
|
||||
ClearDominatorTree(MIRGraph &graph);
|
||||
|
||||
bool
|
||||
BuildDominatorTree(MIRGraph &graph);
|
||||
|
||||
@ -129,13 +132,14 @@ class LinearSum
|
||||
}
|
||||
|
||||
bool multiply(int32_t scale);
|
||||
bool add(const LinearSum &other);
|
||||
bool add(const LinearSum &other, int32_t scale = 1);
|
||||
bool add(MDefinition *term, int32_t scale);
|
||||
bool add(int32_t constant);
|
||||
|
||||
int32_t constant() const { return constant_; }
|
||||
size_t numTerms() const { return terms_.length(); }
|
||||
LinearTerm term(size_t i) const { return terms_[i]; }
|
||||
void replaceTerm(size_t i, MDefinition *def) { terms_[i].term = def; }
|
||||
|
||||
void print(Sprinter &sp) const;
|
||||
void dump(FILE *) const;
|
||||
@ -146,6 +150,16 @@ class LinearSum
|
||||
int32_t constant_;
|
||||
};
|
||||
|
||||
// Convert all components of a linear sum *except* its constant to a definition,
|
||||
// adding any necessary instructions to the end of block.
|
||||
MDefinition *
|
||||
ConvertLinearSum(TempAllocator &alloc, MBasicBlock *block, const LinearSum &sum);
|
||||
|
||||
// Convert the test 'sum >= 0' to a comparison, adding any necessary
|
||||
// instructions to the end of block.
|
||||
MCompare *
|
||||
ConvertLinearInequality(TempAllocator &alloc, MBasicBlock *block, const LinearSum &sum);
|
||||
|
||||
bool
|
||||
AnalyzeNewScriptProperties(JSContext *cx, JSFunction *fun,
|
||||
types::TypeObject *type, HandleObject baseobj,
|
||||
|
@ -32,6 +32,7 @@ OptimizationInfo::initNormalOptimizationInfo()
|
||||
licm_ = true;
|
||||
uce_ = true;
|
||||
rangeAnalysis_ = true;
|
||||
loopUnrolling_ = true;
|
||||
autoTruncate_ = true;
|
||||
registerAllocator_ = RegisterAllocator_LSRA;
|
||||
|
||||
|
@ -73,6 +73,9 @@ class OptimizationInfo
|
||||
// Toggles whether Range Analysis is used.
|
||||
bool rangeAnalysis_;
|
||||
|
||||
// Toggles whether loop unrolling is performed.
|
||||
bool loopUnrolling_;
|
||||
|
||||
// Toggles whether Truncation based on Range Analysis is used.
|
||||
bool autoTruncate_;
|
||||
|
||||
@ -144,6 +147,10 @@ class OptimizationInfo
|
||||
return rangeAnalysis_ && !js_JitOptions.disableRangeAnalysis;
|
||||
}
|
||||
|
||||
bool loopUnrollingEnabled() const {
|
||||
return loopUnrolling_ && !js_JitOptions.disableLoopUnrolling;
|
||||
}
|
||||
|
||||
bool autoTruncateEnabled() const {
|
||||
return autoTruncate_ && rangeAnalysisEnabled();
|
||||
}
|
||||
|
@ -248,6 +248,7 @@ jit::CheckLogging()
|
||||
" pools Literal Pools (ARM only for now)\n"
|
||||
" cacheflush Instruction Cache flushes (ARM only for now)\n"
|
||||
" range Range Analysis\n"
|
||||
" unroll Loop unrolling\n"
|
||||
" logs C1 and JSON visualization logging\n"
|
||||
" all Everything\n"
|
||||
"\n"
|
||||
@ -277,6 +278,8 @@ jit::CheckLogging()
|
||||
EnableChannel(IonSpew_GVN);
|
||||
if (ContainsFlag(env, "range"))
|
||||
EnableChannel(IonSpew_Range);
|
||||
if (ContainsFlag(env, "unroll"))
|
||||
EnableChannel(IonSpew_Unrolling);
|
||||
if (ContainsFlag(env, "licm"))
|
||||
EnableChannel(IonSpew_LICM);
|
||||
if (ContainsFlag(env, "regalloc"))
|
||||
|
@ -34,6 +34,8 @@ namespace jit {
|
||||
_(GVN) \
|
||||
/* Information during Range analysis */ \
|
||||
_(Range) \
|
||||
/* Information during loop unrolling */ \
|
||||
_(Unrolling) \
|
||||
/* Information during LICM */ \
|
||||
_(LICM) \
|
||||
/* Information during regalloc */ \
|
||||
|
@ -54,6 +54,9 @@ JitOptions::JitOptions()
|
||||
// Toggles whether Range Analysis is globally disabled.
|
||||
disableRangeAnalysis = false;
|
||||
|
||||
// Toggles whether Loop Unrolling is globally disabled.
|
||||
disableLoopUnrolling = true;
|
||||
|
||||
// Toggles whether Unreachable Code Elimination is globally disabled.
|
||||
disableUce = false;
|
||||
|
||||
|
@ -40,6 +40,7 @@ struct JitOptions
|
||||
bool disableInlining;
|
||||
bool disableEdgeCaseAnalysis;
|
||||
bool disableRangeAnalysis;
|
||||
bool disableLoopUnrolling;
|
||||
bool disableUce;
|
||||
bool disableEaa;
|
||||
bool eagerCompilation;
|
||||
|
373
js/src/jit/LoopUnroller.cpp
Normal file
373
js/src/jit/LoopUnroller.cpp
Normal file
@ -0,0 +1,373 @@
|
||||
/* -*- 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/LoopUnroller.h"
|
||||
|
||||
#include "jit/MIRGraph.h"
|
||||
|
||||
using namespace js;
|
||||
using namespace js::jit;
|
||||
|
||||
using mozilla::ArrayLength;
|
||||
|
||||
namespace {
|
||||
|
||||
struct LoopUnroller
|
||||
{
|
||||
typedef HashMap<MDefinition *, MDefinition *,
|
||||
PointerHasher<MDefinition *, 2>, SystemAllocPolicy> DefinitionMap;
|
||||
|
||||
explicit LoopUnroller(MIRGraph &graph)
|
||||
: graph(graph), alloc(graph.alloc())
|
||||
{}
|
||||
|
||||
MIRGraph &graph;
|
||||
TempAllocator &alloc;
|
||||
|
||||
// Header and body of the original loop.
|
||||
MBasicBlock *header, *backedge;
|
||||
|
||||
// Header and body of the unrolled loop.
|
||||
MBasicBlock *unrolledHeader, *unrolledBackedge;
|
||||
|
||||
// Old and new preheaders. The old preheader starts out associated with the
|
||||
// original loop, but becomes the preheader of the new loop. The new
|
||||
// preheader will be given to the original loop.
|
||||
MBasicBlock *oldPreheader, *newPreheader;
|
||||
|
||||
// Map terms in the original loop to terms in the current unrolled iteration.
|
||||
DefinitionMap unrolledDefinitions;
|
||||
|
||||
MDefinition *getReplacementDefinition(MDefinition *def);
|
||||
MResumePoint *makeReplacementResumePoint(MBasicBlock *block, MResumePoint *rp);
|
||||
void makeReplacementInstruction(MInstruction *ins);
|
||||
|
||||
void go(LoopIterationBound *bound);
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
MDefinition *
|
||||
LoopUnroller::getReplacementDefinition(MDefinition *def)
|
||||
{
|
||||
if (def->block()->id() < header->id()) {
|
||||
// The definition is loop invariant.
|
||||
return def;
|
||||
}
|
||||
|
||||
DefinitionMap::Ptr p = unrolledDefinitions.lookup(def);
|
||||
JS_ASSERT(p);
|
||||
|
||||
return p->value();
|
||||
}
|
||||
|
||||
void
|
||||
LoopUnroller::makeReplacementInstruction(MInstruction *ins)
|
||||
{
|
||||
MDefinitionVector inputs(alloc);
|
||||
for (size_t i = 0; i < ins->numOperands(); i++) {
|
||||
MDefinition *old = ins->getOperand(i);
|
||||
MDefinition *replacement = getReplacementDefinition(old);
|
||||
if (!inputs.append(replacement))
|
||||
CrashAtUnhandlableOOM("LoopUnroller::makeReplacementDefinition");
|
||||
}
|
||||
|
||||
MInstruction *clone = ins->clone(alloc, inputs);
|
||||
|
||||
unrolledBackedge->add(clone);
|
||||
|
||||
if (!unrolledDefinitions.putNew(ins, clone))
|
||||
CrashAtUnhandlableOOM("LoopUnroller::makeReplacementDefinition");
|
||||
|
||||
if (MResumePoint *old = ins->resumePoint()) {
|
||||
MResumePoint *rp = makeReplacementResumePoint(unrolledBackedge, old);
|
||||
clone->setResumePoint(rp);
|
||||
}
|
||||
}
|
||||
|
||||
MResumePoint *
|
||||
LoopUnroller::makeReplacementResumePoint(MBasicBlock *block, MResumePoint *rp)
|
||||
{
|
||||
MDefinitionVector inputs(alloc);
|
||||
for (size_t i = 0; i < rp->stackDepth(); i++) {
|
||||
MDefinition *old = rp->getOperand(i);
|
||||
MDefinition *replacement = old->isUnused() ? old : getReplacementDefinition(old);
|
||||
if (!inputs.append(replacement))
|
||||
CrashAtUnhandlableOOM("LoopUnroller::makeReplacementResumePoint");
|
||||
}
|
||||
|
||||
MResumePoint *clone = MResumePoint::New(alloc, block, rp->pc(), rp->caller(), rp->mode(), inputs);
|
||||
if (!clone)
|
||||
CrashAtUnhandlableOOM("LoopUnroller::makeReplacementResumePoint");
|
||||
|
||||
return clone;
|
||||
}
|
||||
|
||||
void
|
||||
LoopUnroller::go(LoopIterationBound *bound)
|
||||
{
|
||||
// For now we always unroll loops the same number of times.
|
||||
static const size_t UnrollCount = 10;
|
||||
|
||||
IonSpew(IonSpew_Unrolling, "Attempting to unroll loop");
|
||||
|
||||
header = bound->header;
|
||||
JS_ASSERT(header->isLoopHeader());
|
||||
|
||||
backedge = header->backedge();
|
||||
oldPreheader = header->loopPredecessor();
|
||||
|
||||
JS_ASSERT(oldPreheader->numSuccessors() == 1);
|
||||
|
||||
// Only unroll loops with two blocks: an initial one ending with the
|
||||
// bound's test, and the body ending with the backedge.
|
||||
MTest *test = bound->test;
|
||||
if (header->lastIns() != test)
|
||||
return;
|
||||
if (test->ifTrue() == backedge) {
|
||||
if (test->ifFalse()->id() <= backedge->id())
|
||||
return;
|
||||
} else if (test->ifFalse() == backedge) {
|
||||
if (test->ifTrue()->id() <= backedge->id())
|
||||
return;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
if (backedge->numPredecessors() != 1 || backedge->numSuccessors() != 1)
|
||||
return;
|
||||
JS_ASSERT(backedge->phisEmpty());
|
||||
|
||||
MBasicBlock *bodyBlocks[] = { header, backedge };
|
||||
|
||||
// All instructions in the header and body must be clonable.
|
||||
for (size_t i = 0; i < ArrayLength(bodyBlocks); i++) {
|
||||
MBasicBlock *block = bodyBlocks[i];
|
||||
for (MInstructionIterator iter(block->begin()); iter != block->end(); iter++) {
|
||||
MInstruction *ins = *iter;
|
||||
if (ins->canClone())
|
||||
continue;
|
||||
if (ins->isTest() || ins->isGoto() || ins->isInterruptCheck())
|
||||
continue;
|
||||
IonSpew(IonSpew_Unrolling, "Aborting: can't clone instruction %s", ins->opName());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Compute the linear inequality we will use for exiting the unrolled loop:
|
||||
//
|
||||
// iterationBound - iterationCount - UnrollCount >= 0
|
||||
//
|
||||
LinearSum remainingIterationsInequality(bound->boundSum);
|
||||
if (!remainingIterationsInequality.add(bound->currentSum, -1))
|
||||
return;
|
||||
if (!remainingIterationsInequality.add(-int32_t(UnrollCount)))
|
||||
return;
|
||||
|
||||
// Terms in the inequality need to be either loop invariant or phis from
|
||||
// the original header.
|
||||
for (size_t i = 0; i < remainingIterationsInequality.numTerms(); i++) {
|
||||
MDefinition *def = remainingIterationsInequality.term(i).term;
|
||||
if (def->block()->id() < header->id())
|
||||
continue;
|
||||
if (def->block() == header && def->isPhi())
|
||||
continue;
|
||||
return;
|
||||
}
|
||||
|
||||
// OK, we've checked everything, now unroll the loop.
|
||||
|
||||
IonSpew(IonSpew_Unrolling, "Unrolling loop");
|
||||
|
||||
// The old preheader will go before the unrolled loop, and the old loop
|
||||
// will need a new empty preheader.
|
||||
CompileInfo &info = oldPreheader->info();
|
||||
if (header->trackedSite().pc()) {
|
||||
unrolledHeader =
|
||||
MBasicBlock::New(graph, nullptr, info,
|
||||
oldPreheader, header->trackedSite(), MBasicBlock::LOOP_HEADER);
|
||||
unrolledBackedge =
|
||||
MBasicBlock::New(graph, nullptr, info,
|
||||
unrolledHeader, backedge->trackedSite(), MBasicBlock::NORMAL);
|
||||
newPreheader =
|
||||
MBasicBlock::New(graph, nullptr, info,
|
||||
unrolledHeader, oldPreheader->trackedSite(), MBasicBlock::NORMAL);
|
||||
} else {
|
||||
unrolledHeader = MBasicBlock::NewAsmJS(graph, info, oldPreheader, MBasicBlock::LOOP_HEADER);
|
||||
unrolledBackedge = MBasicBlock::NewAsmJS(graph, info, unrolledHeader, MBasicBlock::NORMAL);
|
||||
newPreheader = MBasicBlock::NewAsmJS(graph, info, unrolledHeader, MBasicBlock::NORMAL);
|
||||
}
|
||||
|
||||
unrolledHeader->discardAllResumePoints();
|
||||
unrolledBackedge->discardAllResumePoints();
|
||||
newPreheader->discardAllResumePoints();
|
||||
|
||||
// Insert new blocks at their RPO position, and update block ids.
|
||||
graph.insertBlockAfter(oldPreheader, unrolledHeader);
|
||||
graph.insertBlockAfter(unrolledHeader, unrolledBackedge);
|
||||
graph.insertBlockAfter(unrolledBackedge, newPreheader);
|
||||
graph.renumberBlocksAfter(oldPreheader);
|
||||
|
||||
if (!unrolledDefinitions.init())
|
||||
CrashAtUnhandlableOOM("LoopUnroller::go");
|
||||
|
||||
// Add phis to the unrolled loop header which correspond to the phis in the
|
||||
// original loop header.
|
||||
JS_ASSERT(header->getPredecessor(0) == oldPreheader);
|
||||
for (MPhiIterator iter(header->phisBegin()); iter != header->phisEnd(); iter++) {
|
||||
MPhi *old = *iter;
|
||||
JS_ASSERT(old->numOperands() == 2);
|
||||
MPhi *phi = MPhi::New(alloc);
|
||||
phi->setResultType(old->type());
|
||||
phi->setResultTypeSet(old->resultTypeSet());
|
||||
phi->setRange(old->range());
|
||||
|
||||
unrolledHeader->addPhi(phi);
|
||||
|
||||
if (!phi->reserveLength(2))
|
||||
CrashAtUnhandlableOOM("LoopUnroller::go");
|
||||
|
||||
// Set the first input for the phi for now. We'll set the second after
|
||||
// finishing the unroll.
|
||||
phi->addInput(old->getOperand(0));
|
||||
|
||||
// The old phi will now take the value produced by the unrolled loop.
|
||||
old->replaceOperand(0, phi);
|
||||
|
||||
if (!unrolledDefinitions.putNew(old, phi))
|
||||
CrashAtUnhandlableOOM("LoopUnroller::go");
|
||||
}
|
||||
|
||||
// The loop condition can bail out on e.g. integer overflow, so make a
|
||||
// resume point based on the initial resume point of the original header.
|
||||
MResumePoint *headerResumePoint = header->entryResumePoint();
|
||||
if (headerResumePoint) {
|
||||
MResumePoint *rp = makeReplacementResumePoint(unrolledHeader, headerResumePoint);
|
||||
unrolledHeader->setEntryResumePoint(rp);
|
||||
|
||||
// Perform an interrupt check at the start of the unrolled loop.
|
||||
unrolledHeader->add(MInterruptCheck::New(alloc));
|
||||
}
|
||||
|
||||
// Generate code for the test in the unrolled loop.
|
||||
for (size_t i = 0; i < remainingIterationsInequality.numTerms(); i++) {
|
||||
MDefinition *def = remainingIterationsInequality.term(i).term;
|
||||
MDefinition *replacement = getReplacementDefinition(def);
|
||||
remainingIterationsInequality.replaceTerm(i, replacement);
|
||||
}
|
||||
MCompare *compare = ConvertLinearInequality(alloc, unrolledHeader, remainingIterationsInequality);
|
||||
MTest *unrolledTest = MTest::New(alloc, compare, unrolledBackedge, newPreheader);
|
||||
unrolledHeader->end(unrolledTest);
|
||||
|
||||
// Make an entry resume point for the unrolled body. The unrolled header
|
||||
// does not have side effects on stack values, even if the original loop
|
||||
// header does, so use the same resume point as for the unrolled header.
|
||||
if (headerResumePoint) {
|
||||
MResumePoint *rp = makeReplacementResumePoint(unrolledBackedge, headerResumePoint);
|
||||
unrolledBackedge->setEntryResumePoint(rp);
|
||||
}
|
||||
|
||||
// Make an entry resume point for the new preheader. There are no
|
||||
// instructions which use this but some other stuff wants one to be here.
|
||||
if (headerResumePoint) {
|
||||
MResumePoint *rp = makeReplacementResumePoint(newPreheader, headerResumePoint);
|
||||
newPreheader->setEntryResumePoint(rp);
|
||||
}
|
||||
|
||||
// Generate the unrolled code.
|
||||
JS_ASSERT(UnrollCount > 1);
|
||||
size_t unrollIndex = 0;
|
||||
while (true) {
|
||||
// Clone the contents of the original loop into the unrolled loop body.
|
||||
for (size_t i = 0; i < ArrayLength(bodyBlocks); i++) {
|
||||
MBasicBlock *block = bodyBlocks[i];
|
||||
for (MInstructionIterator iter(block->begin()); iter != block->end(); iter++) {
|
||||
MInstruction *ins = *iter;
|
||||
if (ins->canClone()) {
|
||||
makeReplacementInstruction(*iter);
|
||||
} else {
|
||||
// Control instructions are handled separately.
|
||||
JS_ASSERT(ins->isTest() || ins->isGoto() || ins->isInterruptCheck());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Compute the value of each loop header phi after the execution of
|
||||
// this unrolled iteration.
|
||||
MDefinitionVector phiValues(alloc);
|
||||
JS_ASSERT(header->getPredecessor(1) == backedge);
|
||||
for (MPhiIterator iter(header->phisBegin()); iter != header->phisEnd(); iter++) {
|
||||
MPhi *old = *iter;
|
||||
MDefinition *oldInput = old->getOperand(1);
|
||||
if (!phiValues.append(getReplacementDefinition(oldInput)))
|
||||
CrashAtUnhandlableOOM("LoopUnroller::go");
|
||||
}
|
||||
|
||||
unrolledDefinitions.clear();
|
||||
|
||||
if (unrollIndex == UnrollCount - 1) {
|
||||
// We're at the end of the last unrolled iteration, set the
|
||||
// backedge input for the unrolled loop phis.
|
||||
size_t phiIndex = 0;
|
||||
for (MPhiIterator iter(unrolledHeader->phisBegin()); iter != unrolledHeader->phisEnd(); iter++) {
|
||||
MPhi *phi = *iter;
|
||||
phi->addInput(phiValues[phiIndex++]);
|
||||
}
|
||||
JS_ASSERT(phiIndex == phiValues.length());
|
||||
break;
|
||||
}
|
||||
|
||||
// Update the map for the phis in the next iteration.
|
||||
size_t phiIndex = 0;
|
||||
for (MPhiIterator iter(header->phisBegin()); iter != header->phisEnd(); iter++) {
|
||||
MPhi *old = *iter;
|
||||
if (!unrolledDefinitions.putNew(old, phiValues[phiIndex++]))
|
||||
CrashAtUnhandlableOOM("LoopUnroller::go");
|
||||
}
|
||||
JS_ASSERT(phiIndex == phiValues.length());
|
||||
|
||||
unrollIndex++;
|
||||
}
|
||||
|
||||
MGoto *backedgeJump = MGoto::New(alloc, unrolledHeader);
|
||||
unrolledBackedge->end(backedgeJump);
|
||||
|
||||
// Place the old preheader before the unrolled loop.
|
||||
JS_ASSERT(oldPreheader->lastIns()->isGoto());
|
||||
oldPreheader->discardLastIns();
|
||||
oldPreheader->end(MGoto::New(alloc, unrolledHeader));
|
||||
|
||||
// Place the new preheader before the original loop.
|
||||
newPreheader->end(MGoto::New(alloc, header));
|
||||
|
||||
// Cleanup the MIR graph.
|
||||
if (!unrolledHeader->addPredecessorWithoutPhis(unrolledBackedge))
|
||||
CrashAtUnhandlableOOM("LoopUnroller::go");
|
||||
header->replacePredecessor(oldPreheader, newPreheader);
|
||||
oldPreheader->setSuccessorWithPhis(unrolledHeader, 0);
|
||||
newPreheader->setSuccessorWithPhis(header, 0);
|
||||
unrolledBackedge->setSuccessorWithPhis(unrolledHeader, 1);
|
||||
}
|
||||
|
||||
bool
|
||||
jit::UnrollLoops(MIRGraph &graph, const LoopIterationBoundVector &bounds)
|
||||
{
|
||||
if (bounds.empty())
|
||||
return true;
|
||||
|
||||
for (size_t i = 0; i < bounds.length(); i++) {
|
||||
LoopUnroller unroller(graph);
|
||||
unroller.go(bounds[i]);
|
||||
}
|
||||
|
||||
// The MIR graph is valid, but now has several new blocks. Update or
|
||||
// recompute previous analysis information for the remaining optimization
|
||||
// passes.
|
||||
ClearDominatorTree(graph);
|
||||
if (!BuildDominatorTree(graph))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
21
js/src/jit/LoopUnroller.h
Normal file
21
js/src/jit/LoopUnroller.h
Normal file
@ -0,0 +1,21 @@
|
||||
/* -*- 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/. */
|
||||
|
||||
#ifndef jit_LoopUnroller_h
|
||||
#define jit_LoopUnroller_h
|
||||
|
||||
#include "jit/RangeAnalysis.h"
|
||||
|
||||
namespace js {
|
||||
namespace jit {
|
||||
|
||||
bool
|
||||
UnrollLoops(MIRGraph &graph, const LoopIterationBoundVector &bounds);
|
||||
|
||||
} // namespace jit
|
||||
} // namespace js
|
||||
|
||||
#endif // jit_LoopUnroller_h
|
@ -2351,6 +2351,20 @@ MResumePoint::New(TempAllocator &alloc, MBasicBlock *block, jsbytecode *pc, MRes
|
||||
return resume;
|
||||
}
|
||||
|
||||
MResumePoint *
|
||||
MResumePoint::New(TempAllocator &alloc, MBasicBlock *block, jsbytecode *pc, MResumePoint *parent,
|
||||
Mode mode, const MDefinitionVector &operands)
|
||||
{
|
||||
MResumePoint *resume = new(alloc) MResumePoint(block, pc, parent, mode);
|
||||
|
||||
if (!resume->operands_.init(alloc, operands.length()))
|
||||
return nullptr;
|
||||
for (size_t i = 0; i < operands.length(); i++)
|
||||
resume->initOperand(i, operands[i]);
|
||||
|
||||
return resume;
|
||||
}
|
||||
|
||||
MResumePoint::MResumePoint(MBasicBlock *block, jsbytecode *pc, MResumePoint *caller,
|
||||
Mode mode)
|
||||
: MNode(block),
|
||||
|
192
js/src/jit/MIR.h
192
js/src/jit/MIR.h
@ -138,6 +138,13 @@ class MUse : public TempObject, public InlineListNode<MUse>
|
||||
: producer_(nullptr), consumer_(nullptr)
|
||||
{ }
|
||||
|
||||
// MUses can only be copied when they are not in a use list.
|
||||
explicit MUse(const MUse &other)
|
||||
: producer_(other.producer_), consumer_(other.consumer_)
|
||||
{
|
||||
JS_ASSERT(!other.next && !other.prev);
|
||||
}
|
||||
|
||||
// Set this use, which was previously clear.
|
||||
inline void init(MDefinition *producer, MNode *consumer);
|
||||
// Like init, but works even when the use contains uninitialized data.
|
||||
@ -386,6 +393,17 @@ class MDefinition : public MNode
|
||||
trackedSite_()
|
||||
{ }
|
||||
|
||||
// Copying a definition leaves the list of uses and the block empty.
|
||||
explicit MDefinition(const MDefinition &other)
|
||||
: id_(0),
|
||||
flags_(other.flags_),
|
||||
range_(other.range_),
|
||||
resultType_(other.resultType_),
|
||||
resultTypeSet_(other.resultTypeSet_),
|
||||
dependency_(other.dependency_),
|
||||
trackedSite_(other.trackedSite_)
|
||||
{ }
|
||||
|
||||
virtual Opcode op() const = 0;
|
||||
virtual const char *opName() const = 0;
|
||||
virtual bool accept(MDefinitionVisitor *visitor) = 0;
|
||||
@ -766,6 +784,8 @@ class MUseDefIterator
|
||||
}
|
||||
};
|
||||
|
||||
typedef Vector<MDefinition *, 8, IonAllocPolicy> MDefinitionVector;
|
||||
|
||||
// An instruction is an SSA name that is inserted into a basic block's IR
|
||||
// stream.
|
||||
class MInstruction
|
||||
@ -779,6 +799,12 @@ class MInstruction
|
||||
: resumePoint_(nullptr)
|
||||
{ }
|
||||
|
||||
// Copying an instruction leaves the block and resume point as empty.
|
||||
explicit MInstruction(const MInstruction &other)
|
||||
: MDefinition(other),
|
||||
resumePoint_(nullptr)
|
||||
{ }
|
||||
|
||||
void setResumePoint(MResumePoint *resumePoint) {
|
||||
JS_ASSERT(!resumePoint_);
|
||||
resumePoint_ = resumePoint;
|
||||
@ -791,6 +817,17 @@ class MInstruction
|
||||
MResumePoint *resumePoint() const {
|
||||
return resumePoint_;
|
||||
}
|
||||
|
||||
// For instructions which can be cloned with new inputs, with all other
|
||||
// information being the same. clone() implementations do not need to worry
|
||||
// about cloning generic MInstruction/MDefinition state like flags and
|
||||
// resume points.
|
||||
virtual bool canClone() const {
|
||||
return false;
|
||||
}
|
||||
virtual MInstruction *clone(TempAllocator &alloc, const MDefinitionVector &inputs) const {
|
||||
MOZ_CRASH();
|
||||
}
|
||||
};
|
||||
|
||||
#define INSTRUCTION_HEADER(opcode) \
|
||||
@ -804,6 +841,18 @@ class MInstruction
|
||||
return visitor->visit##opcode(this); \
|
||||
}
|
||||
|
||||
#define ALLOW_CLONE(typename) \
|
||||
bool canClone() const { \
|
||||
return true; \
|
||||
} \
|
||||
MInstruction *clone(TempAllocator &alloc, \
|
||||
const MDefinitionVector &inputs) const { \
|
||||
MInstruction *res = new(alloc) typename(*this); \
|
||||
for (size_t i = 0; i < numOperands(); i++) \
|
||||
res->replaceOperand(i, inputs[i]); \
|
||||
return res; \
|
||||
}
|
||||
|
||||
template <size_t Arity>
|
||||
class MAryInstruction : public MInstruction
|
||||
{
|
||||
@ -835,6 +884,15 @@ class MAryInstruction : public MInstruction
|
||||
void replaceOperand(size_t index, MDefinition *operand) MOZ_FINAL MOZ_OVERRIDE {
|
||||
operands_[index].replaceProducer(operand);
|
||||
}
|
||||
|
||||
MAryInstruction() { }
|
||||
|
||||
explicit MAryInstruction(const MAryInstruction<Arity> &other)
|
||||
: MInstruction(other)
|
||||
{
|
||||
for (int i = 0; i < (int) Arity; i++) // N.B. use |int| to avoid warnings when Arity == 0
|
||||
operands_[i].init(other.operands_[i].producer(), this);
|
||||
}
|
||||
};
|
||||
|
||||
class MNullaryInstruction : public MAryInstruction<0>
|
||||
@ -1070,6 +1128,8 @@ class MNop : public MNullaryInstruction
|
||||
AliasSet getAliasSet() const {
|
||||
return AliasSet::None();
|
||||
}
|
||||
|
||||
ALLOW_CLONE(MNop)
|
||||
};
|
||||
|
||||
// Truncation barrier. This is intended for protecting its input against
|
||||
@ -1169,6 +1229,8 @@ class MConstant : public MNullaryInstruction
|
||||
bool truncate(TruncateKind kind);
|
||||
|
||||
bool canProduceFloat32() const;
|
||||
|
||||
ALLOW_CLONE(MConstant)
|
||||
};
|
||||
|
||||
// Deep clone a constant JSObject.
|
||||
@ -2785,6 +2847,8 @@ class MCompare
|
||||
}
|
||||
# endif
|
||||
|
||||
ALLOW_CLONE(MCompare)
|
||||
|
||||
protected:
|
||||
bool congruentTo(const MDefinition *ins) const {
|
||||
if (!binaryCongruentTo(ins))
|
||||
@ -2828,6 +2892,8 @@ class MBox : public MUnaryInstruction
|
||||
AliasSet getAliasSet() const {
|
||||
return AliasSet::None();
|
||||
}
|
||||
|
||||
ALLOW_CLONE(MBox)
|
||||
};
|
||||
|
||||
// Note: the op may have been inverted during lowering (to put constants in a
|
||||
@ -2945,6 +3011,8 @@ class MUnbox : public MUnaryInstruction, public BoxInputsPolicy
|
||||
JS_ASSERT(mode() != Fallible);
|
||||
mode_ = Infallible;
|
||||
}
|
||||
|
||||
ALLOW_CLONE(MUnbox)
|
||||
};
|
||||
|
||||
class MGuardObject : public MUnaryInstruction, public SingleObjectPolicy
|
||||
@ -3388,6 +3456,8 @@ class MToDouble
|
||||
void setTruncateKind(TruncateKind kind) {
|
||||
implicitTruncate_ = Max(implicitTruncate_, kind);
|
||||
}
|
||||
|
||||
ALLOW_CLONE(MToDouble)
|
||||
};
|
||||
|
||||
// Converts a primitive (either typed or untyped) to a float32. If the input is
|
||||
@ -3451,6 +3521,8 @@ class MToFloat32
|
||||
|
||||
bool canConsumeFloat32(MUse *use) const { return true; }
|
||||
bool canProduceFloat32() const { return true; }
|
||||
|
||||
ALLOW_CLONE(MToFloat32)
|
||||
};
|
||||
|
||||
// Converts a uint32 to a double (coming from asm.js).
|
||||
@ -3572,6 +3644,8 @@ class MToInt32
|
||||
#ifdef DEBUG
|
||||
bool isConsistentFloat32Use(MUse *use) const { return true; }
|
||||
#endif
|
||||
|
||||
ALLOW_CLONE(MToInt32)
|
||||
};
|
||||
|
||||
// Converts a value or typed input to a truncated int32, for use with bitwise
|
||||
@ -3614,6 +3688,8 @@ class MTruncateToInt32 : public MUnaryInstruction
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
ALLOW_CLONE(MTruncateToInt32)
|
||||
};
|
||||
|
||||
// Converts any type to a string
|
||||
@ -3652,6 +3728,8 @@ class MToString :
|
||||
bool fallible() const {
|
||||
return input()->mightBeType(MIRType_Object);
|
||||
}
|
||||
|
||||
ALLOW_CLONE(MToString)
|
||||
};
|
||||
|
||||
class MBitNot
|
||||
@ -3692,6 +3770,8 @@ class MBitNot
|
||||
bool canRecoverOnBailout() const {
|
||||
return specialization_ != MIRType_None;
|
||||
}
|
||||
|
||||
ALLOW_CLONE(MBitNot)
|
||||
};
|
||||
|
||||
class MTypeOf
|
||||
@ -3837,6 +3917,8 @@ class MBitAnd : public MBinaryBitwiseInstruction
|
||||
bool canRecoverOnBailout() const {
|
||||
return specialization_ != MIRType_None;
|
||||
}
|
||||
|
||||
ALLOW_CLONE(MBitAnd)
|
||||
};
|
||||
|
||||
class MBitOr : public MBinaryBitwiseInstruction
|
||||
@ -3864,6 +3946,8 @@ class MBitOr : public MBinaryBitwiseInstruction
|
||||
bool canRecoverOnBailout() const {
|
||||
return specialization_ != MIRType_None;
|
||||
}
|
||||
|
||||
ALLOW_CLONE(MBitOr)
|
||||
};
|
||||
|
||||
class MBitXor : public MBinaryBitwiseInstruction
|
||||
@ -3892,6 +3976,8 @@ class MBitXor : public MBinaryBitwiseInstruction
|
||||
bool canRecoverOnBailout() const {
|
||||
return specialization_ < MIRType_Object;
|
||||
}
|
||||
|
||||
ALLOW_CLONE(MBitXor)
|
||||
};
|
||||
|
||||
class MShiftInstruction
|
||||
@ -3934,6 +4020,8 @@ class MLsh : public MShiftInstruction
|
||||
bool canRecoverOnBailout() const {
|
||||
return specialization_ != MIRType_None;
|
||||
}
|
||||
|
||||
ALLOW_CLONE(MLsh)
|
||||
};
|
||||
|
||||
class MRsh : public MShiftInstruction
|
||||
@ -3958,6 +4046,8 @@ class MRsh : public MShiftInstruction
|
||||
bool canRecoverOnBailout() const {
|
||||
return specialization_ < MIRType_Object;
|
||||
}
|
||||
|
||||
ALLOW_CLONE(MRsh)
|
||||
};
|
||||
|
||||
class MUrsh : public MShiftInstruction
|
||||
@ -3997,6 +4087,8 @@ class MUrsh : public MShiftInstruction
|
||||
bool canRecoverOnBailout() const {
|
||||
return specialization_ < MIRType_Object;
|
||||
}
|
||||
|
||||
ALLOW_CLONE(MUrsh)
|
||||
};
|
||||
|
||||
class MBinaryArithInstruction
|
||||
@ -4114,6 +4206,8 @@ class MMinMax
|
||||
bool canRecoverOnBailout() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
ALLOW_CLONE(MMinMax)
|
||||
};
|
||||
|
||||
class MAbs
|
||||
@ -4162,6 +4256,8 @@ class MAbs
|
||||
bool canRecoverOnBailout() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
ALLOW_CLONE(MAbs)
|
||||
};
|
||||
|
||||
// Inline implementation of Math.sqrt().
|
||||
@ -4205,6 +4301,8 @@ class MSqrt
|
||||
bool canRecoverOnBailout() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
ALLOW_CLONE(MSqrt)
|
||||
};
|
||||
|
||||
// Inline implementation of atan2 (arctangent of y/x).
|
||||
@ -4253,6 +4351,8 @@ class MAtan2
|
||||
bool canRecoverOnBailout() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
ALLOW_CLONE(MAtan2)
|
||||
};
|
||||
|
||||
// Inline implementation of Math.hypot().
|
||||
@ -4296,6 +4396,8 @@ class MHypot
|
||||
bool possiblyCalls() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
ALLOW_CLONE(MHypot)
|
||||
};
|
||||
|
||||
// Inline implementation of Math.pow().
|
||||
@ -4342,6 +4444,8 @@ class MPow
|
||||
bool canRecoverOnBailout() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
ALLOW_CLONE(MPow)
|
||||
};
|
||||
|
||||
// Inline implementation of Math.pow(x, 0.5), which subtly differs from Math.sqrt(x).
|
||||
@ -4391,6 +4495,8 @@ class MPowHalf
|
||||
bool canRecoverOnBailout() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
ALLOW_CLONE(MPowHalf)
|
||||
};
|
||||
|
||||
// Inline implementation of Math.random().
|
||||
@ -4416,6 +4522,8 @@ class MRandom : public MNullaryInstruction
|
||||
}
|
||||
|
||||
void computeRange(TempAllocator &alloc);
|
||||
|
||||
ALLOW_CLONE(MRandom)
|
||||
};
|
||||
|
||||
class MMathFunction
|
||||
@ -4509,6 +4617,8 @@ class MMathFunction
|
||||
bool canRecoverOnBailout() const {
|
||||
return function_ == Round;
|
||||
}
|
||||
|
||||
ALLOW_CLONE(MMathFunction)
|
||||
};
|
||||
|
||||
class MAdd : public MBinaryArithInstruction
|
||||
@ -4554,6 +4664,8 @@ class MAdd : public MBinaryArithInstruction
|
||||
bool canRecoverOnBailout() const {
|
||||
return specialization_ < MIRType_Object;
|
||||
}
|
||||
|
||||
ALLOW_CLONE(MAdd)
|
||||
};
|
||||
|
||||
class MSub : public MBinaryArithInstruction
|
||||
@ -4595,6 +4707,8 @@ class MSub : public MBinaryArithInstruction
|
||||
bool canRecoverOnBailout() const {
|
||||
return specialization_ < MIRType_Object;
|
||||
}
|
||||
|
||||
ALLOW_CLONE(MSub)
|
||||
};
|
||||
|
||||
class MMul : public MBinaryArithInstruction
|
||||
@ -4696,6 +4810,8 @@ class MMul : public MBinaryArithInstruction
|
||||
bool canRecoverOnBailout() const {
|
||||
return specialization_ < MIRType_Object;
|
||||
}
|
||||
|
||||
ALLOW_CLONE(MMul)
|
||||
};
|
||||
|
||||
class MDiv : public MBinaryArithInstruction
|
||||
@ -4797,6 +4913,8 @@ class MDiv : public MBinaryArithInstruction
|
||||
bool canRecoverOnBailout() const {
|
||||
return specialization_ < MIRType_Object;
|
||||
}
|
||||
|
||||
ALLOW_CLONE(MDiv)
|
||||
};
|
||||
|
||||
class MMod : public MBinaryArithInstruction
|
||||
@ -4871,6 +4989,8 @@ class MMod : public MBinaryArithInstruction
|
||||
bool truncate(TruncateKind kind);
|
||||
void collectRangeInfoPreTrunc();
|
||||
TruncateKind operandTruncateKind(size_t index) const;
|
||||
|
||||
ALLOW_CLONE(MMod)
|
||||
};
|
||||
|
||||
class MConcat
|
||||
@ -4908,6 +5028,7 @@ class MConcat
|
||||
return true;
|
||||
}
|
||||
|
||||
ALLOW_CLONE(MConcat)
|
||||
};
|
||||
|
||||
class MConcatPar
|
||||
@ -4986,6 +5107,8 @@ class MCharCodeAt
|
||||
bool canRecoverOnBailout() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
ALLOW_CLONE(MCharCodeAt)
|
||||
};
|
||||
|
||||
class MFromCharCode
|
||||
@ -5020,6 +5143,8 @@ class MFromCharCode
|
||||
bool canRecoverOnBailout() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
ALLOW_CLONE(MFromCharCode)
|
||||
};
|
||||
|
||||
class MStringSplit
|
||||
@ -5960,6 +6085,8 @@ class MSlots
|
||||
AliasSet getAliasSet() const {
|
||||
return AliasSet::Load(AliasSet::ObjectFields);
|
||||
}
|
||||
|
||||
ALLOW_CLONE(MSlots)
|
||||
};
|
||||
|
||||
// Returns obj->elements.
|
||||
@ -5993,6 +6120,8 @@ class MElements
|
||||
AliasSet getAliasSet() const {
|
||||
return AliasSet::Load(AliasSet::ObjectFields);
|
||||
}
|
||||
|
||||
ALLOW_CLONE(MElements)
|
||||
};
|
||||
|
||||
// A constant value for some object's array elements or typed array elements.
|
||||
@ -6031,6 +6160,8 @@ class MConstantElements : public MNullaryInstruction
|
||||
AliasSet getAliasSet() const {
|
||||
return AliasSet::None();
|
||||
}
|
||||
|
||||
ALLOW_CLONE(MConstantElements)
|
||||
};
|
||||
|
||||
// Passes through an object's elements, after ensuring it is entirely doubles.
|
||||
@ -6140,6 +6271,8 @@ class MInitializedLength
|
||||
}
|
||||
|
||||
void computeRange(TempAllocator &alloc);
|
||||
|
||||
ALLOW_CLONE(MInitializedLength)
|
||||
};
|
||||
|
||||
// Store to the initialized length in an elements header. Note the input is an
|
||||
@ -6168,6 +6301,8 @@ class MSetInitializedLength
|
||||
AliasSet getAliasSet() const {
|
||||
return AliasSet::Store(AliasSet::ObjectFields);
|
||||
}
|
||||
|
||||
ALLOW_CLONE(MSetInitializedLength)
|
||||
};
|
||||
|
||||
// Load the array length from an elements header.
|
||||
@ -6199,6 +6334,8 @@ class MArrayLength
|
||||
}
|
||||
|
||||
void computeRange(TempAllocator &alloc);
|
||||
|
||||
ALLOW_CLONE(MArrayLength)
|
||||
};
|
||||
|
||||
// Store to the length in an elements header. Note the input is an *index*, one
|
||||
@ -6295,6 +6432,8 @@ class MTypedArrayElements
|
||||
AliasSet getAliasSet() const {
|
||||
return AliasSet::Load(AliasSet::ObjectFields);
|
||||
}
|
||||
|
||||
ALLOW_CLONE(MTypedArrayElements)
|
||||
};
|
||||
|
||||
// Checks whether a typed object is neutered.
|
||||
@ -6531,6 +6670,8 @@ class MBoundsCheck
|
||||
return AliasSet::None();
|
||||
}
|
||||
void computeRange(TempAllocator &alloc);
|
||||
|
||||
ALLOW_CLONE(MBoundsCheck)
|
||||
};
|
||||
|
||||
// Bailout if index < minimum.
|
||||
@ -6639,6 +6780,8 @@ class MLoadElement
|
||||
AliasSet getAliasSet() const {
|
||||
return AliasSet::Load(AliasSet::Element);
|
||||
}
|
||||
|
||||
ALLOW_CLONE(MLoadElement)
|
||||
};
|
||||
|
||||
// Load a value from a dense array's element vector. If the index is
|
||||
@ -6703,6 +6846,8 @@ class MLoadElementHole
|
||||
return AliasSet::Load(AliasSet::Element);
|
||||
}
|
||||
void collectRangeInfoPreTrunc();
|
||||
|
||||
ALLOW_CLONE(MLoadElementHole)
|
||||
};
|
||||
|
||||
class MStoreElementCommon
|
||||
@ -6785,6 +6930,8 @@ class MStoreElement
|
||||
bool fallible() const {
|
||||
return needsHoleCheck();
|
||||
}
|
||||
|
||||
ALLOW_CLONE(MStoreElement)
|
||||
};
|
||||
|
||||
// Like MStoreElement, but supports indexes >= initialized length. The downside
|
||||
@ -6834,6 +6981,8 @@ class MStoreElementHole
|
||||
// or reallocate obj->elements.
|
||||
return AliasSet::Store(AliasSet::Element | AliasSet::ObjectFields);
|
||||
}
|
||||
|
||||
ALLOW_CLONE(MStoreElementHole)
|
||||
};
|
||||
|
||||
// Array.prototype.pop or Array.prototype.shift on a dense array.
|
||||
@ -6884,6 +7033,8 @@ class MArrayPopShift
|
||||
AliasSet getAliasSet() const {
|
||||
return AliasSet::Store(AliasSet::Element | AliasSet::ObjectFields);
|
||||
}
|
||||
|
||||
ALLOW_CLONE(MArrayPopShift)
|
||||
};
|
||||
|
||||
// Array.prototype.push on a dense array. Returns the new array length.
|
||||
@ -6917,6 +7068,8 @@ class MArrayPush
|
||||
return AliasSet::Store(AliasSet::Element | AliasSet::ObjectFields);
|
||||
}
|
||||
void computeRange(TempAllocator &alloc);
|
||||
|
||||
ALLOW_CLONE(MArrayPush)
|
||||
};
|
||||
|
||||
// Array.prototype.concat on two dense arrays.
|
||||
@ -7022,6 +7175,8 @@ class MLoadTypedArrayElement
|
||||
void computeRange(TempAllocator &alloc);
|
||||
|
||||
bool canProduceFloat32() const { return arrayType_ == Scalar::Float32; }
|
||||
|
||||
ALLOW_CLONE(MLoadTypedArrayElement)
|
||||
};
|
||||
|
||||
// Load a value from a typed array. Out-of-bounds accesses are handled in-line.
|
||||
@ -7082,6 +7237,8 @@ class MLoadTypedArrayElementHole
|
||||
return AliasSet::Load(AliasSet::TypedArrayElement);
|
||||
}
|
||||
bool canProduceFloat32() const { return arrayType_ == Scalar::Float32; }
|
||||
|
||||
ALLOW_CLONE(MLoadTypedArrayElementHole)
|
||||
};
|
||||
|
||||
// Load a value fallibly or infallibly from a statically known typed array.
|
||||
@ -7207,6 +7364,8 @@ class MStoreTypedArrayElement
|
||||
bool canConsumeFloat32(MUse *use) const {
|
||||
return use == getUseFor(2) && arrayType_ == Scalar::Float32;
|
||||
}
|
||||
|
||||
ALLOW_CLONE(MStoreTypedArrayElement)
|
||||
};
|
||||
|
||||
class MStoreTypedArrayElementHole
|
||||
@ -7275,6 +7434,8 @@ class MStoreTypedArrayElementHole
|
||||
bool canConsumeFloat32(MUse *use) const {
|
||||
return use == getUseFor(3) && arrayType_ == Scalar::Float32;
|
||||
}
|
||||
|
||||
ALLOW_CLONE(MStoreTypedArrayElementHole)
|
||||
};
|
||||
|
||||
// Store a value infallibly to a statically known typed array.
|
||||
@ -7360,6 +7521,8 @@ class MEffectiveAddress : public MBinaryInstruction
|
||||
int32_t displacement() const {
|
||||
return displacement_;
|
||||
}
|
||||
|
||||
ALLOW_CLONE(MEffectiveAddress)
|
||||
};
|
||||
|
||||
// Clamp input to range [0, 255] for Uint8ClampedArray.
|
||||
@ -7393,6 +7556,8 @@ class MClampToUint8
|
||||
return AliasSet::None();
|
||||
}
|
||||
void computeRange(TempAllocator &alloc);
|
||||
|
||||
ALLOW_CLONE(MClampToUint8)
|
||||
};
|
||||
|
||||
class MLoadFixedSlot
|
||||
@ -7441,6 +7606,8 @@ class MLoadFixedSlot
|
||||
}
|
||||
|
||||
bool mightAlias(const MDefinition *store) const;
|
||||
|
||||
ALLOW_CLONE(MLoadFixedSlot)
|
||||
};
|
||||
|
||||
class MStoreFixedSlot
|
||||
@ -7493,6 +7660,8 @@ class MStoreFixedSlot
|
||||
void setNeedsBarrier(bool needsBarrier = true) {
|
||||
needsBarrier_ = needsBarrier;
|
||||
}
|
||||
|
||||
ALLOW_CLONE(MStoreFixedSlot)
|
||||
};
|
||||
|
||||
typedef Vector<JSObject *, 4, IonAllocPolicy> ObjectVector;
|
||||
@ -8284,6 +8453,8 @@ class MGuardClass
|
||||
AliasSet getAliasSet() const {
|
||||
return AliasSet::Load(AliasSet::ObjectFields);
|
||||
}
|
||||
|
||||
ALLOW_CLONE(MGuardClass)
|
||||
};
|
||||
|
||||
// Load from vp[slot] (slots that are not inline in an object).
|
||||
@ -8335,6 +8506,8 @@ class MLoadSlot
|
||||
return AliasSet::Load(AliasSet::DynamicSlot);
|
||||
}
|
||||
bool mightAlias(const MDefinition *store) const;
|
||||
|
||||
ALLOW_CLONE(MLoadSlot)
|
||||
};
|
||||
|
||||
// Inline call to access a function's environment (scope chain).
|
||||
@ -8486,6 +8659,8 @@ class MStoreSlot
|
||||
AliasSet getAliasSet() const {
|
||||
return AliasSet::Store(AliasSet::DynamicSlot);
|
||||
}
|
||||
|
||||
ALLOW_CLONE(MStoreSlot)
|
||||
};
|
||||
|
||||
class MGetNameCache
|
||||
@ -9159,6 +9334,8 @@ class MStringLength
|
||||
bool canRecoverOnBailout() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
ALLOW_CLONE(MStringLength)
|
||||
};
|
||||
|
||||
// Inlined version of Math.floor().
|
||||
@ -9204,6 +9381,8 @@ class MFloor
|
||||
bool canRecoverOnBailout() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
ALLOW_CLONE(MFloor)
|
||||
};
|
||||
|
||||
// Inlined version of Math.ceil().
|
||||
@ -9245,6 +9424,8 @@ class MCeil
|
||||
return congruentIfOperandsEqual(ins);
|
||||
}
|
||||
void computeRange(TempAllocator &alloc);
|
||||
|
||||
ALLOW_CLONE(MCeil)
|
||||
};
|
||||
|
||||
// Inlined version of Math.round().
|
||||
@ -9291,6 +9472,8 @@ class MRound
|
||||
bool canRecoverOnBailout() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
ALLOW_CLONE(MRound)
|
||||
};
|
||||
|
||||
class MIteratorStart
|
||||
@ -9893,6 +10076,8 @@ class MTypeBarrier
|
||||
return false;
|
||||
return input()->type() != type;
|
||||
}
|
||||
|
||||
ALLOW_CLONE(MTypeBarrier)
|
||||
};
|
||||
|
||||
// Like MTypeBarrier, guard that the value is in the given type set. This is
|
||||
@ -9978,6 +10163,8 @@ class MPostWriteBarrier : public MBinaryInstruction, public ObjectPolicy<0>
|
||||
return use == getUseFor(1);
|
||||
}
|
||||
#endif
|
||||
|
||||
ALLOW_CLONE(MPostWriteBarrier)
|
||||
};
|
||||
|
||||
class MNewDeclEnvObject : public MNullaryInstruction
|
||||
@ -10269,6 +10456,9 @@ class MResumePoint MOZ_FINAL : public MNode, public InlineForwardListNode<MResum
|
||||
public:
|
||||
static MResumePoint *New(TempAllocator &alloc, MBasicBlock *block, jsbytecode *pc,
|
||||
MResumePoint *parent, Mode mode);
|
||||
static MResumePoint *New(TempAllocator &alloc, MBasicBlock *block, jsbytecode *pc,
|
||||
MResumePoint *parent, Mode mode,
|
||||
const MDefinitionVector &operands);
|
||||
|
||||
MNode::Kind kind() const {
|
||||
return MNode::ResumePoint;
|
||||
@ -10913,8 +11103,6 @@ MControlInstruction *MDefinition::toControlInstruction() {
|
||||
return (MControlInstruction *)this;
|
||||
}
|
||||
|
||||
typedef Vector<MDefinition *, 8, IonAllocPolicy> MDefinitionVector;
|
||||
|
||||
// Helper functions used to decide how to build MIR.
|
||||
|
||||
bool ElementAccessIsDenseNative(MDefinition *obj, MDefinition *id);
|
||||
|
@ -103,6 +103,17 @@ MIRGraph::insertBlockAfter(MBasicBlock *at, MBasicBlock *block)
|
||||
numBlocks_++;
|
||||
}
|
||||
|
||||
void
|
||||
MIRGraph::renumberBlocksAfter(MBasicBlock *at)
|
||||
{
|
||||
MBasicBlockIterator iter = begin(at);
|
||||
iter++;
|
||||
|
||||
uint32_t id = at->id();
|
||||
for (; iter != end(); iter++)
|
||||
iter->setId(++id);
|
||||
}
|
||||
|
||||
void
|
||||
MIRGraph::removeBlocksAfter(MBasicBlock *start)
|
||||
{
|
||||
@ -897,6 +908,15 @@ MBasicBlock::insertAfter(MInstruction *at, MInstruction *ins)
|
||||
ins->setTrackedSite(at->trackedSite());
|
||||
}
|
||||
|
||||
void
|
||||
MBasicBlock::insertAtEnd(MInstruction *ins)
|
||||
{
|
||||
if (hasLastIns())
|
||||
insertBefore(lastIns(), ins);
|
||||
else
|
||||
add(ins);
|
||||
}
|
||||
|
||||
void
|
||||
MBasicBlock::add(MInstruction *ins)
|
||||
{
|
||||
|
@ -167,8 +167,7 @@ class MBasicBlock : public TempObject, public InlineListNode<MBasicBlock>
|
||||
MDefinition *pop();
|
||||
void popn(uint32_t n);
|
||||
|
||||
// Adds an instruction to this block's instruction list. |ins| may be
|
||||
// nullptr to simplify OOM checking.
|
||||
// Adds an instruction to this block's instruction list.
|
||||
void add(MInstruction *ins);
|
||||
|
||||
// Marks the last instruction of the block; no further instructions
|
||||
@ -237,6 +236,8 @@ class MBasicBlock : public TempObject, public InlineListNode<MBasicBlock>
|
||||
void insertBefore(MInstruction *at, MInstruction *ins);
|
||||
void insertAfter(MInstruction *at, MInstruction *ins);
|
||||
|
||||
void insertAtEnd(MInstruction *ins);
|
||||
|
||||
// Add an instruction to this block, from elsewhere in the graph.
|
||||
void addFromElsewhere(MInstruction *ins);
|
||||
|
||||
@ -311,12 +312,11 @@ class MBasicBlock : public TempObject, public InlineListNode<MBasicBlock>
|
||||
}
|
||||
MOZ_CRASH();
|
||||
}
|
||||
#ifdef DEBUG
|
||||
bool hasLastIns() const {
|
||||
return !instructions_.empty() && instructions_.rbegin()->isControlInstruction();
|
||||
}
|
||||
#endif
|
||||
MControlInstruction *lastIns() const {
|
||||
JS_ASSERT(hasLastIns());
|
||||
return instructions_.rbegin()->toControlInstruction();
|
||||
}
|
||||
MPhiIterator phisBegin() const {
|
||||
@ -467,6 +467,9 @@ class MBasicBlock : public TempObject, public InlineListNode<MBasicBlock>
|
||||
MResumePoint *entryResumePoint() const {
|
||||
return entryResumePoint_;
|
||||
}
|
||||
void setEntryResumePoint(MResumePoint *rp) {
|
||||
entryResumePoint_ = rp;
|
||||
}
|
||||
void clearEntryResumePoint() {
|
||||
entryResumePoint_ = nullptr;
|
||||
}
|
||||
@ -616,6 +619,8 @@ class MIRGraph
|
||||
void addBlock(MBasicBlock *block);
|
||||
void insertBlockAfter(MBasicBlock *at, MBasicBlock *block);
|
||||
|
||||
void renumberBlocksAfter(MBasicBlock *at);
|
||||
|
||||
void unmarkBlocks();
|
||||
|
||||
void setReturnAccumulator(MIRGraphReturns *accum) {
|
||||
|
@ -1626,11 +1626,14 @@ RangeAnalysis::analyzeLoop(MBasicBlock *header)
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!loopIterationBounds.append(iterationBound))
|
||||
return false;
|
||||
|
||||
#ifdef DEBUG
|
||||
if (IonSpewEnabled(IonSpew_Range)) {
|
||||
Sprinter sp(GetIonContext()->cx);
|
||||
sp.init();
|
||||
iterationBound->sum.print(sp);
|
||||
iterationBound->boundSum.print(sp);
|
||||
IonSpew(IonSpew_Range, "computed symbolic bound on backedges: %s",
|
||||
sp.string());
|
||||
}
|
||||
@ -1748,7 +1751,8 @@ RangeAnalysis::analyzeLoopIterationCount(MBasicBlock *header,
|
||||
if (lhsModified.term != lhs.term)
|
||||
return nullptr;
|
||||
|
||||
LinearSum bound(alloc());
|
||||
LinearSum iterationBound(alloc());
|
||||
LinearSum currentIteration(alloc());
|
||||
|
||||
if (lhsModified.constant == 1 && !lessEqual) {
|
||||
// The value of lhs is 'initial(lhs) + iterCount' and this will end
|
||||
@ -1759,16 +1763,21 @@ RangeAnalysis::analyzeLoopIterationCount(MBasicBlock *header,
|
||||
// iterCount == rhsN - initial(lhs) - lhsN
|
||||
|
||||
if (rhs) {
|
||||
if (!bound.add(rhs, 1))
|
||||
if (!iterationBound.add(rhs, 1))
|
||||
return nullptr;
|
||||
}
|
||||
if (!bound.add(lhsInitial, -1))
|
||||
if (!iterationBound.add(lhsInitial, -1))
|
||||
return nullptr;
|
||||
|
||||
int32_t lhsConstant;
|
||||
if (!SafeSub(0, lhs.constant, &lhsConstant))
|
||||
return nullptr;
|
||||
if (!bound.add(lhsConstant))
|
||||
if (!iterationBound.add(lhsConstant))
|
||||
return nullptr;
|
||||
|
||||
if (!currentIteration.add(lhs.term, 1))
|
||||
return nullptr;
|
||||
if (!currentIteration.add(lhsInitial, -1))
|
||||
return nullptr;
|
||||
} else if (lhsModified.constant == -1 && lessEqual) {
|
||||
// The value of lhs is 'initial(lhs) - iterCount'. Similar to the above
|
||||
@ -1777,19 +1786,24 @@ RangeAnalysis::analyzeLoopIterationCount(MBasicBlock *header,
|
||||
// initial(lhs) - iterCount + lhsN == rhs
|
||||
// iterCount == initial(lhs) - rhs + lhsN
|
||||
|
||||
if (!bound.add(lhsInitial, 1))
|
||||
if (!iterationBound.add(lhsInitial, 1))
|
||||
return nullptr;
|
||||
if (rhs) {
|
||||
if (!bound.add(rhs, -1))
|
||||
if (!iterationBound.add(rhs, -1))
|
||||
return nullptr;
|
||||
}
|
||||
if (!bound.add(lhs.constant))
|
||||
if (!iterationBound.add(lhs.constant))
|
||||
return nullptr;
|
||||
|
||||
if (!currentIteration.add(lhsInitial, 1))
|
||||
return nullptr;
|
||||
if (!currentIteration.add(lhs.term, -1))
|
||||
return nullptr;
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return new(alloc()) LoopIterationBound(header, test, bound);
|
||||
return new(alloc()) LoopIterationBound(header, test, iterationBound, currentIteration);
|
||||
}
|
||||
|
||||
void
|
||||
@ -1836,7 +1850,7 @@ RangeAnalysis::analyzeLoopPhi(MBasicBlock *header, LoopIterationBound *loopBound
|
||||
// phi is initial(phi) + (loopBound - 1) * N, without requiring us to
|
||||
// ensure that loopBound >= 0.
|
||||
|
||||
LinearSum limitSum(loopBound->sum);
|
||||
LinearSum limitSum(loopBound->boundSum);
|
||||
if (!limitSum.multiply(modified.constant) || !limitSum.add(initialSum))
|
||||
return;
|
||||
|
||||
@ -1876,63 +1890,6 @@ SymbolicBoundIsValid(MBasicBlock *header, MBoundsCheck *ins, const SymbolicBound
|
||||
return bb == bound->loop->test->block();
|
||||
}
|
||||
|
||||
// Convert all components of a linear sum *except* its constant to a definition,
|
||||
// adding any necessary instructions to the end of block.
|
||||
static inline MDefinition *
|
||||
ConvertLinearSum(TempAllocator &alloc, MBasicBlock *block, const LinearSum &sum)
|
||||
{
|
||||
MDefinition *def = nullptr;
|
||||
|
||||
for (size_t i = 0; i < sum.numTerms(); i++) {
|
||||
LinearTerm term = sum.term(i);
|
||||
JS_ASSERT(!term.term->isConstant());
|
||||
if (term.scale == 1) {
|
||||
if (def) {
|
||||
def = MAdd::New(alloc, def, term.term);
|
||||
def->toAdd()->setInt32();
|
||||
block->insertBefore(block->lastIns(), def->toInstruction());
|
||||
def->computeRange(alloc);
|
||||
} else {
|
||||
def = term.term;
|
||||
}
|
||||
} else if (term.scale == -1) {
|
||||
if (!def) {
|
||||
def = MConstant::New(alloc, Int32Value(0));
|
||||
block->insertBefore(block->lastIns(), def->toInstruction());
|
||||
def->computeRange(alloc);
|
||||
}
|
||||
def = MSub::New(alloc, def, term.term);
|
||||
def->toSub()->setInt32();
|
||||
block->insertBefore(block->lastIns(), def->toInstruction());
|
||||
def->computeRange(alloc);
|
||||
} else {
|
||||
JS_ASSERT(term.scale != 0);
|
||||
MConstant *factor = MConstant::New(alloc, Int32Value(term.scale));
|
||||
block->insertBefore(block->lastIns(), factor);
|
||||
MMul *mul = MMul::New(alloc, term.term, factor);
|
||||
mul->setInt32();
|
||||
block->insertBefore(block->lastIns(), mul);
|
||||
mul->computeRange(alloc);
|
||||
if (def) {
|
||||
def = MAdd::New(alloc, def, mul);
|
||||
def->toAdd()->setInt32();
|
||||
block->insertBefore(block->lastIns(), def->toInstruction());
|
||||
def->computeRange(alloc);
|
||||
} else {
|
||||
def = mul;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!def) {
|
||||
def = MConstant::New(alloc, Int32Value(0));
|
||||
block->insertBefore(block->lastIns(), def->toInstruction());
|
||||
def->computeRange(alloc);
|
||||
}
|
||||
|
||||
return def;
|
||||
}
|
||||
|
||||
bool
|
||||
RangeAnalysis::tryHoistBoundsCheck(MBasicBlock *header, MBoundsCheck *ins)
|
||||
{
|
||||
|
@ -32,21 +32,30 @@ struct LoopIterationBound : public TempObject
|
||||
// Loop for which this bound applies.
|
||||
MBasicBlock *header;
|
||||
|
||||
// Test from which this bound was derived. Code in the loop body which this
|
||||
// Test from which this bound was derived; after executing exactly 'bound'
|
||||
// times this test will exit the loop. Code in the loop body which this
|
||||
// test dominates (will include the backedge) will execute at most 'bound'
|
||||
// times. Other code in the loop will execute at most '1 + Max(bound, 0)'
|
||||
// times.
|
||||
MTest *test;
|
||||
|
||||
// Symbolic bound computed for the number of backedge executions.
|
||||
LinearSum sum;
|
||||
// Symbolic bound computed for the number of backedge executions. The terms
|
||||
// in this bound are all loop invariant.
|
||||
LinearSum boundSum;
|
||||
|
||||
LoopIterationBound(MBasicBlock *header, MTest *test, LinearSum sum)
|
||||
: header(header), test(test), sum(sum)
|
||||
// Linear sum for the number of iterations already executed, at the start
|
||||
// of the loop header. This will use loop invariant terms and header phis.
|
||||
LinearSum currentSum;
|
||||
|
||||
LoopIterationBound(MBasicBlock *header, MTest *test, LinearSum boundSum, LinearSum currentSum)
|
||||
: header(header), test(test),
|
||||
boundSum(boundSum), currentSum(currentSum)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
typedef Vector<LoopIterationBound *, 0, SystemAllocPolicy> LoopIterationBoundVector;
|
||||
|
||||
// A symbolic upper or lower bound computed for a term.
|
||||
struct SymbolicBound : public TempObject
|
||||
{
|
||||
@ -90,7 +99,7 @@ class RangeAnalysis
|
||||
TempAllocator &alloc() const;
|
||||
|
||||
public:
|
||||
MOZ_CONSTEXPR RangeAnalysis(MIRGenerator *mir, MIRGraph &graph) :
|
||||
RangeAnalysis(MIRGenerator *mir, MIRGraph &graph) :
|
||||
mir(mir), graph_(graph) {}
|
||||
bool addBetaNodes();
|
||||
bool analyze();
|
||||
@ -99,6 +108,9 @@ class RangeAnalysis
|
||||
bool prepareForUCE(bool *shouldRemoveDeadCode);
|
||||
bool truncate();
|
||||
|
||||
// Any iteration bounds discovered for loops in the graph.
|
||||
LoopIterationBoundVector loopIterationBounds;
|
||||
|
||||
private:
|
||||
bool analyzeLoop(MBasicBlock *header);
|
||||
LoopIterationBound *analyzeLoopIterationCount(MBasicBlock *header,
|
||||
|
@ -224,8 +224,6 @@ js::DumpIonScriptCounts(Sprinter *sp, jit::IonScriptCounts *ionCounts)
|
||||
Sprint(sp, "IonScript [%lu blocks]:\n", ionCounts->numBlocks());
|
||||
for (size_t i = 0; i < ionCounts->numBlocks(); i++) {
|
||||
const jit::IonBlockCounts &block = ionCounts->block(i);
|
||||
if (block.hitCount() < 10)
|
||||
continue;
|
||||
Sprint(sp, "BB #%lu [%05u]", block.id(), block.offset());
|
||||
for (size_t j = 0; j < block.numSuccessors(); j++)
|
||||
Sprint(sp, " -> #%lu", block.successor(j));
|
||||
|
@ -173,6 +173,7 @@ UNIFIED_SOURCES += [
|
||||
'jit/LinearScan.cpp',
|
||||
'jit/LIR.cpp',
|
||||
'jit/LiveRangeAllocator.cpp',
|
||||
'jit/LoopUnroller.cpp',
|
||||
'jit/Lowering.cpp',
|
||||
'jit/MCallOptimize.cpp',
|
||||
'jit/MIR.cpp',
|
||||
|
@ -5787,14 +5787,23 @@ SetRuntimeOptions(JSRuntime *rt, const OptionParser &op)
|
||||
return OptionFailure("ion-edgecase-analysis", str);
|
||||
}
|
||||
|
||||
if (const char *str = op.getStringOption("ion-range-analysis")) {
|
||||
if (strcmp(str, "on") == 0)
|
||||
jit::js_JitOptions.disableRangeAnalysis = false;
|
||||
else if (strcmp(str, "off") == 0)
|
||||
jit::js_JitOptions.disableRangeAnalysis = true;
|
||||
else
|
||||
return OptionFailure("ion-range-analysis", str);
|
||||
}
|
||||
if (const char *str = op.getStringOption("ion-range-analysis")) {
|
||||
if (strcmp(str, "on") == 0)
|
||||
jit::js_JitOptions.disableRangeAnalysis = false;
|
||||
else if (strcmp(str, "off") == 0)
|
||||
jit::js_JitOptions.disableRangeAnalysis = true;
|
||||
else
|
||||
return OptionFailure("ion-range-analysis", str);
|
||||
}
|
||||
|
||||
if (const char *str = op.getStringOption("ion-loop-unrolling")) {
|
||||
if (strcmp(str, "on") == 0)
|
||||
jit::js_JitOptions.disableLoopUnrolling = false;
|
||||
else if (strcmp(str, "off") == 0)
|
||||
jit::js_JitOptions.disableLoopUnrolling = true;
|
||||
else
|
||||
return OptionFailure("ion-loop-unrolling", str);
|
||||
}
|
||||
|
||||
if (op.getBoolOption("ion-check-range-analysis"))
|
||||
jit::js_JitOptions.checkRangeAnalysis = true;
|
||||
@ -6059,6 +6068,8 @@ main(int argc, char **argv, char **envp)
|
||||
"Find edge cases where Ion can avoid bailouts (default: on, off to disable)")
|
||||
|| !op.addStringOption('\0', "ion-range-analysis", "on/off",
|
||||
"Range analysis (default: on, off to disable)")
|
||||
|| !op.addStringOption('\0', "ion-loop-unrolling", "on/off",
|
||||
"Loop unrolling (default: off, on to enable)")
|
||||
|| !op.addBoolOption('\0', "ion-check-range-analysis",
|
||||
"Range analysis checking")
|
||||
|| !op.addStringOption('\0', "ion-inlining", "on/off",
|
||||
|
@ -838,6 +838,7 @@ TraceLogging::lazyInit()
|
||||
enabledTextIds[TraceLogger::UCE] = true;
|
||||
enabledTextIds[TraceLogger::LICM] = true;
|
||||
enabledTextIds[TraceLogger::RangeAnalysis] = true;
|
||||
enabledTextIds[TraceLogger::LoopUnrolling] = true;
|
||||
enabledTextIds[TraceLogger::EffectiveAddressAnalysis] = true;
|
||||
enabledTextIds[TraceLogger::EliminateDeadCode] = true;
|
||||
enabledTextIds[TraceLogger::EdgeCaseAnalysis] = true;
|
||||
|
@ -143,6 +143,7 @@ namespace jit {
|
||||
_(UCE) \
|
||||
_(LICM) \
|
||||
_(RangeAnalysis) \
|
||||
_(LoopUnrolling) \
|
||||
_(EffectiveAddressAnalysis) \
|
||||
_(EliminateDeadCode) \
|
||||
_(EdgeCaseAnalysis) \
|
||||
|
Loading…
Reference in New Issue
Block a user