Bug 889451 - Implement range analysis for or, xor, not, ursh, abs, min, and max, and better range analysis for shifts and mul. r=nbp

This commit is contained in:
Dan Gohman 2013-07-03 15:07:35 -07:00
parent e6afc1fc30
commit 50849cbe27
4 changed files with 252 additions and 31 deletions

View File

@ -2639,6 +2639,7 @@ class MBitNot
return AliasSet::Store(AliasSet::Any);
return AliasSet::None();
}
void computeRange();
};
class MTypeOf
@ -2784,6 +2785,7 @@ class MBitOr : public MBinaryBitwiseInstruction
MDefinition *foldIfEqual() {
return getOperand(0); // x | x => x
}
void computeRange();
};
class MBitXor : public MBinaryBitwiseInstruction
@ -2806,6 +2808,7 @@ class MBitXor : public MBinaryBitwiseInstruction
MDefinition *foldIfEqual() {
return this;
}
void computeRange();
};
class MShiftInstruction
@ -2912,6 +2915,8 @@ class MUrsh : public MShiftInstruction
bool fallible() {
return canOverflow();
}
void computeRange();
};
class MBinaryArithInstruction
@ -3017,6 +3022,7 @@ class MMinMax
AliasSet getAliasSet() const {
return AliasSet::None();
}
void computeRange();
};
class MAbs
@ -3305,7 +3311,6 @@ class MAdd : public MBinaryArithInstruction
}
return add;
}
void analyzeTruncateBackward();
double getIdentity() {
return 0;

View File

@ -457,6 +457,51 @@ Range::and_(const Range *lhs, const Range *rhs)
return new Range(lower, upper);
}
Range *
Range::or_(const Range *lhs, const Range *rhs)
{
int64_t lower = INT32_MIN;
int64_t upper = INT32_MAX;
// If the sign bits are the same, the result has the same sign.
if (lhs->lower_ >= 0 && rhs->lower_ >= 0)
lower = 0;
else if (lhs->upper_ < 0 || rhs->upper_ < 0)
upper = -1;
return new Range(lower, upper);
}
Range *
Range::xor_(const Range *lhs, const Range *rhs)
{
int64_t lower = INT32_MIN;
int64_t upper = INT32_MAX;
// If the sign bits are identical, the result is non-negative.
if (lhs->lower_ >= 0 && rhs->lower_ >= 0)
lower = 0;
else if (lhs->upper_ < 0 && rhs->upper_ < 0)
lower = 0;
return new Range(lower, upper);
}
Range *
Range::not_(const Range *op)
{
int64_t lower = INT32_MIN;
int64_t upper = INT32_MAX;
// Not inverts all bits, including the sign bit.
if (op->lower_ >= 0)
upper = -1;
else if (op->upper_ < 0)
lower = 0;
return new Range(lower, upper);
}
Range *
Range::mul(const Range *lhs, const Range *rhs)
{
@ -475,16 +520,25 @@ Range::mul(const Range *lhs, const Range *rhs)
}
Range *
Range::shl(const Range *lhs, int32_t c)
Range::lsh(const Range *lhs, int32_t c)
{
int32_t shift = c & 0x1f;
// If the shift doesn't loose bits or shift bits into the sign bit, we
// can simply compute the correct range by shifting.
if (((uint32_t)lhs->lower_ << shift << 1 >> shift >> 1) == lhs->lower_ &&
((uint32_t)lhs->upper_ << shift << 1 >> shift >> 1) == lhs->upper_)
{
return new Range(
(int64_t)lhs->lower_ << shift,
(int64_t)lhs->upper_ << shift);
}
return new Range(INT32_MIN, INT32_MAX);
}
Range *
Range::shr(const Range *lhs, int32_t c)
Range::rsh(const Range *lhs, int32_t c)
{
int32_t shift = c & 0x1f;
return new Range(
@ -492,6 +546,92 @@ Range::shr(const Range *lhs, int32_t c)
(int64_t)lhs->upper_ >> shift);
}
Range *
Range::ursh(const Range *lhs, int32_t c)
{
int32_t shift = c & 0x1f;
// If the value is always non-negative or always negative, we can simply
// compute the correct range by shifting.
if ((lhs->lower_ >= 0 && !lhs->isUpperInfinite()) ||
(lhs->upper_ < 0 && !lhs->isLowerInfinite()))
{
return new Range(
(int64_t)((uint32_t)lhs->lower_ >> shift),
(int64_t)((uint32_t)lhs->upper_ >> shift));
}
// Otherwise return the most general range after the shift.
return new Range(0, (int64_t)(UINT32_MAX >> shift));
}
Range *
Range::lsh(const Range *lhs, const Range *rhs)
{
return new Range(INT32_MIN, INT32_MAX);
}
Range *
Range::rsh(const Range *lhs, const Range *rhs)
{
return new Range(Min(lhs->lower(), 0), Max(lhs->upper(), 0));
}
Range *
Range::ursh(const Range *lhs, const Range *rhs)
{
return new Range(0, (lhs->lower() >= 0 && !lhs->isUpperInfinite()) ? lhs->upper() : UINT32_MAX);
}
Range *
Range::abs(const Range *op)
{
// Get the lower and upper values of the operand, and adjust them
// for infinities. Range's constructor treats any value beyond the
// int32_t range as infinity.
int64_t l = (int64_t)op->lower() - op->isLowerInfinite();
int64_t u = (int64_t)op->upper() + op->isUpperInfinite();
return new Range(Max(Max(int64_t(0), l), -u),
Max(Abs(l), Abs(u)),
op->isDecimal(),
op->exponent());
}
Range *
Range::min(const Range *lhs, const Range *rhs)
{
// Get the lower and upper values of the operand, and adjust them
// for infinities. Range's constructor treats any value beyond the
// int32_t range as infinity.
int64_t leftLower = (int64_t)lhs->lower() - lhs->isLowerInfinite();
int64_t leftUpper = (int64_t)lhs->upper() + lhs->isUpperInfinite();
int64_t rightLower = (int64_t)rhs->lower() - rhs->isLowerInfinite();
int64_t rightUpper = (int64_t)rhs->upper() + rhs->isUpperInfinite();
return new Range(Min(leftLower, rightLower),
Min(leftUpper, rightUpper),
lhs->isDecimal() || rhs->isDecimal(),
Max(lhs->exponent(), rhs->exponent()));
}
Range *
Range::max(const Range *lhs, const Range *rhs)
{
// Get the lower and upper values of the operand, and adjust them
// for infinities. Range's constructor treats any value beyond the
// int32_t range as infinity.
int64_t leftLower = (int64_t)lhs->lower() - lhs->isLowerInfinite();
int64_t leftUpper = (int64_t)lhs->upper() + lhs->isUpperInfinite();
int64_t rightLower = (int64_t)rhs->lower() - rhs->isLowerInfinite();
int64_t rightUpper = (int64_t)rhs->upper() + rhs->isUpperInfinite();
return new Range(Max(leftLower, rightLower),
Max(leftUpper, rightUpper),
lhs->isDecimal() || rhs->isDecimal(),
Max(lhs->exponent(), rhs->exponent()));
}
bool
Range::negativeZeroMul(const Range *lhs, const Range *rhs)
{
@ -650,28 +790,75 @@ MBitAnd::computeRange()
setRange(Range::and_(&left, &right));
}
void
MBitOr::computeRange()
{
Range left(getOperand(0));
Range right(getOperand(1));
setRange(Range::or_(&left, &right));
}
void
MBitXor::computeRange()
{
Range left(getOperand(0));
Range right(getOperand(1));
setRange(Range::xor_(&left, &right));
}
void
MBitNot::computeRange()
{
Range op(getOperand(0));
setRange(Range::not_(&op));
}
void
MLsh::computeRange()
{
MDefinition *right = getOperand(1);
if (!right->isConstant())
return;
Range left(getOperand(0));
Range right(getOperand(1));
int32_t c = right->toConstant()->value().toInt32();
Range other(getOperand(0));
setRange(Range::shl(&other, c));
MDefinition *rhs = getOperand(1);
if (!rhs->isConstant()) {
setRange(Range::lsh(&left, &right));
return;
}
int32_t c = rhs->toConstant()->value().toInt32();
setRange(Range::lsh(&left, c));
}
void
MRsh::computeRange()
{
MDefinition *right = getOperand(1);
if (!right->isConstant())
return;
Range left(getOperand(0));
Range right(getOperand(1));
int32_t c = right->toConstant()->value().toInt32();
Range other(getOperand(0));
setRange(Range::shr(&other, c));
MDefinition *rhs = getOperand(1);
if (!rhs->isConstant()) {
setRange(Range::rsh(&left, &right));
return;
}
int32_t c = rhs->toConstant()->value().toInt32();
setRange(Range::rsh(&left, c));
}
void
MUrsh::computeRange()
{
Range left(getOperand(0));
Range right(getOperand(1));
MDefinition *rhs = getOperand(1);
if (!rhs->isConstant()) {
setRange(Range::ursh(&left, &right));
return;
}
int32_t c = rhs->toConstant()->value().toInt32();
setRange(Range::ursh(&left, c));
}
void
@ -681,17 +868,18 @@ MAbs::computeRange()
return;
Range other(getOperand(0));
setRange(Range::abs(&other));
}
int64_t max = 0;
if (other.isInt32())
max = Max(Abs<int64_t>(other.lower()), Abs<int64_t>(other.upper()));
else
max = RANGE_INF_MAX;
void
MMinMax::computeRange()
{
if (specialization_ != MIRType_Int32 && specialization_ != MIRType_Double)
return;
Range *range = new Range(0, max,
other.isDecimal(),
other.exponent());
setRange(range);
Range left(getOperand(0));
Range right(getOperand(1));
setRange(isMax() ? Range::max(&left, &right) : Range::min(&left, &right));
}
void
@ -719,7 +907,7 @@ MSub::computeRange()
void
MMul::computeRange()
{
if ((specialization() != MIRType_Int32 && specialization() != MIRType_Double) || isTruncated())
if (specialization() != MIRType_Int32 && specialization() != MIRType_Double)
return;
Range left(getOperand(0));
Range right(getOperand(1));
@ -1537,7 +1725,7 @@ RangeAnalysis::truncate()
}
// Set truncated flag if range analysis ensure that it has no
// rounding errors and no freactional part.
// rounding errors and no fractional part.
const Range *r = iter->range();
if (!r || r->hasRoundingErrors())
continue;

View File

@ -215,8 +215,18 @@ class Range : public TempObject {
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_t c);
static Range * shr(const Range *lhs, int32_t c);
static Range * or_(const Range *lhs, const Range *rhs);
static Range * xor_(const Range *lhs, const Range *rhs);
static Range * not_(const Range *op);
static Range * lsh(const Range *lhs, int32_t c);
static Range * rsh(const Range *lhs, int32_t c);
static Range * ursh(const Range *lhs, int32_t c);
static Range * lsh(const Range *lhs, const Range *rhs);
static Range * rsh(const Range *lhs, const Range *rhs);
static Range * ursh(const Range *lhs, const Range *rhs);
static Range * abs(const Range *op);
static Range * min(const Range *lhs, const Range *rhs);
static Range * max(const Range *lhs, const Range *rhs);
static bool negativeZeroMul(const Range *lhs, const Range *rhs);

View File

@ -0,0 +1,18 @@
/*
js> (((-1 >>> 1) + 1) * Math.pow(2, 52 - 30) + 1) & 1
0
js> (((-1 >> 1) + 1) * Math.pow(2, 52 - 30) + 1) & 1
1
*/
function f(x) {
if (x >= 0) {
// if it does not fail, try with lower power of 2.
return (((x >>> 1) + 1) * 4194304 /* 2 ** (52 - 30) */ + 1) & 1;
}
return 2;
}
assertEq(f(-1 >>> 1), 1);
assertEq(f(-1 >>> 0), 0);
assertEq(f(-1 >>> 0), 0);