Bug 1053788: Activate float32 specialization of min/max; r=nbp
authorBenjamin Bouvier <benj@benj.me>
Fri, 19 Sep 2014 14:39:53 +0200
changeset 206198 4dda6de2f09a8b5473ddd46312c703f03e4beaf4
parent 206197 bad63e9d0b19aaf3bb0c5f3a2dd84b885f55e316
child 206199 b1b54c2c878582ba959229aa28d0bdc1ab160723
push id49368
push userbenj@benj.me
push dateFri, 19 Sep 2014 12:42:58 +0000
treeherdermozilla-inbound@b1b54c2c8785 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnbp
bugs1053788
milestone35.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1053788: Activate float32 specialization of min/max; r=nbp
js/src/jit-test/tests/ion/testFloat32-correctness.js
js/src/jit-test/tests/ion/testFloat32.js
js/src/jit/MIR.cpp
js/src/jit/MIR.h
--- a/js/src/jit-test/tests/ion/testFloat32-correctness.js
+++ b/js/src/jit-test/tests/ion/testFloat32-correctness.js
@@ -84,16 +84,48 @@ function sqrt() {
     for(var i = 0; i < 7; ++i) {
         var sqrtf = Math.fround(Math.sqrt(f32[i]));
         var sqrtd = 1 + Math.sqrt(f32[i]) - 1; // force no float32 by chaining arith ops
         assertEq( sqrtf, Math.fround(sqrtd) );
     }
 }
 test(setupSqrt, sqrt);
 
+// MMinMax
+function setupMinMax() {
+    f32[0] = -0;
+    f32[1] = 0;
+    f32[2] = 1;
+    f32[3] = 4;
+    f32[4] = -1;
+    f32[5] = Infinity;
+    f32[6] = NaN;
+    f32[7] = 13.37;
+    f32[8] = -Infinity;
+    f32[9] = Math.pow(2,31) - 1;
+}
+function minMax() {
+    for(var i = 0; i < 9; ++i) {
+        for(var j = 0; j < 9; j++) {
+            var minf = Math.fround(Math.min(f32[i], f32[j]));
+            var mind = 1 / (1 / Math.min(f32[i], f32[j])); // force no float32 by chaining arith ops
+            assertFloat32(minf, true);
+            assertFloat32(mind, false);
+            assertEq( minf, Math.fround(mind) );
+
+            var maxf = Math.fround(Math.max(f32[i], f32[j]));
+            var maxd = 1 / (1 / Math.max(f32[i], f32[j])); // force no float32 by chaining arith ops
+            assertFloat32(maxf, true);
+            assertFloat32(maxd, false);
+            assertEq( maxf, Math.fround(maxd) );
+        }
+    }
+}
+test(setupMinMax, minMax);
+
 // MTruncateToInt32
 // The only way to get a MTruncateToInt32 with a Float32 input is to use Math.imul
 function setupTruncateToInt32() {
     f32[0] = -1;
     f32[1] = 4;
     f32[2] = 5.13;
 }
 function truncateToInt32() {
--- a/js/src/jit-test/tests/ion/testFloat32.js
+++ b/js/src/jit-test/tests/ion/testFloat32.js
@@ -56,18 +56,18 @@
 setJitCompilerOption("ion.warmup.trigger", 50);
 
 function test(f) {
     f32[0] = .5;
     for(var n = 110; n; n--)
         f();
 }
 
-var f32 = new Float32Array(2);
-var f64 = new Float64Array(2);
+var f32 = new Float32Array(4);
+var f64 = new Float64Array(4);
 
 function acceptAdd() {
     var use = f32[0] + 1;
     assertFloat32(use, true);
     f32[0] = use;
 }
 test(acceptAdd);
 
@@ -146,16 +146,89 @@ test(acceptSqrt);
 
 function refuseSqrt() {
     var res = Math.sqrt(f32[0]);
     assertFloat32(res, false);
     f32[0] = res + 1;
 }
 test(refuseSqrt);
 
+function acceptMin() {
+    var res = Math.min(f32[0], f32[1]);
+    assertFloat32(res, true);
+    f64[0] = res;
+}
+test(acceptMin);
+
+// In theory, we could do it, as Math.min/max actually behave as a Phi (it's a
+// float32 producer iff its inputs are producers, it's a consumer iff its uses
+// are consumers). In practice, this would involve some overhead for big chains
+// of min/max.
+function refuseMinAdd() {
+    var res = Math.min(f32[0], f32[1]) + f32[2];
+    assertFloat32(res, false);
+    f32[3] = res;
+}
+test(refuseMinAdd);
+
+function acceptSeveralMinMax() {
+    var x = Math.min(f32[0], f32[1]);
+    var y = Math.max(f32[2], f32[3]);
+    var res = Math.min(x, y);
+    assertFloat32(res, true);
+    f64[0] = res;
+}
+test(acceptSeveralMinMax);
+
+function acceptSeveralMinMax2() {
+    var res = Math.min(f32[0], f32[1], f32[2], f32[3]);
+    assertFloat32(res, true);
+    f64[0] = res;
+}
+test(acceptSeveralMinMax2);
+
+function partialMinMax() {
+    var x = Math.min(f32[0], f32[1]);
+    var y = Math.min(f64[0], f32[1]);
+    var res  = Math.min(x, y);
+    assertFloat32(x, true);
+    assertFloat32(y, false);
+    assertFloat32(res, false);
+    f64[0] = res;
+}
+test(partialMinMax);
+
+function refuseSeveralMinMax() {
+    var res = Math.min(f32[0], f32[1] + f32[2], f32[2], f32[3]);
+    assertFloat32(res, false);
+    f64[0] = res;
+}
+test(refuseSeveralMinMax);
+
+function refuseMin() {
+    var res = Math.min(f32[0], 42.13 + f32[1]);
+    assertFloat32(res, false);
+    f64[0] = res;
+}
+test(refuseMin);
+
+function acceptMax() {
+    var res = Math.max(f32[0], f32[1]);
+    assertFloat32(res, true);
+    f64[0] = res;
+}
+test(acceptMax);
+
+function refuseMax() {
+    var res = Math.max(f32[0], 42.13 + f32[1]);
+    assertFloat32(res, false);
+    f64[0] = res;
+}
+test(refuseMax);
+
 function acceptAbs() {
     var res = Math.abs(f32[0]);
     assertFloat32(res, true);
     f32[0] = res;
 }
 test(acceptAbs);
 
 function refuseAbs() {
--- a/js/src/jit/MIR.cpp
+++ b/js/src/jit/MIR.cpp
@@ -1693,18 +1693,41 @@ MBinaryArithInstruction::trySpecializeFl
 {
     // Do not use Float32 if we can use int32.
     if (specialization_ == MIRType_Int32)
         return;
 
     MDefinition *left = lhs();
     MDefinition *right = rhs();
 
-    if (!left->canProduceFloat32() || !right->canProduceFloat32()
-        || !CheckUsesAreFloat32Consumers(this))
+    if (!left->canProduceFloat32() || !right->canProduceFloat32() ||
+        !CheckUsesAreFloat32Consumers(this))
+    {
+        if (left->type() == MIRType_Float32)
+            ConvertDefinitionToDouble<0>(alloc, left, this);
+        if (right->type() == MIRType_Float32)
+            ConvertDefinitionToDouble<1>(alloc, right, this);
+        return;
+    }
+
+    specialization_ = MIRType_Float32;
+    setResultType(MIRType_Float32);
+}
+
+void
+MMinMax::trySpecializeFloat32(TempAllocator &alloc)
+{
+    if (specialization_ == MIRType_Int32)
+        return;
+
+    MDefinition *left = lhs();
+    MDefinition *right = rhs();
+
+    if (!(left->canProduceFloat32() || (left->isMinMax() && left->type() == MIRType_Float32)) ||
+        !(right->canProduceFloat32() || (right->isMinMax() && right->type() == MIRType_Float32)))
     {
         if (left->type() == MIRType_Float32)
             ConvertDefinitionToDouble<0>(alloc, left, this);
         if (right->type() == MIRType_Float32)
             ConvertDefinitionToDouble<1>(alloc, right, this);
         return;
     }
 
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -4569,16 +4569,19 @@ class MMinMax
         return AliasSet::None();
     }
     void computeRange(TempAllocator &alloc);
     bool writeRecoverData(CompactBufferWriter &writer) const;
     bool canRecoverOnBailout() const {
         return true;
     }
 
+    bool isFloat32Commutative() const { return true; }
+    void trySpecializeFloat32(TempAllocator &alloc);
+
     ALLOW_CLONE(MMinMax)
 };
 
 class MAbs
   : public MUnaryInstruction,
     public ArithPolicy::Data
 {
     bool implicitTruncate_;