Bug 1078696 - Recovered store instructions should prevent Float32 operations. r=bbouvier
authorNicolas B. Pierron <nicolas.b.pierron@mozilla.com>
Wed, 15 Oct 2014 11:24:02 +0200
changeset 210522 f4be6b8ddbe28ef99dc273b08586dca0fd1638af
parent 210521 7d5bc73600d2df25fa1ca5a2a6262988e06d0c3b
child 210523 baf095348b7e79ca5c77665d82e51a15ab109bd8
push id27654
push userryanvm@gmail.com
push dateWed, 15 Oct 2014 18:31:27 +0000
treeherdermozilla-central@a280a03c9f3c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbbouvier
bugs1078696
milestone36.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 1078696 - Recovered store instructions should prevent Float32 operations. r=bbouvier
js/src/jit-test/tests/ion/test-scalar-replacement-float32.js
js/src/jit/MIR.h
js/src/jit/TypePolicy.cpp
js/src/jit/TypePolicy.h
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/ion/test-scalar-replacement-float32.js
@@ -0,0 +1,98 @@
+setJitCompilerOption("ion.warmup.trigger", 30);
+var max = 40;
+
+// This test case verify that even if we do some scalar replacement, we keep a
+// correct computation of Float32 maths.  In this case, when the object is not
+// escaped, the "store" instruction are preventing any float32 optimization to
+// kick-in.  After Scalar Replacement, the store is removed, and the Float32
+// optimizations can avoid Double coercions.
+function escape_object(o) {
+    if (o.e) {
+        print(o);
+    }
+}
+
+var func = null;
+var check_object_argument_func = function (i, res) {
+    with ({}) { /* trun off the jit for this function, do not inline */ };
+    if (i == max - 1)
+        return funname.arguments[1].d;
+    return res;
+};
+
+var test_object_ref_check = eval(uneval(check_object_argument_func).replace("funname", "test_object_ref"));
+function test_object_ref(x, tmp) {
+    tmp = {
+        a: Math.fround(Math.pow(2 * x / max, 0)),
+        b: Math.fround(Math.pow(2 * x / max, 25)),
+        c: Math.fround(Math.pow(2 * x / max, 50)),
+        d: 0
+    };
+
+    tmp.d = tmp.a + tmp.b;
+    assertFloat32(tmp.d, false);
+    escape_object(tmp);
+    return test_object_ref_check(x, Math.fround(tmp.c + Math.fround(tmp.d)));
+}
+
+var test_object_check = eval(uneval(check_object_argument_func).replace("funname", "test_object"));
+function test_object(x, tmp) {
+    tmp = {
+        a: Math.fround(Math.pow(2 * x / max, 0)),
+        b: Math.fround(Math.pow(2 * x / max, 25)),
+        c: Math.fround(Math.pow(2 * x / max, 50)),
+        d: 0
+    };
+
+    tmp.d = tmp.a + tmp.b;
+    assertFloat32(tmp.d, false);
+    return test_object_check(x, Math.fround(tmp.c + Math.fround(tmp.d)));
+}
+
+// Same test with Arrays.
+function escape_array(o) {
+    if (o.length == 0) {
+        print(o);
+    }
+}
+
+var check_array_argument_func = function (i, res) {
+    with ({}) { /* trun off the jit for this function, do not inline */ };
+    if (i == max - 1) {
+        return funname.arguments[1][3];
+    }
+    return res;
+};
+
+var test_array_ref_check = eval(uneval(check_array_argument_func).replace("funname", "test_array_ref"));
+function test_array_ref(x, tmp) {
+    tmp = [
+        Math.fround(Math.pow(2 * x / max, 0)),
+        Math.fround(Math.pow(2 * x / max, 25)),
+        Math.fround(Math.pow(2 * x / max, 50)),
+        0
+    ];
+    tmp[3] = tmp[0] + tmp[1];
+    assertFloat32(tmp[3], false);
+    escape_array(tmp);
+    return test_array_ref_check(x, Math.fround(tmp[2] + Math.fround(tmp[3])));
+}
+
+var test_array_check = eval(uneval(check_array_argument_func).replace("funname", "test_array"));
+function test_array(x, tmp) {
+    tmp = [
+        Math.fround(Math.pow(2 * x / max, 0)),
+        Math.fround(Math.pow(2 * x / max, 25)),
+        Math.fround(Math.pow(2 * x / max, 50)),
+        0
+    ];
+    tmp[3] = tmp[0] + tmp[1];
+    assertFloat32(tmp[3], false);
+    return test_array_check(x, Math.fround(tmp[2] + Math.fround(tmp[3])));
+}
+
+
+for (var i = 0; i < max; i++) {
+    assertEq(test_object_ref(i, undefined), test_object(i, undefined));
+    assertEq(test_array_ref(i, undefined), test_array(i, undefined));
+}
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -2663,17 +2663,19 @@ class MNewDerivedTypedObject
     bool writeRecoverData(CompactBufferWriter &writer) const;
     bool canRecoverOnBailout() const {
         return true;
     }
 };
 
 // Represent the content of all slots of an object.  This instruction is not
 // lowered and is not used to generate code.
-class MObjectState : public MVariadicInstruction
+class MObjectState
+  : public MVariadicInstruction,
+    public NoFloatPolicyAfter<1>::Data
 {
   private:
     uint32_t numSlots_;
     uint32_t numFixedSlots_;
 
     explicit MObjectState(MDefinition *obj);
 
     bool init(TempAllocator &alloc, MDefinition *obj);
@@ -2725,17 +2727,19 @@ class MObjectState : public MVariadicIns
     bool writeRecoverData(CompactBufferWriter &writer) const;
     bool canRecoverOnBailout() const {
         return true;
     }
 };
 
 // Represent the contents of all elements of an array.  This instruction is not
 // lowered and is not used to generate code.
-class MArrayState : public MVariadicInstruction
+class MArrayState
+  : public MVariadicInstruction,
+    public NoFloatPolicyAfter<2>::Data
 {
   private:
     uint32_t numElements_;
 
     explicit MArrayState(MDefinition *arr)
     {
         // This instruction is only used as a summary for bailout paths.
         setResultType(MIRType_Object);
--- a/js/src/jit/TypePolicy.cpp
+++ b/js/src/jit/TypePolicy.cpp
@@ -499,16 +499,28 @@ NoFloatPolicy<Op>::staticAdjustInputs(Te
     return true;
 }
 
 template bool NoFloatPolicy<0>::staticAdjustInputs(TempAllocator &alloc, MInstruction *def);
 template bool NoFloatPolicy<1>::staticAdjustInputs(TempAllocator &alloc, MInstruction *def);
 template bool NoFloatPolicy<2>::staticAdjustInputs(TempAllocator &alloc, MInstruction *def);
 template bool NoFloatPolicy<3>::staticAdjustInputs(TempAllocator &alloc, MInstruction *def);
 
+template <unsigned FirstOp>
+bool
+NoFloatPolicyAfter<FirstOp>::adjustInputs(TempAllocator &alloc, MInstruction *def)
+{
+    for (size_t op = FirstOp, e = def->numOperands(); op < e; op++)
+        EnsureOperandNotFloat32(alloc, def, op);
+    return true;
+}
+
+template bool NoFloatPolicyAfter<1>::adjustInputs(TempAllocator &alloc, MInstruction *def);
+template bool NoFloatPolicyAfter<2>::adjustInputs(TempAllocator &alloc, MInstruction *def);
+
 template <unsigned Op>
 bool
 BoxPolicy<Op>::staticAdjustInputs(TempAllocator &alloc, MInstruction *ins)
 {
     MDefinition *in = ins->getOperand(Op);
     if (in->type() == MIRType_Value)
         return true;
 
@@ -951,16 +963,18 @@ FilterTypeSetPolicy::adjustInputs(TempAl
     _(MixPolicy<ObjectPolicy<0>, NoFloatPolicy<2> >)                    \
     _(MixPolicy<ObjectPolicy<0>, NoFloatPolicy<3> >)                    \
     _(MixPolicy<ObjectPolicy<0>, ObjectPolicy<1> >)                     \
     _(MixPolicy<ObjectPolicy<0>, StringPolicy<1> >)                     \
     _(MixPolicy<ObjectPolicy<1>, ConvertToStringPolicy<0> >)            \
     _(MixPolicy<StringPolicy<0>, IntPolicy<1> >)                        \
     _(MixPolicy<StringPolicy<0>, StringPolicy<1> >)                     \
     _(NoFloatPolicy<0>)                                                 \
+    _(NoFloatPolicyAfter<1>)                                            \
+    _(NoFloatPolicyAfter<2>)                                            \
     _(ObjectPolicy<0>)                                                  \
     _(ObjectPolicy<1>)                                                  \
     _(ObjectPolicy<3>)                                                  \
     _(StringPolicy<0>)
 
 
 namespace js {
 namespace jit {
--- a/js/src/jit/TypePolicy.h
+++ b/js/src/jit/TypePolicy.h
@@ -228,16 +228,26 @@ class NoFloatPolicy : public TypePolicy
   public:
     EMPTY_DATA_;
     static bool staticAdjustInputs(TempAllocator &alloc, MInstruction *def);
     bool adjustInputs(TempAllocator &alloc, MInstruction *def) {
         return staticAdjustInputs(alloc, def);
     }
 };
 
+// Policy for guarding variadic instructions such as object / array state
+// instructions.
+template <unsigned FirstOp>
+class NoFloatPolicyAfter : public TypePolicy
+{
+  public:
+    EMPTY_DATA_;
+    bool adjustInputs(TempAllocator &alloc, MInstruction *ins);
+};
+
 // Box objects or strings as an input to a ToDouble instruction.
 class ToDoublePolicy : public BoxInputsPolicy
 {
   public:
     EMPTY_DATA_;
     static bool staticAdjustInputs(TempAllocator &alloc, MInstruction *def);
     bool adjustInputs(TempAllocator &alloc, MInstruction *def) {
         return staticAdjustInputs(alloc, def);