diff --git a/js/src/ion/MIR.cpp b/js/src/ion/MIR.cpp index d44f757bb793..b2c3f1014d20 100644 --- a/js/src/ion/MIR.cpp +++ b/js/src/ion/MIR.cpp @@ -958,9 +958,20 @@ MMul::analyzeEdgeCasesBackward() canBeNegativeZero_ = NeedNegativeZeroCheck(this); } -bool -MMul::updateForReplacement(MDefinition *ins) +void +MMul::analyzeTruncateBackward() { + if (!isPossibleTruncated()) + setPossibleTruncated(js::ion::EdgeCaseAnalysis::AllUsesTruncate(this)); +} + +bool +MMul::updateForReplacement(MDefinition *ins_) +{ + JS_ASSERT(ins_->isMul()); + MMul *ins = ins_->toMul(); + if (isPossibleTruncated()) + setPossibleTruncated(ins->isPossibleTruncated()); return true; } diff --git a/js/src/ion/MIR.h b/js/src/ion/MIR.h index f235c2c5039c..ec39c856ec34 100644 --- a/js/src/ion/MIR.h +++ b/js/src/ion/MIR.h @@ -2498,11 +2498,24 @@ class MSub : public MBinaryArithInstruction class MMul : public MBinaryArithInstruction { + // Annotation the result could be a negative zero + // and we need to guard this during execution. bool canBeNegativeZero_; + // Annotation the result of this Mul is only used in int32 domain + // and we could possible truncate the result. + bool possibleTruncate_; + + // Annotation the Mul can truncate. This is only set after range analysis, + // because the result could be in the imprecise double range. + // In that case the truncated result isn't correct. + bool implicitTruncate_; + MMul(MDefinition *left, MDefinition *right, MIRType type) : MBinaryArithInstruction(left, right), - canBeNegativeZero_(true) + canBeNegativeZero_(true), + possibleTruncate_(false), + implicitTruncate_(false) { if (type != MIRType_Value) specialization_ = type; @@ -2521,13 +2534,14 @@ class MMul : public MBinaryArithInstruction MDefinition *foldsTo(bool useValueNumbers); void analyzeEdgeCasesForward(); void analyzeEdgeCasesBackward(); + void analyzeTruncateBackward(); double getIdentity() { return 1; } bool canOverflow() { - return !range()->isFinite(); + return !implicitTruncate_ && !range()->isFinite(); } bool canBeNegativeZero() { @@ -2546,8 +2560,18 @@ class MMul : public MBinaryArithInstruction 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)); } + + bool isPossibleTruncated() const { + return possibleTruncate_; + } + + void setPossibleTruncated(bool truncate) { + possibleTruncate_ = truncate; + } }; class MDiv : public MBinaryArithInstruction diff --git a/js/src/ion/RangeAnalysis.cpp b/js/src/ion/RangeAnalysis.cpp index 8ac284c5e1b3..1edccd6a7301 100644 --- a/js/src/ion/RangeAnalysis.cpp +++ b/js/src/ion/RangeAnalysis.cpp @@ -417,6 +417,24 @@ Range::shr(const Range *lhs, int32 c) return ret; } +bool +Range::precisionLossMul(const Range *lhs, const Range *rhs) +{ + int64_t loss = 1LL<<53; // result must be lower than 2^53 + int64_t a = (int64_t)lhs->lower_ * (int64_t)rhs->lower_; + int64_t b = (int64_t)lhs->lower_ * (int64_t)rhs->upper_; + int64_t c = (int64_t)lhs->upper_ * (int64_t)rhs->lower_; + int64_t d = (int64_t)lhs->upper_ * (int64_t)rhs->upper_; + int64_t lower = Min( Min(a, b), Min(c, d) ); + int64_t upper = Max( Max(a, b), Max(c, d) ); + if (lower < 0) + lower = -lower; + if (upper < 0) + upper = -upper; + + return lower > loss || upper > loss; +} + bool Range::update(const Range *other) { diff --git a/js/src/ion/RangeAnalysis.h b/js/src/ion/RangeAnalysis.h index 2d755656466f..b60f6857745f 100644 --- a/js/src/ion/RangeAnalysis.h +++ b/js/src/ion/RangeAnalysis.h @@ -126,6 +126,8 @@ class Range { 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); + inline void makeLowerInfinite() { lower_infinite_ = true; lower_ = JSVAL_INT_MIN; diff --git a/js/src/jit-test/tests/ion/bug809472.js b/js/src/jit-test/tests/ion/bug809472.js new file mode 100644 index 000000000000..662d6e524cac --- /dev/null +++ b/js/src/jit-test/tests/ion/bug809472.js @@ -0,0 +1,19 @@ +function test1(x) { + return (x*((2<<23)-1))|0 +} +function test2(x) { + return (x*((2<<22)-1))|0 +} +function test3(x) { + return (x*((2<<21)-1))|0 +} +function test4(x) { + var b = x + x + 3 + return (b*b) | 0 +} +//MAX_INT +var x = 0x7ffffffe; +assertEq(test1(x), 2113929216); +assertEq(test2(x), 2130706434); +assertEq(test3(x), 2139095042); +assertEq(test4(x), 0);