Bug 1246658 part 4 - Replace MConstant's js::Value with a custom union. r=luke
authorJan de Mooij <jdemooij@mozilla.com>
Tue, 16 Feb 2016 12:53:28 +0100
changeset 284344 d170a9874c2b
parent 284343 85b4f57c10f9
child 284345 1493196bb7a0
push id71919
push userjandemooij@gmail.com
push date2016-02-16 11:57 +0000
treeherdermozilla-inbound@1493196bb7a0 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersluke
bugs1246658
milestone47.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 1246658 part 4 - Replace MConstant's js::Value with a custom union. r=luke
js/src/jit/MCallOptimize.cpp
js/src/jit/MIR.cpp
js/src/jit/MIR.h
js/src/jit/RangeAnalysis.cpp
--- a/js/src/jit/MCallOptimize.cpp
+++ b/js/src/jit/MCallOptimize.cpp
@@ -3283,20 +3283,22 @@ IonBuilder::inlineConstructSimdObject(Ca
     // containing the coercion of 'undefined' to the right type.
     MConstant* defVal = nullptr;
     MIRType laneType = SimdTypeToLaneType(simdType);
     if (callInfo.argc() < SimdTypeToLength(simdType)) {
         if (laneType == MIRType_Int32) {
             defVal = constant(Int32Value(0));
         } else if (laneType == MIRType_Boolean) {
             defVal = constant(BooleanValue(false));
+        } else if (laneType == MIRType_Double) {
+            defVal = constant(DoubleNaNValue());
         } else {
-            MOZ_ASSERT(IsFloatingPointType(laneType));
-            defVal = constant(DoubleNaNValue());
-            defVal->setResultType(laneType);
+            MOZ_ASSERT(laneType == MIRType_Float32);
+            defVal = MConstant::NewFloat32(alloc(), GenericNaN());
+            current->add(defVal);
         }
     }
 
     MDefinition* lane[4];
     for (unsigned i = 0; i < 4; i++)
         lane[i] = callInfo.getArgWithDefault(i, defVal);
 
     // Convert boolean lanes into Int32 0 / -1.
--- a/js/src/jit/MIR.cpp
+++ b/js/src/jit/MIR.cpp
@@ -170,18 +170,22 @@ EvaluateExactReciprocal(TempAllocator& a
         return nullptr;
 
     // check if rhs is a power of two
     if (mozilla::Abs(num) & (mozilla::Abs(num) - 1))
         return nullptr;
 
     Value ret;
     ret.setDouble(1.0 / (double) num);
-    MConstant* foldedRhs = MConstant::New(alloc, ret);
-    foldedRhs->setResultType(ins->type());
+    MConstant* foldedRhs;
+    if (ins->type() == MIRType_Float32)
+        foldedRhs = MConstant::NewFloat32(alloc, ret.toDouble());
+    else
+        foldedRhs = MConstant::New(alloc, ret);
+    MOZ_ASSERT(foldedRhs->type() == ins->type());
     ins->block()->insertBefore(ins, foldedRhs);
 
     MMul* mul = MMul::New(alloc, left, foldedRhs, ins->type());
     mul->setCommutative();
     return mul;
 }
 
 void
@@ -655,33 +659,30 @@ MDefinition::emptyResultTypeSet() const
 
 MConstant*
 MConstant::New(TempAllocator& alloc, const Value& v, CompilerConstraintList* constraints)
 {
     return new(alloc) MConstant(v, constraints);
 }
 
 MConstant*
-MConstant::NewTypedValue(TempAllocator& alloc, const Value& v, MIRType type,
-                         CompilerConstraintList* constraints)
-{
-    MOZ_ASSERT(!IsSimdType(type));
-    MOZ_ASSERT_IF(type == MIRType_Float32,
-                  IsNaN(v.toDouble()) || v.toDouble() == double(float(v.toDouble())));
-    MConstant* constant = new(alloc) MConstant(v, constraints);
-    constant->setResultType(type);
-    return constant;
+MConstant::NewFloat32(TempAllocator& alloc, double d)
+{
+    MOZ_ASSERT(IsNaN(d) || d == double(float(d)));
+    return new(alloc) MConstant(float(d));
 }
 
 MConstant*
 MConstant::NewAsmJS(TempAllocator& alloc, const Value& v, MIRType type)
 {
     if (type == MIRType_Float32)
-        return NewTypedValue(alloc, Float32Value(v.toNumber()), type);
-    return NewTypedValue(alloc, v, type);
+        return NewFloat32(alloc, v.toNumber());
+    MConstant* res = New(alloc, v);
+    MOZ_ASSERT(res->type() == type);
+    return res;
 }
 
 MConstant*
 MConstant::NewConstraintlessObject(TempAllocator& alloc, JSObject* v)
 {
     return new(alloc) MConstant(v);
 }
 
@@ -734,66 +735,140 @@ jit::IonCompilationCanUseNurseryPointers
     // or in progress Ion compilations.
     JSRuntime* rt = TlsPerThreadData.get()->runtimeFromMainThread();
     return rt->gc.storeBuffer.cancelIonCompilations();
 }
 
 #endif // DEBUG
 
 MConstant::MConstant(const js::Value& vp, CompilerConstraintList* constraints)
-  : value_(vp)
 {
     setResultType(MIRTypeFromValue(vp));
-    if (vp.isObject()) {
+
+    MOZ_ASSERT(payload_.asBits == 0);
+
+    switch (type()) {
+      case MIRType_Undefined:
+      case MIRType_Null:
+        break;
+      case MIRType_Boolean:
+        payload_.b = vp.toBoolean();
+        break;
+      case MIRType_Int32:
+        payload_.i32 = vp.toInt32();
+        break;
+      case MIRType_Double:
+        payload_.d = vp.toDouble();
+        break;
+      case MIRType_String:
+        MOZ_ASSERT(vp.toString()->isAtom());
+        payload_.str = vp.toString();
+        break;
+      case MIRType_Symbol:
+        payload_.sym = vp.toSymbol();
+        break;
+      case MIRType_Object:
+        payload_.obj = &vp.toObject();
         // Create a singleton type set for the object. This isn't necessary for
         // other types as the result type encodes all needed information.
         MOZ_ASSERT_IF(IsInsideNursery(&vp.toObject()), IonCompilationCanUseNurseryPointers());
         setResultTypeSet(MakeSingletonTypeSet(constraints, &vp.toObject()));
-    }
-    if (vp.isMagic() && vp.whyMagic() == JS_UNINITIALIZED_LEXICAL) {
+        break;
+      case MIRType_MagicOptimizedArguments:
+      case MIRType_MagicOptimizedOut:
+      case MIRType_MagicHole:
+      case MIRType_MagicIsConstructing:
+        break;
+      case MIRType_MagicUninitializedLexical:
         // JS_UNINITIALIZED_LEXICAL does not escape to script and is not
         // observed in type sets. However, it may flow around freely during
         // Ion compilation. Give it an unknown typeset to poison any type sets
         // it merges with.
         //
         // TODO We could track uninitialized lexicals more precisely by tracking
         // them in type sets.
         setResultTypeSet(MakeUnknownTypeSet());
+        break;
+      default:
+        MOZ_CRASH("Unexpected type");
     }
 
-    MOZ_ASSERT_IF(vp.isString(), vp.toString()->isAtom());
-
     setMovable();
 }
 
 MConstant::MConstant(JSObject* obj)
-  : value_(ObjectValue(*obj))
 {
     MOZ_ASSERT_IF(IsInsideNursery(obj), IonCompilationCanUseNurseryPointers());
     setResultType(MIRType_Object);
+    payload_.obj = obj;
     setMovable();
 }
 
+MConstant::MConstant(float f)
+{
+    setResultType(MIRType_Float32);
+    payload_.f = f;
+    setMovable();
+}
+
+#ifdef DEBUG
+void
+MConstant::assertInitializedPayload() const
+{
+    // valueHash() and equals() expect the unused payload bits to be
+    // initialized to zero. Assert this in debug builds.
+
+    switch (type()) {
+      case MIRType_Int32:
+      case MIRType_Float32:
+        MOZ_ASSERT((payload_.asBits >> 32) == 0);
+        break;
+      case MIRType_Boolean:
+        MOZ_ASSERT((payload_.asBits >> 1) == 0);
+        break;
+      case MIRType_Double:
+        break;
+      case MIRType_String:
+      case MIRType_Object:
+      case MIRType_Symbol:
+        MOZ_ASSERT_IF(JS_BITS_PER_WORD == 32, (payload_.asBits >> 32) == 0);
+        break;
+      default:
+        MOZ_ASSERT(IsNullOrUndefined(type()) || IsMagicType(type()));
+        MOZ_ASSERT(payload_.asBits == 0);
+        break;
+    }
+}
+#endif
+
 HashNumber
 MConstant::valueHash() const
 {
+    static_assert(sizeof(Payload) == sizeof(uint64_t),
+                  "Code below assumes payload fits in 64 bits");
+
+    assertInitializedPayload();
+
+    // Build a 64-bit value holding both the payload and the type.
+    static const size_t TypeBits = 8;
+    static const size_t TypeShift = 64 - TypeBits;
+    MOZ_ASSERT(uintptr_t(type()) <= (1 << TypeBits) - 1);
+    uint64_t bits = (uint64_t(type()) << TypeShift) ^ payload_.asBits;
+
     // Fold all 64 bits into the 32-bit result. It's tempting to just discard
     // half of the bits, as this is just a hash, however there are many common
     // patterns of values where only the low or the high bits vary, so
     // discarding either side would lead to excessive hash collisions.
-    uint64_t bits = JSVAL_TO_IMPL(value_).asBits;
     return (HashNumber)bits ^ (HashNumber)(bits >> 32);
 }
 
 bool
 MConstant::congruentTo(const MDefinition* ins) const
 {
-    if (!ins->isConstant())
-        return false;
-    return ins->toConstant()->value_ == value_;
+    return ins->isConstant() && equals(ins->toConstant());
 }
 
 void
 MConstant::printOpcode(GenericPrinter& out) const
 {
     PrintOpcodeName(out, op());
     out.printf(" ");
     switch (type()) {
@@ -875,45 +950,82 @@ MConstant::canProduceFloat32() const
         return IsFloat32Representable(toDouble());
     MOZ_ASSERT(type() == MIRType_Float32);
     return true;
 }
 
 Value
 MConstant::toJSValue() const
 {
-    return value_;
+    // Wasm has types like int64 that cannot be stored as js::Value. It also
+    // doesn't want the NaN canonicalization enforced by js::Value.
+    MOZ_ASSERT(!IsCompilingAsmJS());
+
+    switch (type()) {
+      case MIRType_Undefined:
+        return UndefinedValue();
+      case MIRType_Null:
+        return NullValue();
+      case MIRType_Boolean:
+        return BooleanValue(toBoolean());
+      case MIRType_Int32:
+        return Int32Value(toInt32());
+      case MIRType_Double:
+        return DoubleValue(toDouble());
+      case MIRType_Float32:
+        return Float32Value(toFloat32());
+      case MIRType_String:
+        return StringValue(toString());
+      case MIRType_Symbol:
+        return SymbolValue(toSymbol());
+      case MIRType_Object:
+        return ObjectValue(toObject());
+      case MIRType_MagicOptimizedArguments:
+        return MagicValue(JS_OPTIMIZED_ARGUMENTS);
+      case MIRType_MagicOptimizedOut:
+        return MagicValue(JS_OPTIMIZED_OUT);
+      case MIRType_MagicHole:
+        return MagicValue(JS_ELEMENTS_HOLE);
+      case MIRType_MagicIsConstructing:
+        return MagicValue(JS_IS_CONSTRUCTING);
+      case MIRType_MagicUninitializedLexical:
+        return MagicValue(JS_UNINITIALIZED_LEXICAL);
+      default:
+        MOZ_CRASH("Unexpected type");
+    }
 }
 
 bool
 MConstant::valueToBoolean(bool* res) const
 {
     switch (type()) {
       case MIRType_Boolean:
-        *res = value_.toBoolean();
+        *res = toBoolean();
         return true;
       case MIRType_Int32:
-        *res = value_.toInt32() != 0;
+        *res = toInt32() != 0;
         return true;
       case MIRType_Double:
+        *res = !mozilla::IsNaN(toDouble()) && toDouble() != 0.0;
+        return true;
       case MIRType_Float32:
-        *res = !mozilla::IsNaN(value_.toDouble()) && value_.toDouble() != 0;
+        *res = !mozilla::IsNaN(toFloat32()) && toFloat32() != 0.0f;
         return true;
       case MIRType_Null:
       case MIRType_Undefined:
         *res = false;
         return true;
       case MIRType_Symbol:
         *res = true;
         return true;
       case MIRType_String:
-        *res = value_.toString()->length() != 0;
+        *res = toString()->length() != 0;
         return true;
       case MIRType_Object:
-        *res = !EmulatesUndefined(&value_.toObject());
+        *res = !EmulatesUndefined(&toObject());
         return true;
       default:
         MOZ_ASSERT(IsMagicType(type()));
         return false;
     }
 }
 
 MDefinition*
@@ -1397,17 +1509,17 @@ MMathFunction::foldsTo(TempAllocator& al
       case Round:
         out = js::math_round_impl(in);
         break;
       default:
         return this;
     }
 
     if (input->type() == MIRType_Float32)
-        return MConstant::NewTypedValue(alloc, DoubleValue(out), MIRType_Float32);
+        return MConstant::NewFloat32(alloc, out);
     return MConstant::New(alloc, DoubleValue(out));
 }
 
 MDefinition*
 MAtomicIsLockFree::foldsTo(TempAllocator& alloc)
 {
     MDefinition* input = getOperand(0);
     if (!input->isConstant() || input->type() != MIRType_Int32)
@@ -2692,22 +2804,21 @@ MMinMax::foldsTo(TempAllocator& alloc)
             result = js::math_min_impl(lnum, rnum);
 
         // The folded MConstant should maintain the same MIRType with
         // the original MMinMax.
         if (type() == MIRType_Int32) {
             int32_t cast;
             if (mozilla::NumberEqualsInt32(result, &cast))
                 return MConstant::New(alloc, Int32Value(cast));
+        } else if (type() == MIRType_Float32) {
+            return MConstant::NewFloat32(alloc, result);
         } else {
-            MOZ_ASSERT(IsFloatingPointType(type()));
-            MConstant* constant = MConstant::New(alloc, DoubleValue(result));
-            if (type() == MIRType_Float32)
-                constant->setResultType(MIRType_Float32);
-            return constant;
+            MOZ_ASSERT(type() == MIRType_Double);
+            return MConstant::New(alloc, DoubleValue(result));
         }
     }
 
     MDefinition* operand = lhs()->isConstant() ? rhs() : lhs();
     MConstant* constant = lhs()->isConstant() ? lhs()->toConstant() : rhs()->toConstant();
 
     if (operand->isToDouble() && operand->getOperand(0)->type() == MIRType_Int32) {
         // min(int32, cte >= INT32_MAX) = int32
@@ -3587,22 +3698,19 @@ MToFloat32::foldsTo(TempAllocator& alloc
 
     if (input->type() == MIRType_Float32)
         return input;
 
     // If x is a Float32, Float32(Double(x)) == x
     if (input->isToDouble() && input->toToDouble()->input()->type() == MIRType_Float32)
         return input->toToDouble()->input();
 
-    if (input->isConstant() && input->toConstant()->isNumber()) {
-        float out = float(input->toConstant()->toNumber());
-        MConstant* c = MConstant::New(alloc, DoubleValue(out));
-        c->setResultType(MIRType_Float32);
-        return c;
-    }
+    if (input->isConstant() && input->toConstant()->isNumber())
+        return MConstant::NewFloat32(alloc, float(input->toConstant()->toNumber()));
+
     return this;
 }
 
 MDefinition*
 MToString::foldsTo(TempAllocator& alloc)
 {
     MDefinition* in = input();
     if (in->isBox())
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -1362,28 +1362,51 @@ class MLimitedTruncate
     void setTruncateKind(TruncateKind kind) {
         truncate_ = kind;
     }
 };
 
 // A constant js::Value.
 class MConstant : public MNullaryInstruction
 {
-    Value value_;
+    struct Payload {
+        union {
+            bool b;
+            int32_t i32;
+            float f;
+            double d;
+            JSString* str;
+            JS::Symbol* sym;
+            JSObject* obj;
+            uint64_t asBits;
+        };
+        Payload() : asBits(0) {}
+    };
+
+    Payload payload_;
+
+    static_assert(sizeof(Payload) == sizeof(uint64_t),
+                  "asBits must be big enough for all payload bits");
+
+#ifdef DEBUG
+    void assertInitializedPayload() const;
+#else
+    void assertInitializedPayload() const {}
+#endif
 
   protected:
     MConstant(const Value& v, CompilerConstraintList* constraints);
     explicit MConstant(JSObject* obj);
+    explicit MConstant(float f);
 
   public:
     INSTRUCTION_HEADER(Constant)
     static MConstant* New(TempAllocator& alloc, const Value& v,
                           CompilerConstraintList* constraints = nullptr);
-    static MConstant* NewTypedValue(TempAllocator& alloc, const Value& v, MIRType type,
-                                    CompilerConstraintList* constraints = nullptr);
+    static MConstant* NewFloat32(TempAllocator& alloc, double d);
     static MConstant* NewAsmJS(TempAllocator& alloc, const Value& v, MIRType type);
     static MConstant* NewConstraintlessObject(TempAllocator& alloc, JSObject* v);
 
     // Try to convert this constant to boolean, similar to js::ToBoolean.
     // Returns false if the type is MIRType_Magic*.
     bool valueToBoolean(bool* res) const;
 
     // Like valueToBoolean, but returns the result directly instead of using
@@ -1417,54 +1440,55 @@ class MConstant : public MNullaryInstruc
     void computeRange(TempAllocator& alloc) override;
     bool needTruncation(TruncateKind kind) override;
     void truncate() override;
 
     bool canProduceFloat32() const override;
 
     ALLOW_CLONE(MConstant)
 
-    bool equals(const MConstant* other) {
-        return value_ == other->value_;
+    bool equals(const MConstant* other) const {
+        assertInitializedPayload();
+        return type() == other->type() && payload_.asBits == other->payload_.asBits;
     }
 
     bool toBoolean() const {
         MOZ_ASSERT(type() == MIRType_Boolean);
-        return value_.toBoolean();
+        return payload_.b;
     }
     int32_t toInt32() const {
         MOZ_ASSERT(type() == MIRType_Int32);
-        return value_.toInt32();
+        return payload_.i32;
     }
     bool isInt32(int32_t i) const {
-        return type() == MIRType_Int32 && value_.toInt32() == i;
+        return type() == MIRType_Int32 && payload_.i32 == i;
     }
     double toDouble() const {
         MOZ_ASSERT(type() == MIRType_Double);
-        return value_.toDouble();
+        return payload_.d;
     }
     float toFloat32() const {
         MOZ_ASSERT(type() == MIRType_Float32);
-        return value_.toDouble();
+        return payload_.f;
     }
     JSString* toString() const {
         MOZ_ASSERT(type() == MIRType_String);
-        return value_.toString();
+        return payload_.str;
     }
     JS::Symbol* toSymbol() const {
         MOZ_ASSERT(type() == MIRType_Symbol);
-        return value_.toSymbol();
+        return payload_.sym;
     }
     JSObject& toObject() const {
         MOZ_ASSERT(type() == MIRType_Object);
-        return value_.toObject();
+        return *payload_.obj;
     }
     JSObject* toObjectOrNull() const {
         if (type() == MIRType_Object)
-            return &value_.toObject();
+            return payload_.obj;
         MOZ_ASSERT(type() == MIRType_Null);
         return nullptr;
     }
 
     bool isNumber() const {
         return IsNumberType(type());
     }
     double toNumber() const {
--- a/js/src/jit/RangeAnalysis.cpp
+++ b/js/src/jit/RangeAnalysis.cpp
@@ -2400,27 +2400,28 @@ void
 MDefinition::truncate()
 {
     MOZ_CRASH("No procedure defined for truncating this instruction.");
 }
 
 bool
 MConstant::needTruncation(TruncateKind kind)
 {
-    return value_.isDouble();
+    return IsFloatingPointType(type());
 }
 
 void
 MConstant::truncate()
 {
     MOZ_ASSERT(needTruncation(Truncate));
 
     // Truncate the double to int, since all uses truncates it.
-    int32_t res = ToInt32(value_.toDouble());
-    value_.setInt32(res);
+    int32_t res = ToInt32(toNumber());
+    payload_.asBits = 0;
+    payload_.i32 = res;
     setResultType(MIRType_Int32);
     if (range())
         range()->setInt32(res, res);
 }
 
 bool
 MPhi::needTruncation(TruncateKind kind)
 {