mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Add symbolic range analysis for loop induction variables, bug 766592. r=mjrosenb
This commit is contained in:
parent
229b6c663a
commit
dc4f919839
@ -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())
|
||||
|
@ -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_);
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
|
@ -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())
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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"
|
||||
|
@ -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;
|
||||
}
|
||||
|
179
js/src/ion/MIR.h
179
js/src/ion/MIR.h
@ -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
|
||||
|
@ -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
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user