Add symbolic range analysis for loop induction variables, bug 766592. r=mjrosenb

This commit is contained in:
Brian Hackett 2012-11-23 23:23:03 -05:00
parent 229b6c663a
commit dc4f919839
12 changed files with 1150 additions and 749 deletions

View File

@ -868,6 +868,17 @@ CompileBackEnd(MIRGenerator *mir)
return NULL;
}
if (js_IonOptions.licm) {
LICM licm(mir, graph);
if (!licm.analyze())
return NULL;
IonSpewPass("LICM");
AssertGraphCoherency(graph);
if (mir->shouldCancel("LICM"))
return NULL;
}
if (js_IonOptions.rangeAnalysis) {
RangeAnalysis r(graph);
if (!r.addBetaNobes())
@ -903,17 +914,6 @@ CompileBackEnd(MIRGenerator *mir)
if (mir->shouldCancel("DCE"))
return NULL;
if (js_IonOptions.licm) {
LICM licm(mir, graph);
if (!licm.analyze())
return NULL;
IonSpewPass("LICM");
AssertGraphCoherency(graph);
if (mir->shouldCancel("LICM"))
return NULL;
}
if (js_IonOptions.edgeCaseAnalysis) {
EdgeCaseAnalysis edgeCaseAnalysis(mir, graph);
if (!edgeCaseAnalysis.analyzeLate())

View File

@ -812,7 +812,7 @@ typedef HashMap<uint32,
static HashNumber
BoundsCheckHashIgnoreOffset(MBoundsCheck *check)
{
LinearSum indexSum = ExtractLinearSum(check->index());
SimpleLinearSum indexSum = ExtractLinearSum(check->index());
uintptr_t index = indexSum.term ? uintptr_t(indexSum.term) : 0;
uintptr_t length = uintptr_t(check->length());
return index ^ length;
@ -840,43 +840,105 @@ FindDominatingBoundsCheck(BoundsCheckMap &checks, MBoundsCheck *check, size_t in
}
// Extract a linear sum from ins, if possible (otherwise giving the sum 'ins + 0').
LinearSum
SimpleLinearSum
ion::ExtractLinearSum(MDefinition *ins)
{
if (ins->isBeta())
ins = ins->getOperand(0);
if (ins->type() != MIRType_Int32)
return LinearSum(ins, 0);
return SimpleLinearSum(ins, 0);
if (ins->isConstant()) {
const Value &v = ins->toConstant()->value();
JS_ASSERT(v.isInt32());
return LinearSum(NULL, v.toInt32());
return SimpleLinearSum(NULL, v.toInt32());
} else if (ins->isAdd() || ins->isSub()) {
MDefinition *lhs = ins->getOperand(0);
MDefinition *rhs = ins->getOperand(1);
if (lhs->type() == MIRType_Int32 && rhs->type() == MIRType_Int32) {
LinearSum lsum = ExtractLinearSum(lhs);
LinearSum rsum = ExtractLinearSum(rhs);
SimpleLinearSum lsum = ExtractLinearSum(lhs);
SimpleLinearSum rsum = ExtractLinearSum(rhs);
JS_ASSERT(lsum.term || rsum.term);
if (lsum.term && rsum.term)
return LinearSum(ins, 0);
return SimpleLinearSum(ins, 0);
// Check if this is of the form <SUM> + n, n + <SUM> or <SUM> - n.
if (ins->isAdd()) {
int32 constant;
if (!SafeAdd(lsum.constant, rsum.constant, &constant))
return LinearSum(ins, 0);
return LinearSum(lsum.term ? lsum.term : rsum.term, constant);
return SimpleLinearSum(ins, 0);
return SimpleLinearSum(lsum.term ? lsum.term : rsum.term, constant);
} else if (lsum.term) {
int32 constant;
if (!SafeSub(lsum.constant, rsum.constant, &constant))
return LinearSum(ins, 0);
return LinearSum(lsum.term, constant);
return SimpleLinearSum(ins, 0);
return SimpleLinearSum(lsum.term, constant);
}
}
}
return LinearSum(ins, 0);
return SimpleLinearSum(ins, 0);
}
// Extract a linear inequality holding when a boolean test goes in the
// specified direction, of the form 'lhs + lhsN <= rhs' (or >=).
bool
ion::ExtractLinearInequality(MTest *test, BranchDirection direction,
SimpleLinearSum *plhs, MDefinition **prhs, bool *plessEqual)
{
if (!test->getOperand(0)->isCompare())
return false;
MCompare *compare = test->getOperand(0)->toCompare();
MDefinition *lhs = compare->getOperand(0);
MDefinition *rhs = compare->getOperand(1);
if (compare->specialization() != MIRType_Int32)
return false;
JS_ASSERT(lhs->type() == MIRType_Int32);
JS_ASSERT(rhs->type() == MIRType_Int32);
JSOp jsop = compare->jsop();
if (direction == FALSE_BRANCH)
jsop = analyze::NegateCompareOp(jsop);
SimpleLinearSum lsum = ExtractLinearSum(lhs);
SimpleLinearSum rsum = ExtractLinearSum(rhs);
if (!SafeSub(lsum.constant, rsum.constant, &lsum.constant))
return false;
// Normalize operations to use <= or >=.
switch (jsop) {
case JSOP_LE:
*plessEqual = true;
break;
case JSOP_LT:
/* x < y ==> x + 1 <= y */
if (!SafeAdd(lsum.constant, 1, &lsum.constant))
return false;
*plessEqual = true;
break;
case JSOP_GE:
*plessEqual = false;
break;
case JSOP_GT:
/* x > y ==> x - 1 >= y */
if (!SafeSub(lsum.constant, 1, &lsum.constant))
return false;
*plessEqual = false;
break;
default:
return false;
}
*plhs = lsum;
*prhs = rsum.term;
return true;
}
static bool
@ -889,8 +951,8 @@ TryEliminateBoundsCheck(MBoundsCheck *dominating, MBoundsCheck *dominated, bool
if (dominating->length() != dominated->length())
return true;
LinearSum sumA = ExtractLinearSum(dominating->index());
LinearSum sumB = ExtractLinearSum(dominated->index());
SimpleLinearSum sumA = ExtractLinearSum(dominating->index());
SimpleLinearSum sumB = ExtractLinearSum(dominated->index());
// Both terms should be NULL or the same definition.
if (sumA.term != sumB.term)
@ -1010,3 +1072,86 @@ ion::EliminateRedundantBoundsChecks(MIRGraph &graph)
JS_ASSERT(index == graph.numBlocks());
return true;
}
bool
LinearSum::multiply(int32 scale)
{
for (size_t i = 0; i < terms_.length(); i++) {
if (!SafeMul(scale, terms_[i].scale, &terms_[i].scale))
return false;
}
return SafeMul(scale, constant_, &constant_);
}
bool
LinearSum::add(const LinearSum &other)
{
for (size_t i = 0; i < other.terms_.length(); i++) {
if (!add(other.terms_[i].term, other.terms_[i].scale))
return false;
}
return add(other.constant_);
}
bool
LinearSum::add(MDefinition *term, int32 scale)
{
JS_ASSERT(term);
if (scale == 0)
return true;
if (term->isConstant()) {
int32 constant = term->toConstant()->value().toInt32();
if (!SafeMul(constant, scale, &constant))
return false;
return add(constant);
}
for (size_t i = 0; i < terms_.length(); i++) {
if (term == terms_[i].term) {
if (!SafeAdd(scale, terms_[i].scale, &terms_[i].scale))
return false;
if (terms_[i].scale == 0) {
terms_[i] = terms_.back();
terms_.popBack();
}
return true;
}
}
terms_.append(LinearTerm(term, scale));
return true;
}
bool
LinearSum::add(int32 constant)
{
return SafeAdd(constant, constant_, &constant_);
}
void
LinearSum::print(Sprinter &sp) const
{
for (size_t i = 0; i < terms_.length(); i++) {
int32 scale = terms_[i].scale;
int32 id = terms_[i].term->id();
JS_ASSERT(scale);
if (scale > 0) {
if (i)
sp.printf("+");
if (scale == 1)
sp.printf("#%d", id);
else
sp.printf("%d*#%d", scale, id);
} else if (scale == -1) {
sp.printf("-#%d", id);
} else {
sp.printf("%d*#%d", scale, id);
}
}
if (constant_ > 0)
sp.printf("+%d", constant_);
else if (constant_ < 0)
sp.printf("%d", constant_);
}

View File

@ -11,6 +11,7 @@
// This file declares various analysis passes that operate on MIR.
#include "IonAllocPolicy.h"
#include "MIR.h"
namespace js {
namespace ion {
@ -45,23 +46,69 @@ AssertGraphCoherency(MIRGraph &graph);
bool
EliminateRedundantBoundsChecks(MIRGraph &graph);
// Linear sum of term(s). For now the only linear sums which can be represented
// are 'n' or 'x + n' (for any computation x).
class MDefinition;
struct LinearSum
// Simple linear sum of the form 'n' or 'x + n'.
struct SimpleLinearSum
{
MDefinition *term;
int32 constant;
LinearSum(MDefinition *term, int32 constant)
SimpleLinearSum(MDefinition *term, int32 constant)
: term(term), constant(constant)
{}
};
LinearSum
SimpleLinearSum
ExtractLinearSum(MDefinition *ins);
bool
ExtractLinearInequality(MTest *test, BranchDirection direction,
SimpleLinearSum *plhs, MDefinition **prhs, bool *plessEqual);
struct LinearTerm
{
MDefinition *term;
int32 scale;
LinearTerm(MDefinition *term, int32 scale)
: term(term), scale(scale)
{
}
};
// General linear sum of the form 'x1*n1 + x2*n2 + ... + n'
class LinearSum
{
public:
LinearSum()
: constant_(0)
{
}
LinearSum(const LinearSum &other)
: constant_(other.constant_)
{
for (size_t i = 0; i < other.terms_.length(); i++)
terms_.append(other.terms_[i]);
}
bool multiply(int32 scale);
bool add(const LinearSum &other);
bool add(MDefinition *term, int32 scale);
bool add(int32 constant);
int32 constant() const { return constant_; }
size_t numTerms() const { return terms_.length(); }
LinearTerm term(size_t i) const { return terms_[i]; }
void print(Sprinter &sp) const;
private:
Vector<LinearTerm, 2, IonAllocPolicy> terms_;
int32 constant_;
};
} // namespace ion
} // namespace js

View File

@ -13,6 +13,7 @@
#include "MIR.h"
#include "MIRGraph.h"
#include "LinearScan.h"
#include "RangeAnalysis.h"
using namespace js;
using namespace js::ion;
@ -255,8 +256,14 @@ JSONSpewer::spewMDef(MDefinition *def)
integerValue(use.def()->id());
endList();
stringProperty("type", "%s : [%d, %d]", StringFromMIRType(def->type()),
def->range()->lower(), def->range()->upper());
if (def->range()) {
Sprinter sp(GetIonContext()->cx);
sp.init();
def->range()->print(sp);
stringProperty("type", "%s : %s", sp.string());
} else {
stringProperty("type", "%s", StringFromMIRType(def->type()));
}
if (def->isInstruction()) {
if (MResumePoint *rp = def->toInstruction()->resumePoint())

View File

@ -17,64 +17,6 @@
using namespace js;
using namespace js::ion;
bool
ion::ExtractLinearInequality(MTest *test, BranchDirection direction,
LinearSum *plhs, MDefinition **prhs, bool *plessEqual)
{
if (!test->getOperand(0)->isCompare())
return false;
MCompare *compare = test->getOperand(0)->toCompare();
MDefinition *lhs = compare->getOperand(0);
MDefinition *rhs = compare->getOperand(1);
if (compare->specialization() != MIRType_Int32)
return false;
JS_ASSERT(lhs->type() == MIRType_Int32);
JS_ASSERT(rhs->type() == MIRType_Int32);
JSOp jsop = compare->jsop();
if (direction == FALSE_BRANCH)
jsop = analyze::NegateCompareOp(jsop);
LinearSum lsum = ExtractLinearSum(lhs);
LinearSum rsum = ExtractLinearSum(rhs);
if (!SafeSub(lsum.constant, rsum.constant, &lsum.constant))
return false;
// Normalize operations to use <= or >=.
switch (jsop) {
case JSOP_LE:
*plessEqual = true;
break;
case JSOP_LT:
/* x < y ==> x + 1 <= y */
if (!SafeAdd(lsum.constant, 1, &lsum.constant))
return false;
*plessEqual = true;
break;
case JSOP_GE:
*plessEqual = false;
break;
case JSOP_GT:
/* x > y ==> x - 1 >= y */
if (!SafeSub(lsum.constant, 1, &lsum.constant))
return false;
*plessEqual = false;
break;
default:
return false;
}
*plhs = lsum;
*prhs = rsum.term;
return true;
}
LICM::LICM(MIRGenerator *mir, MIRGraph &graph)
: mir(mir), graph(graph)
{
@ -178,7 +120,6 @@ bool
Loop::optimize()
{
InstructionQueue invariantInstructions;
InstructionQueue boundsChecks;
IonSpew(IonSpew_LICM, "These instructions are in the loop: ");
@ -220,62 +161,17 @@ Loop::optimize()
if (IonSpewEnabled(IonSpew_LICM))
fprintf(IonSpewFile, " Loop Invariant!\n");
} else if (ins->isBoundsCheck()) {
if (!boundsChecks.append(ins))
return false;
}
}
if (!hoistInstructions(invariantInstructions, boundsChecks))
if (!hoistInstructions(invariantInstructions))
return false;
return true;
}
bool
Loop::hoistInstructions(InstructionQueue &toHoist, InstructionQueue &boundsChecks)
Loop::hoistInstructions(InstructionQueue &toHoist)
{
// Hoist bounds checks first, so that hoistBoundsCheck can test for
// invariant instructions, but delay actual insertion until the end to
// handle dependencies on loop invariant instructions.
InstructionQueue hoistedChecks;
for (size_t i = 0; i < boundsChecks.length(); i++) {
MBoundsCheck *ins = boundsChecks[i]->toBoundsCheck();
if (isLoopInvariant(ins) || !isInLoop(ins))
continue;
// Try to find a test dominating the bounds check which can be
// transformed into a hoistable check. Stop after the first such check
// which could be transformed (the one which will be the closest to the
// access in the source).
MBasicBlock *block = ins->block();
while (true) {
BranchDirection direction;
MTest *branch = block->immediateDominatorBranch(&direction);
if (branch) {
MInstruction *upper, *lower;
tryHoistBoundsCheck(ins, branch, direction, &upper, &lower);
if (upper && !hoistedChecks.append(upper))
return false;
if (lower && !hoistedChecks.append(lower))
return false;
if (upper || lower) {
// Note: replace all uses of the original bounds check with the
// actual index. This is usually done during bounds check elimination,
// but in this case it's safe to do it here since the load/store is
// definitely not loop-invariant, so we will never move it before
// one of the bounds checks we just added.
ins->replaceAllUsesWith(ins->index());
ins->block()->discard(ins);
break;
}
}
MBasicBlock *dom = block->immediateDominator();
if (dom == block)
break;
block = dom;
}
}
// Move all instructions to the preLoop_ block just before the control instruction.
for (size_t i = 0; i < toHoist.length(); i++) {
MInstruction *ins = toHoist[i];
@ -292,11 +188,6 @@ Loop::hoistInstructions(InstructionQueue &toHoist, InstructionQueue &boundsCheck
}
}
for (size_t i = 0; i < hoistedChecks.length(); i++) {
MInstruction *ins = hoistedChecks[i];
preLoop_->insertBefore(preLoop_->lastIns(), ins);
}
return true;
}
@ -370,186 +261,3 @@ Loop::popFromWorklist()
toReturn->setNotInWorklist();
return toReturn;
}
// Try to compute hoistable checks for the upper and lower bound on ins,
// according to a test in the loop which dominates ins.
//
// Given a bounds check within a loop which is not loop invariant, we would
// like to compute loop invariant bounds checks which imply that the inner
// check will succeed. These invariant checks can then be added to the
// preheader, and the inner check eliminated.
//
// Example:
//
// for (i = v; i < n; i++)
// x[i] = 0;
//
// There are two constraints captured by the bounds check here: i >= 0, and
// i < length(x). 'i' is not loop invariant, but we can still hoist these
// checks:
//
// - At the point of the check, it is known that i < n. Given this,
// if n <= length(x) then i < length(x), and since n and length(x) are loop
// invariant the former condition can be hoisted and the i < length(x) check
// removed.
//
// - i is only incremented within the loop, so if its initial value is >= 0
// then all its values within the loop will also be >= 0. The lower bounds
// check can be hoisted as v >= 0.
//
// tryHoistBoundsCheck encodes this logic. Given a bounds check B and a test T
// in the loop dominating that bounds check, where B and T share a non-invariant
// term lhs, a new check C is computed such that T && C imply B.
void
Loop::tryHoistBoundsCheck(MBoundsCheck *ins, MTest *test, BranchDirection direction,
MInstruction **pupper, MInstruction **plower)
{
*pupper = NULL;
*plower = NULL;
if (!isLoopInvariant(ins->length()))
return;
LinearSum lhs(NULL, 0);
MDefinition *rhs;
bool lessEqual;
if (!ExtractLinearInequality(test, direction, &lhs, &rhs, &lessEqual))
return;
// Ensure the rhs is a loop invariant term.
if (rhs && !isLoopInvariant(rhs)) {
if (lhs.term && !isLoopInvariant(lhs.term))
return;
MDefinition *temp = lhs.term;
lhs.term = rhs;
rhs = temp;
if (!SafeSub(0, lhs.constant, &lhs.constant))
return;
lessEqual = !lessEqual;
}
JS_ASSERT_IF(rhs, isLoopInvariant(rhs));
// Ensure the lhs is a phi node from the start of the loop body.
if (!lhs.term || !lhs.term->isPhi() || lhs.term->block() != header_)
return;
// Check if the lhs in the conditional matches the bounds check index.
LinearSum index = ExtractLinearSum(ins->index());
if (index.term != lhs.term)
return;
if (!lessEqual)
return;
// At the point of the access, it is known that lhs + lhsN <= rhs, and the
// bounds check is that lhs + indexN + maximum < length. To ensure the
// bounds check holds then, we need to ensure that:
//
// rhs - lhsN + indexN + maximum < length
int32 adjustment;
if (!SafeSub(index.constant, lhs.constant, &adjustment))
return;
if (!SafeAdd(adjustment, ins->maximum(), &adjustment))
return;
// For the lower bound, check that lhs + indexN + minimum >= 0, e.g.
//
// lhs >= -indexN - minimum
//
// lhs is not loop invariant, but if this condition holds of the backing
// variable at loop entry and the variable's value never decreases in the
// loop body, it will hold throughout the loop.
uint32 position = preLoop_->positionInPhiSuccessor();
MDefinition *initialIndex = lhs.term->toPhi()->getOperand(position);
if (!nonDecreasing(initialIndex, lhs.term))
return;
int32 lowerBound;
if (!SafeSub(0, index.constant, &lowerBound))
return;
if (!SafeSub(lowerBound, ins->minimum(), &lowerBound))
return;
// XXX limit on how much can be hoisted, to ensure ballast works?
if (!rhs) {
rhs = MConstant::New(Int32Value(adjustment));
adjustment = 0;
preLoop_->insertBefore(preLoop_->lastIns(), rhs->toInstruction());
}
MBoundsCheck *upper = MBoundsCheck::New(rhs, ins->length());
upper->setMinimum(adjustment);
upper->setMaximum(adjustment);
MBoundsCheckLower *lower = MBoundsCheckLower::New(initialIndex);
lower->setMinimum(lowerBound);
*pupper = upper;
*plower = lower;
}
// Determine whether the possible value of start (a phi node within the loop)
// can become smaller than an initial value at loop entry.
bool
Loop::nonDecreasing(MDefinition *initial, MDefinition *start)
{
MDefinitionVector worklist;
MDefinitionVector seen;
if (!worklist.append(start))
return false;
while (!worklist.empty()) {
MDefinition *def = worklist.popCopy();
bool duplicate = false;
for (size_t i = 0; i < seen.length() && !duplicate; i++) {
if (seen[i] == def)
duplicate = true;
}
if (duplicate)
continue;
if (!seen.append(def))
return false;
if (def->type() != MIRType_Int32)
return false;
if (!isInLoop(def)) {
if (def != initial)
return false;
continue;
}
if (def->isPhi()) {
MPhi *phi = def->toPhi();
for (size_t i = 0; i < phi->numOperands(); i++) {
if (!worklist.append(phi->getOperand(i)))
return false;
}
continue;
}
if (def->isAdd()) {
if (def->toAdd()->specialization() != MIRType_Int32)
return false;
MDefinition *lhs = def->toAdd()->getOperand(0);
MDefinition *rhs = def->toAdd()->getOperand(1);
if (!rhs->isConstant())
return false;
Value v = rhs->toConstant()->value();
if (!v.isInt32() || v.toInt32() < 0)
return false;
if (!worklist.append(lhs))
return false;
continue;
}
return false;
}
return true;
}

View File

@ -33,12 +33,6 @@ class LICM
bool analyze();
};
// Extract a linear inequality holding when a boolean test goes in the
// specified direction, of the form 'lhs + lhsN <= rhs' (or >=).
bool
ExtractLinearInequality(MTest *test, BranchDirection direction,
LinearSum *plhs, MDefinition **prhs, bool *plessEqual);
class Loop
{
MIRGenerator *mir;
@ -77,7 +71,7 @@ class Loop
// Along the way it adds instructions to the worklist for invariance testing.
LoopReturn iterateLoopBlocks(MBasicBlock *current);
bool hoistInstructions(InstructionQueue &toHoist, InstructionQueue &boundsChecks);
bool hoistInstructions(InstructionQueue &toHoist);
// Utility methods for invariance testing and instruction hoisting.
bool isInLoop(MDefinition *ins);
@ -96,15 +90,6 @@ class Loop
inline bool isHoistable(const MDefinition *ins) const {
return ins->isMovable() && !ins->isEffectful();
}
// State for hoisting bounds checks. Even if the terms involved in a bounds
// check are not loop invariant, we analyze the tests and increments done
// in the loop to try to find a stronger condition which can be hoisted.
void tryHoistBoundsCheck(MBoundsCheck *ins, MTest *test, BranchDirection direction,
MInstruction **pupper, MInstruction **plower);
bool nonDecreasing(MDefinition *initial, MDefinition *start);
};
} // namespace ion

View File

@ -10,6 +10,7 @@
#include "MIR.h"
#include "MIRGraph.h"
#include "IonSpewer.h"
#include "RangeAnalysis.h"
#include "jsanalyze.h"
#include "jsbool.h"
#include "jsnum.h"

View File

@ -10,6 +10,7 @@
#include "MIR.h"
#include "MIRGraph.h"
#include "EdgeCaseAnalysis.h"
#include "RangeAnalysis.h"
#include "IonSpewer.h"
#include "jsnum.h"
#include "jsstr.h"
@ -284,11 +285,6 @@ MConstant::MConstant(const js::Value &vp)
{
setResultType(MIRTypeFromValue(vp));
setMovable();
if (type() == MIRType_Int32) {
range()->setLower(value().toInt32());
range()->setUpper(value().toInt32());
}
}
HashNumber
@ -476,53 +472,6 @@ MPhi::addInput(MDefinition *ins)
return inputs_.append(ins);
}
bool
MPhi::recomputeRange()
{
if (type() != MIRType_Int32)
return false;
// Use RangeUpdater rather than Range because it needs to
// track if it has been updated yet.
RangeUpdater r;
JS_ASSERT(getOperand(0)->op() != MDefinition::Op_OsrValue);
bool updated = false;
for (size_t i = 0; i < numOperands(); i++) {
if (getOperand(i)->block()->earlyAbort()) {
IonSpew(IonSpew_Range, "Ignoring unreachable input %d", getOperand(i)->id());
continue;
}
if (!isOSRLikeValue(getOperand(i))) {
if (block()->isLoopHeader()) {
IonSpew(IonSpew_Range, " Updating input #%d (inst %d)", i, getOperand(i)->id());
changeCounts_[i].updateRange(getOperand(i)->range());
r.unionWith(&changeCounts_[i]);
} else {
r.unionWith(getOperand(i)->range());
}
#ifdef DEBUG
if (IonSpewEnabled(IonSpew_Range)) {
fprintf(IonSpewFile, " %d:", getOperand(i)->id());
getOperand(i)->range()->printRange(IonSpewFile);
fprintf(IonSpewFile, " => ");
r.printRange(IonSpewFile);
fprintf(IonSpewFile, "\n");
}
#endif
}
}
if (!updated) {
IonSpew(IonSpew_Range, "My block is unreachable %d", id());
block()->setEarlyAbort();
return false;
}
return range()->update(r.getRange());
}
uint32
MPrepareCall::argc() const
{
@ -894,6 +843,12 @@ MAdd::updateForReplacement(MDefinition *ins_)
return true;
}
bool
MAdd::fallible()
{
return !isTruncated() && (!range() || !range()->isFinite());
}
void
MSub::analyzeTruncateBackward()
{
@ -911,6 +866,12 @@ MSub::updateForReplacement(MDefinition *ins_)
return true;
}
bool
MSub::fallible()
{
return !isTruncated() && (!range() || !range()->isFinite());
}
MDefinition *
MMul::foldsTo(bool useValueNumbers)
{
@ -975,6 +936,24 @@ MMul::updateForReplacement(MDefinition *ins_)
return true;
}
bool
MMul::canOverflow()
{
if (implicitTruncate_)
return false;
return !range() || !range()->isFinite();
}
bool
MMul::canBeNegativeZero()
{
if (!range())
return canBeNegativeZero_;
if (range()->lower() > 0 || range()->upper() < 0)
return false;
return canBeNegativeZero_;
}
void
MBinaryArithInstruction::infer(JSContext *cx, const TypeOracle::BinaryTypes &b)
{
@ -1530,6 +1509,12 @@ MNot::foldsTo(bool useValueNumbers)
return this;
}
bool
MBoundsCheckLower::fallible()
{
return !range() || range()->lower() < minimum_;
}
void
MBeta::printOpcode(FILE *fp)
{
@ -1537,17 +1522,23 @@ MBeta::printOpcode(FILE *fp)
fprintf(fp, " ");
getOperand(0)->printName(fp);
fprintf(fp, " ");
comparison_.printRange(fp);
Sprinter sp(GetIonContext()->cx);
sp.init();
comparison_->print(sp);
fprintf(fp, "%s", sp.string());
}
bool
MBeta::recomputeRange()
void
MBeta::computeRange()
{
bool nullRange = false;
bool ret = range()->update(Range::intersect(val_->range(), &comparison_, &nullRange));
if (nullRange) {
IonSpew(IonSpew_Range, "Marking block for inst %d unexitable", id());
block()->setEarlyAbort();
bool emptyRange = false;
Range *range = Range::intersect(val_->range(), comparison_, &emptyRange);
if (emptyRange) {
IonSpew(IonSpew_Range, "Marking block for inst %d unexitable", id());
block()->setEarlyAbort();
} else {
setRange(range);
}
return ret;
}

View File

@ -24,12 +24,14 @@
#include "IonMacroAssembler.h"
#include "Bailouts.h"
#include "FixedList.h"
#include "RangeAnalysis.h"
#include "CompilerRoot.h"
namespace js {
namespace ion {
class ValueNumberData;
class Range;
static const inline
MIRType MIRTypeFromValue(const js::Value &vp)
{
@ -228,11 +230,7 @@ class MDefinition : public MNode
uint32 id_; // Instruction ID, which after block re-ordering
// is sorted within a basic block.
ValueNumberData *valueNumber_; // The instruction's value number (see GVN for details in use)
// Bug 765126: This should be a pointer. The range should only be allocated if range analysis is
// enabled.
Range range_; // The most specific known range for this def.
Range *range_; // Any computed range for this def.
MIRType resultType_; // Representation of result type.
uint32 flags_; // Bit flags.
union {
@ -292,8 +290,11 @@ class MDefinition : public MNode
return trackedPc_;
}
Range *range() {
return &range_;
Range *range() const {
return range_;
}
void setRange(Range *range) {
range_ = range;
}
virtual HashNumber valueHash() const;
@ -306,9 +307,10 @@ class MDefinition : public MNode
virtual void analyzeEdgeCasesBackward();
virtual void analyzeTruncateBackward();
bool earlyAbortCheck();
// Propagate a range. Return true if the range changed.
virtual bool recomputeRange() {
return false;
// Compute an absolute or symbolic range for the value of this node.
// Ranges are only computed for definitions whose type is int32.
virtual void computeRange() {
}
MNode::Kind kind() const {
@ -623,6 +625,8 @@ class MConstant : public MNullaryInstruction
AliasSet getAliasSet() const {
return AliasSet::None();
}
void computeRange();
};
class MParameter : public MNullaryInstruction
@ -852,6 +856,17 @@ class MGoto : public MAryControlInstruction<0, 1>
}
};
enum BranchDirection {
FALSE_BRANCH,
TRUE_BRANCH
};
static inline BranchDirection
NegateBranchDirection(BranchDirection dir)
{
return (dir == FALSE_BRANCH) ? TRUE_BRANCH : FALSE_BRANCH;
}
// Tests if the input instruction evaluates to true or false, and jumps to the
// start of a corresponding basic block.
class MTest
@ -875,6 +890,9 @@ class MTest
MBasicBlock *ifFalse() const {
return getSuccessor(1);
}
MBasicBlock *branchSuccessor(BranchDirection dir) const {
return (dir == TRUE_BRANCH) ? ifTrue() : ifFalse();
}
TypePolicy *typePolicy() {
return this;
}
@ -1701,7 +1719,6 @@ class MToInt32 : public MUnaryInstruction
{
setResultType(MIRType_Int32);
setMovable();
range()->set(JSVAL_INT_MIN, JSVAL_INT_MAX);
}
public:
@ -1742,7 +1759,6 @@ class MTruncateToInt32 : public MUnaryInstruction
{
setResultType(MIRType_Int32);
setMovable();
range()->set(JSVAL_INT_MIN, JSVAL_INT_MAX);
}
public:
@ -1808,7 +1824,6 @@ class MBitNot
{
setResultType(MIRType_Int32);
setMovable();
range()->set(JSVAL_INT_MIN, JSVAL_INT_MAX);
}
public:
@ -1904,7 +1919,6 @@ class MBinaryBitwiseInstruction
{
setResultType(MIRType_Int32);
setMovable();
range()->set(JSVAL_INT_MIN, JSVAL_INT_MAX);
}
public:
@ -1947,12 +1961,7 @@ class MBitAnd : public MBinaryBitwiseInstruction
MDefinition *foldIfEqual() {
return getOperand(0); // x & x => x;
}
bool recomputeRange() {
Range *left = getOperand(0)->range();
Range *right = getOperand(1)->range();
return range()->update(Range::and_(left, right));
}
void computeRange();
};
class MBitOr : public MBinaryBitwiseInstruction
@ -2031,15 +2040,7 @@ class MLsh : public MShiftInstruction
return getOperand(0);
}
bool recomputeRange() {
MDefinition *right = getOperand(1);
if (!right->isConstant())
return false;
int32 c = right->toConstant()->value().toInt32();
const Range *other = getOperand(0)->range();
return range()->update(Range::shl(other, c));
}
void computeRange();
};
class MRsh : public MShiftInstruction
@ -2057,15 +2058,7 @@ class MRsh : public MShiftInstruction
// x >> 0 => x
return getOperand(0);
}
bool recomputeRange() {
MDefinition *right = getOperand(1);
if (!right->isConstant())
return false;
int32 c = right->toConstant()->value().toInt32();
Range *other = getOperand(0)->range();
return range()->update(Range::shr(other, c));
}
void computeRange();
};
class MUrsh : public MShiftInstruction
@ -2140,6 +2133,11 @@ class MBinaryArithInstruction
void infer(JSContext *cx, const TypeOracle::BinaryTypes &b);
void setInt32() {
specialization_ = MIRType_Int32;
setResultType(MIRType_Int32);
}
bool congruentTo(MDefinition *const &ins) const {
return MBinaryInstruction::congruentTo(ins);
}
@ -2226,17 +2224,7 @@ class MAbs
AliasSet getAliasSet() const {
return AliasSet::None();
}
bool recomputeRange() {
if (specialization_ != MIRType_Int32)
return false;
Range *other = getOperand(0)->range();
Range r(0,
Max(Range::abs64((int64_t)other->lower()),
Range::abs64((int64_t)other->upper())));
return range()->update(r);
}
void computeRange();
};
// Inline implementation of Math.sqrt().
@ -2439,18 +2427,8 @@ class MAdd : public MBinaryArithInstruction
return 0;
}
bool fallible() {
return !isTruncated() && !range()->isFinite();
}
bool recomputeRange() {
if (specialization() != MIRType_Int32)
return false;
Range *left = getOperand(0)->range();
Range *right = getOperand(1)->range();
Range next = isTruncated() ? Range::addTruncate(left,right) : Range::add(left, right);
return range()->update(next);
}
bool fallible();
void computeRange();
};
class MSub : public MBinaryArithInstruction
@ -2482,18 +2460,8 @@ class MSub : public MBinaryArithInstruction
return 0;
}
bool fallible() {
return !isTruncated() && !range()->isFinite();
}
bool recomputeRange() {
if (specialization() != MIRType_Int32)
return false;
Range *left = getOperand(0)->range();
Range *right = getOperand(1)->range();
Range next = isTruncated() ? Range::subTruncate(left,right) : Range::sub(left, right);
return range()->update(next);
}
bool fallible();
void computeRange();
};
class MMul : public MBinaryArithInstruction
@ -2540,30 +2508,16 @@ class MMul : public MBinaryArithInstruction
return 1;
}
bool canOverflow() {
return !implicitTruncate_ && !range()->isFinite();
}
bool canOverflow();
bool canBeNegativeZero();
bool canBeNegativeZero() {
if (range()->lower() > 0 || range()->upper() < 0)
return false;
return canBeNegativeZero_;
}
bool updateForReplacement(MDefinition *ins);
bool fallible() {
return canBeNegativeZero_ || canOverflow();
}
bool recomputeRange() {
if (specialization() != MIRType_Int32)
return false;
Range *left = getOperand(0)->range();
Range *right = getOperand(1)->range();
if (isPossibleTruncated())
implicitTruncate_ = !Range::precisionLossMul(left, right);
return range()->update(Range::mul(left, right));
}
void computeRange();
bool isPossibleTruncated() const {
return possibleTruncate_;
@ -2655,21 +2609,7 @@ class MMod : public MBinaryArithInstruction
return 1;
}
bool recomputeRange() {
if (specialization() != MIRType_Int32)
return false;
Range *rhs = getOperand(1)->range();
int64_t a = Range::abs64((int64_t)rhs->lower());
int64_t b = Range::abs64((int64_t)rhs->upper());
if (a ==0 && b == 0) {
// We should never take something % 0.
Range r(INT_MIN, INT_MAX);
return range()->update(r);
}
int64_t bound = Max(1-a, b-1);
Range r(-bound, bound);
return range()->update(r);
}
void computeRange();
};
class MConcat
@ -2709,8 +2649,6 @@ class MCharCodeAt
{
setMovable();
setResultType(MIRType_Int32);
range()->set(0, 65535); //ECMA 262 says that the integer will be
//non-negative and less than 65535.
}
public:
@ -2728,6 +2666,8 @@ class MCharCodeAt
// Strings are immutable, so there is no implicit dependency.
return AliasSet::None();
}
void computeRange();
};
class MFromCharCode
@ -2760,9 +2700,6 @@ class MPhi : public MDefinition, public InlineForwardListNode<MPhi>
bool triedToSpecialize_;
bool hasBytecodeUses_;
bool isIterator_;
// For every input to the phi, track how many times it has changed
// Only used in loop headers, so it defaults to 0 elements to conserve space
js::Vector<RangeChangeCount, 0, IonAllocPolicy> changeCounts_;
MPhi(uint32 slot)
: slot_(slot),
triedToSpecialize_(false),
@ -2819,10 +2756,7 @@ class MPhi : public MDefinition, public InlineForwardListNode<MPhi>
AliasSet getAliasSet() const {
return AliasSet::None();
}
bool recomputeRange();
bool initCounts() {
return changeCounts_.resize(inputs_.length());
}
void computeRange();
};
// The goal of a Beta node is to split a def at a conditionally taken
@ -2830,19 +2764,20 @@ class MPhi : public MDefinition, public InlineForwardListNode<MPhi>
class MBeta : public MUnaryInstruction
{
private:
Range comparison_;
const Range *comparison_;
MDefinition *val_;
MBeta(MDefinition *val, const Range &comp)
MBeta(MDefinition *val, const Range *comp)
: MUnaryInstruction(val),
comparison_(comp),
val_(val)
{
setResultType(val->type());
}
public:
INSTRUCTION_HEADER(Beta);
void printOpcode(FILE *fp);
static MBeta *New(MDefinition *val, const Range &comp)
static MBeta *New(MDefinition *val, const Range *comp)
{
return new MBeta(val, comp);
}
@ -2851,7 +2786,7 @@ class MBeta : public MUnaryInstruction
return AliasSet::None();
}
bool recomputeRange();
void computeRange();
};
// MIR representation of a Value on the OSR StackFrame.
@ -3509,9 +3444,7 @@ class MBoundsCheckLower
AliasSet getAliasSet() const {
return AliasSet::None();
}
bool fallible() {
return range()->lower() < minimum_;
}
bool fallible();
};
// Load a value from a dense array's element vector and does a hole check if the
@ -3987,7 +3920,6 @@ class MClampToUint8
{
setResultType(MIRType_Int32);
setMovable();
range()->set(0, 255);
}
public:
@ -4011,6 +3943,7 @@ class MClampToUint8
AliasSet getAliasSet() const {
return AliasSet::None();
}
void computeRange();
};
class MLoadFixedSlot

View File

@ -30,11 +30,6 @@ typedef InlineForwardListIterator<MPhi> MPhiIterator;
class LBlock;
enum BranchDirection {
FALSE_BRANCH,
TRUE_BRANCH
};
class MBasicBlock : public TempObject, public InlineListNode<MBasicBlock>
{
public:

File diff suppressed because it is too large Load Diff

View File

@ -11,6 +11,7 @@
#include "wtf/Platform.h"
#include "MIR.h"
#include "CompileInfo.h"
#include "IonAnalysis.h"
namespace js {
namespace ion {
@ -18,6 +19,52 @@ namespace ion {
class MBasicBlock;
class MIRGraph;
// An upper bound computed on the number of backedges a loop will take.
// This count only includes backedges taken while running Ion code: for OSR
// loops, this will exclude iterations that executed in the interpreter or in
// baseline compiled code.
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 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;
LoopIterationBound(MBasicBlock *header, MTest *test, LinearSum sum)
: header(header), test(test), sum(sum)
{
}
};
// A symbolic upper or lower bound computed for a term.
struct SymbolicBound : public TempObject
{
// Any loop iteration bound from which this was derived.
//
// If non-NULL, then 'sum' is only valid within the loop body, at points
// dominated by the loop bound's test (see LoopIterationBound).
//
// If NULL, then 'sum' is always valid.
LoopIterationBound *loop;
// Computed symbolic bound, see above.
LinearSum sum;
SymbolicBound(LoopIterationBound *loop, LinearSum sum)
: loop(loop), sum(sum)
{
}
void print(Sprinter &sp) const;
};
class RangeAnalysis
{
protected:
@ -33,16 +80,20 @@ class RangeAnalysis
bool addBetaNobes();
bool analyze();
bool removeBetaNobes();
private:
void analyzeLoop(MBasicBlock *header);
LoopIterationBound *analyzeLoopIterationCount(MBasicBlock *header,
MTest *test, BranchDirection direction);
void analyzeLoopPhi(MBasicBlock *header, LoopIterationBound *loopBound, MPhi *phi);
bool tryHoistBoundsCheck(MBasicBlock *header, MBoundsCheck *ins);
void markBlocksInLoopBody(MBasicBlock *header, MBasicBlock *current);
};
struct RangeChangeCount;
class Range {
class Range : public TempObject {
private:
// :TODO: we should do symbolic range evaluation, where we have
// information of the form v1 < v2 for arbitrary defs v1 and v2, not
// just constants of type int32.
// (Bug 766592)
// Absolute ranges.
//
// We represent ranges where the endpoints can be in the set:
// {-infty} U [INT_MIN, INT_MAX] U {infty}. A bound of +/-
// infty means that the value may have overflowed in that
@ -69,33 +120,38 @@ class Range {
int32 upper_;
bool upper_infinite_;
// Any symbolic lower or upper bound computed for this term.
const SymbolicBound *symbolicLower_;
const SymbolicBound *symbolicUpper_;
public:
Range()
: lower_(JSVAL_INT_MIN),
lower_infinite_(true),
upper_(JSVAL_INT_MAX),
upper_infinite_(true)
upper_infinite_(true),
symbolicLower_(NULL),
symbolicUpper_(NULL)
{}
Range(int64_t l, int64_t h) {
Range(int64_t l, int64_t h)
: symbolicLower_(NULL),
symbolicUpper_(NULL)
{
setLower(l);
setUpper(h);
}
Range(const Range &other)
: lower_(other.lower_),
lower_infinite_(other.lower_infinite_),
upper_(other.upper_),
upper_infinite_(other.upper_infinite_)
: lower_(other.lower_),
lower_infinite_(other.lower_infinite_),
upper_(other.upper_),
upper_infinite_(other.upper_infinite_),
symbolicLower_(NULL),
symbolicUpper_(NULL)
{}
static Range Truncate(int64_t l, int64_t h) {
Range ret(l,h);
if (!ret.isFinite()) {
ret.makeLowerInfinite();
ret.makeUpperInfinite();
}
return ret;
}
static Range *Truncate(int64_t l, int64_t h);
static int64_t abs64(int64_t x) {
#ifdef WTF_OS_WINDOWS
@ -105,7 +161,7 @@ class Range {
#endif
}
void printRange(FILE *fp);
void print(Sprinter &sp) const;
bool update(const Range *other);
bool update(const Range &other) {
return update(&other);
@ -116,15 +172,15 @@ class Range {
// copying when chaining together unions when handling Phi
// nodes.
void unionWith(const Range *other);
static Range intersect(const Range *lhs, const Range *rhs, bool *nullRange);
static Range addTruncate(const Range *lhs, const Range *rhs);
static Range subTruncate(const Range *lhs, const Range *rhs);
static Range add(const Range *lhs, const Range *rhs);
static Range sub(const Range *lhs, const Range *rhs);
static Range mul(const Range *lhs, const Range *rhs);
static Range and_(const Range *lhs, const Range *rhs);
static Range shl(const Range *lhs, int32 c);
static Range shr(const Range *lhs, int32 c);
static Range * intersect(const Range *lhs, const Range *rhs, bool *emptyRange);
static Range * addTruncate(const Range *lhs, const Range *rhs);
static Range * subTruncate(const Range *lhs, const Range *rhs);
static Range * add(const Range *lhs, const Range *rhs);
static Range * sub(const Range *lhs, const Range *rhs);
static Range * mul(const Range *lhs, const Range *rhs);
static Range * and_(const Range *lhs, const Range *rhs);
static Range * shl(const Range *lhs, int32 c);
static Range * shr(const Range *lhs, int32 c);
static bool precisionLossMul(const Range *lhs, const Range *rhs);
@ -184,35 +240,20 @@ class Range {
setLower(l);
setUpper(h);
}
};
struct RangeChangeCount {
Range oldRange;
unsigned char lowerCount_ : 4;
unsigned char upperCount_ : 4;
RangeChangeCount() : oldRange(), lowerCount_(0), upperCount_(0) {};
void updateRange(Range *newRange) {
JS_ASSERT(newRange->lower() >= oldRange.lower());
if (newRange->lower() != oldRange.lower())
lowerCount_ = lowerCount_ < 15 ? lowerCount_ + 1 : lowerCount_;
JS_ASSERT(newRange->upper() <= oldRange.upper());
if (newRange->upper() != oldRange.upper())
upperCount_ = upperCount_ < 15 ? upperCount_ + 1 : upperCount_;
oldRange = *newRange;
const SymbolicBound *symbolicLower() const {
return symbolicLower_;
}
const SymbolicBound *symbolicUpper() const {
return symbolicUpper_;
}
void setSymbolicLower(SymbolicBound *bound) {
symbolicLower_ = bound;
}
void setSymbolicUpper(SymbolicBound *bound) {
symbolicUpper_ = bound;
}
};
class RangeUpdater {
Range r_;
bool lowerSet_;
bool upperSet_;
public:
RangeUpdater() : r_(), lowerSet_(false), upperSet_(false) {}
void unionWith(const Range *other);
void unionWith(RangeChangeCount *other);
void updateLower(const Range * other);
void updateUpper(const Range * other);
Range *getRange() { JS_ASSERT(lowerSet_ && upperSet_); return &r_; }
void printRange(FILE *fp) { r_.printRange(fp); }
};
} // namespace ion