diff --git a/js/src/jit-test/tests/ion/dce-with-rinstructions.js b/js/src/jit-test/tests/ion/dce-with-rinstructions.js index 036a7eab742..bb4300d4846 100644 --- a/js/src/jit-test/tests/ion/dce-with-rinstructions.js +++ b/js/src/jit-test/tests/ion/dce-with-rinstructions.js @@ -419,6 +419,45 @@ function rpowhalf_object(i) { return i; } +var uceFault_min_number = eval(uneval(uceFault).replace('uceFault', 'uceFault_min_number')); +function rmin_number(i) { + var x = Math.min(i, i-1, i-2.1); + if (uceFault_min_number(i) || uceFault_min_number(i)) + assertEq(x, i-2.1); + return i; +} + +var uceFault_min_object = eval(uneval(uceFault).replace('uceFault', 'uceFault_min_object')); +function rmin_object(i) { + var t = i; + var o = { valueOf: function () { return t; } }; + var x = Math.min(o, o-1, o-2.1) + t = 1000; + if (uceFault_min_object(i) || uceFault_min_object(i)) + assertEq(x, i-2.1); + return i; +} + +var uceFault_max_number = eval(uneval(uceFault).replace('uceFault', 'uceFault_max_number')); +function rmax_number(i) { + var x = Math.max(i, i-1, i-2.1); + if (uceFault_max_number(i) || uceFault_max_number(i)) + assertEq(x, i); + return i; +} + +var uceFault_max_object = eval(uneval(uceFault).replace('uceFault', 'uceFault_max_object')); +function rmax_object(i) { + var t = i; + var o = { valueOf: function () { return t; } }; + var x = Math.max(o, o-1, o-2.1) + t = 1000; + if (uceFault_max_object(i) || uceFault_max_object(i)) + assertEq(x, i); + return i; +} + + for (i = 0; i < 100; i++) { rbitnot_number(i); rbitnot_object(i); @@ -464,6 +503,10 @@ for (i = 0; i < 100; i++) { rpow_object(i); rpowhalf_number(i); rpowhalf_object(i); + rmin_number(i); + rmin_object(i); + rmax_number(i); + rmax_object(i); } // Test that we can refer multiple time to the same recover instruction, as well diff --git a/js/src/jit/MIR.h b/js/src/jit/MIR.h index 432cf6f3445..36fc35446c5 100644 --- a/js/src/jit/MIR.h +++ b/js/src/jit/MIR.h @@ -3936,6 +3936,10 @@ class MMinMax return AliasSet::None(); } void computeRange(TempAllocator &alloc); + bool writeRecoverData(CompactBufferWriter &writer) const; + bool canRecoverOnBailout() const { + return true; + } }; class MAbs diff --git a/js/src/jit/Recover.cpp b/js/src/jit/Recover.cpp index 088532ea0ca..7e923244e51 100644 --- a/js/src/jit/Recover.cpp +++ b/js/src/jit/Recover.cpp @@ -710,6 +710,34 @@ RPowHalf::recover(JSContext *cx, SnapshotIterator &iter) const return true; } +bool +MMinMax::writeRecoverData(CompactBufferWriter &writer) const +{ + MOZ_ASSERT(canRecoverOnBailout()); + writer.writeUnsigned(uint32_t(RInstruction::Recover_MinMax)); + writer.writeByte(isMax_); + return true; +} + +RMinMax::RMinMax(CompactBufferReader &reader) +{ + isMax_ = reader.readByte(); +} + +bool +RMinMax::recover(JSContext *cx, SnapshotIterator &iter) const +{ + RootedValue a(cx, iter.read()); + RootedValue b(cx, iter.read()); + RootedValue result(cx); + + if (!js_minmax_impl(cx, isMax_, a, b, &result)) + return false; + + iter.storeInstructionResult(result); + return true; +} + bool MMathFunction::writeRecoverData(CompactBufferWriter &writer) const { diff --git a/js/src/jit/Recover.h b/js/src/jit/Recover.h index 488bd4f4bbb..0cf1fd4dd06 100644 --- a/js/src/jit/Recover.h +++ b/js/src/jit/Recover.h @@ -39,6 +39,7 @@ namespace jit { _(FromCharCode) \ _(Pow) \ _(PowHalf) \ + _(MinMax) \ _(NewObject) \ _(NewDerivedTypedObject) @@ -372,6 +373,21 @@ class RPowHalf MOZ_FINAL : public RInstruction bool recover(JSContext *cx, SnapshotIterator &iter) const; }; +class RMinMax MOZ_FINAL : public RInstruction +{ + private: + bool isMax_; + + public: + RINSTRUCTION_HEADER_(MinMax) + + virtual uint32_t numOperands() const { + return 2; + } + + bool recover(JSContext *cx, SnapshotIterator &iter) const; +}; + class RNewObject MOZ_FINAL : public RInstruction { private: diff --git a/js/src/jsmath.cpp b/js/src/jsmath.cpp index 13eaaa73d46..458d035b80a 100644 --- a/js/src/jsmath.cpp +++ b/js/src/jsmath.cpp @@ -552,6 +552,15 @@ js::math_log(JSContext *cx, unsigned argc, Value *vp) return true; } +static double +max_double(double x, double y) +{ + // Math.max(num, NaN) => NaN, Math.max(-0, +0) => +0 + if (x > y || IsNaN(x) || (x == y && IsNegative(y))) + return x; + return y; +} + bool js_math_max(JSContext *cx, unsigned argc, Value *vp) { @@ -562,14 +571,21 @@ js_math_max(JSContext *cx, unsigned argc, Value *vp) double x; if (!ToNumber(cx, args[i], &x)) return false; - // Math.max(num, NaN) => NaN, Math.max(-0, +0) => +0 - if (x > maxval || IsNaN(x) || (x == maxval && IsNegative(maxval))) - maxval = x; + maxval = max_double(x, maxval); } args.rval().setNumber(maxval); return true; } +static double +min_double(double x, double y) +{ + // Math.min(num, NaN) => NaN, Math.min(-0, +0) => -0 + if (x < y || IsNaN(x) || (x == y && IsNegativeZero(x))) + return x; + return y; +} + bool js_math_min(JSContext *cx, unsigned argc, Value *vp) { @@ -580,14 +596,30 @@ js_math_min(JSContext *cx, unsigned argc, Value *vp) double x; if (!ToNumber(cx, args[i], &x)) return false; - // Math.min(num, NaN) => NaN, Math.min(-0, +0) => -0 - if (x < minval || IsNaN(x) || (x == minval && IsNegativeZero(x))) - minval = x; + minval = min_double(x, minval); } args.rval().setNumber(minval); return true; } +bool +js_minmax_impl(JSContext *cx, bool max, HandleValue a, HandleValue b, MutableHandleValue res) +{ + double x, y; + + if (!ToNumber(cx, a, &x)) + return false; + if (!ToNumber(cx, b, &y)) + return false; + + if (max) + res.setNumber(max_double(x, y)); + else + res.setNumber(min_double(x, y)); + + return true; +} + // Disable PGO for Math.pow() and related functions (see bug 791214). #if defined(_MSC_VER) # pragma optimize("g", off) diff --git a/js/src/jsmath.h b/js/src/jsmath.h index 18b9f744391..ff6fab33ebc 100644 --- a/js/src/jsmath.h +++ b/js/src/jsmath.h @@ -111,11 +111,16 @@ extern bool js_math_sqrt(JSContext *cx, unsigned argc, js::Value *vp); extern bool -js_math_pow_handle(JSContext *cx, js::HandleValue base, js::HandleValue power, js::MutableHandleValue result); +js_math_pow_handle(JSContext *cx, js::HandleValue base, js::HandleValue power, + js::MutableHandleValue result); extern bool js_math_pow(JSContext *cx, unsigned argc, js::Value *vp); +extern bool +js_minmax_impl(JSContext *cx, bool max, js::HandleValue a, js::HandleValue b, + js::MutableHandleValue res); + namespace js { extern bool