Bug 1039458 - Add disabled loop unrolling optimization pass, r=jandem.

This commit is contained in:
Brian Hackett 2014-08-10 12:41:51 -08:00
parent 0a1e77e258
commit 585dac288d
23 changed files with 878 additions and 96 deletions

View File

@ -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>;

View File

@ -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))

View File

@ -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,

View File

@ -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,

View File

@ -32,6 +32,7 @@ OptimizationInfo::initNormalOptimizationInfo()
licm_ = true;
uce_ = true;
rangeAnalysis_ = true;
loopUnrolling_ = true;
autoTruncate_ = true;
registerAllocator_ = RegisterAllocator_LSRA;

View File

@ -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();
}

View File

@ -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"))

View File

@ -34,6 +34,8 @@ namespace jit {
_(GVN) \
/* Information during Range analysis */ \
_(Range) \
/* Information during loop unrolling */ \
_(Unrolling) \
/* Information during LICM */ \
_(LICM) \
/* Information during regalloc */ \

View File

@ -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;

View File

@ -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
View 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
View 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

View File

@ -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),

View File

@ -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);

View File

@ -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)
{

View File

@ -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) {

View File

@ -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)
{

View File

@ -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,

View File

@ -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));

View File

@ -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',

View File

@ -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",

View File

@ -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;

View File

@ -143,6 +143,7 @@ namespace jit {
_(UCE) \
_(LICM) \
_(RangeAnalysis) \
_(LoopUnrolling) \
_(EffectiveAddressAnalysis) \
_(EliminateDeadCode) \
_(EdgeCaseAnalysis) \