Bug 899309 - modify JS::Value and some helper functions to be constexpr-foldable; r=luke
authorNathan Froyd <froydnj@mozilla.com>
Mon, 29 Jul 2013 16:59:34 -0400
changeset 142145 1adc4b65b54b89dcb9a606057c4a54bc4fdf302d
parent 142144 928f0878d1cdef204feaed4e66a8f4baadf09d8f
child 142146 b0e1bdb61af3b17d8ce74de0dd4d18872bf7ac6c
push id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
reviewersluke
bugs899309
milestone26.0a1
Bug 899309 - modify JS::Value and some helper functions to be constexpr-foldable; r=luke
js/public/Value.h
--- a/js/public/Value.h
+++ b/js/public/Value.h
@@ -328,30 +328,71 @@ typedef union jsval_layout
     size_t asWord;
     uintptr_t asUIntPtr;
 } JSVAL_ALIGNMENT jsval_layout;
 # endif /* JS_BITS_PER_WORD */
 #endif  /* defined(IS_LITTLE_ENDIAN) */
 
 JS_STATIC_ASSERT(sizeof(jsval_layout) == 8);
 
+/*
+ * For codesize purposes on some platforms, it's important that the
+ * compiler know that JS::Values constructed from constant values can be
+ * folded to constant bit patterns at compile time, rather than
+ * constructed at runtime.  Doing this requires a fair amount of C++11
+ * features, which are not supported on all of our compilers.  Set up
+ * some defines and helper macros in an attempt to confine the ugliness
+ * here, rather than scattering it all about the file.  The important
+ * features are:
+ *
+ * - constexpr;
+ * - defaulted functions;
+ * - C99-style designated initializers.
+ */
+#if defined(__clang__)
+#  if __has_feature(cxx_constexpr) && __has_feature(cxx_defaulted_functions)
+#    define JS_VALUE_IS_CONSTEXPR
+#  endif
+#elif defined(__GNUC__)
+/*
+ * We need 4.5 for defaulted functions, 4.6 for constexpr, 4.7 because 4.6
+ * doesn't understand |(X) { .field = ... }| syntax, and 4.7.3 because
+ * versions prior to that have bugs in the C++ front-end that cause crashes.
+ */
+#  if MOZ_GCC_VERSION_AT_LEAST(4, 7, 3)
+#    define JS_VALUE_IS_CONSTEXPR
+#  endif
+#endif
+
+#if defined(JS_VALUE_IS_CONSTEXPR)
+#  define JS_RETURN_LAYOUT_FROM_BITS(BITS) \
+    return (jsval_layout) { .asBits = (BITS) }
+#  define JS_VALUE_CONSTEXPR MOZ_CONSTEXPR
+#  define JS_VALUE_CONSTEXPR_VAR MOZ_CONSTEXPR_VAR
+#else
+#  define JS_RETURN_LAYOUT_FROM_BITS(BITS) \
+    jsval_layout l;                        \
+    l.asBits = (BITS);                     \
+    return l;
+#  define JS_VALUE_CONSTEXPR
+#  define JS_VALUE_CONSTEXPR_VAR const
+#endif
+
 #if JS_BITS_PER_WORD == 32
 
 /*
  * N.B. GCC, in some but not all cases, chooses to emit signed comparison of
  * JSValueTag even though its underlying type has been forced to be uint32_t.
  * Thus, all comparisons should explicitly cast operands to uint32_t.
  */
 
-static inline jsval_layout
+static inline JS_VALUE_CONSTEXPR jsval_layout
 BUILD_JSVAL(JSValueTag tag, uint32_t payload)
 {
-    jsval_layout l;
-    l.asBits = (((uint64_t)(uint32_t)tag) << 32) | payload;
-    return l;
+    JS_RETURN_LAYOUT_FROM_BITS((((uint64_t)(uint32_t)tag) << 32) | payload);
 }
 
 static inline bool
 JSVAL_IS_DOUBLE_IMPL(jsval_layout l)
 {
     return (uint32_t)l.s.tag <= (uint32_t)JSVAL_TAG_CLEAR;
 }
 
@@ -371,23 +412,27 @@ JSVAL_IS_INT32_IMPL(jsval_layout l)
 }
 
 static inline int32_t
 JSVAL_TO_INT32_IMPL(jsval_layout l)
 {
     return l.s.payload.i32;
 }
 
-static inline jsval_layout
+static inline JS_VALUE_CONSTEXPR jsval_layout
 INT32_TO_JSVAL_IMPL(int32_t i)
 {
+#if defined(JS_VALUE_IS_CONSTEXPR)
+    return BUILD_JSVAL(JSVAL_TAG_INT32, i);
+#else
     jsval_layout l;
     l.s.tag = JSVAL_TAG_INT32;
     l.s.payload.i32 = i;
     return l;
+#endif
 }
 
 static inline bool
 JSVAL_IS_NUMBER_IMPL(jsval_layout l)
 {
     JSValueTag tag = l.s.tag;
     MOZ_ASSERT(tag != JSVAL_TAG_CLEAR);
     return (uint32_t)tag <= (uint32_t)JSVAL_UPPER_INCL_TAG_OF_NUMBER_SET;
@@ -564,22 +609,20 @@ JSVAL_EXTRACT_NON_DOUBLE_TYPE_IMPL(jsval
 {
     uint32_t type = l.s.tag & 0xF;
     MOZ_ASSERT(type > JSVAL_TYPE_DOUBLE);
     return (JSValueType)type;
 }
 
 #elif JS_BITS_PER_WORD == 64
 
-static inline jsval_layout
+static inline JS_VALUE_CONSTEXPR jsval_layout
 BUILD_JSVAL(JSValueTag tag, uint64_t payload)
 {
-    jsval_layout l;
-    l.asBits = (((uint64_t)(uint32_t)tag) << JSVAL_TAG_SHIFT) | payload;
-    return l;
+    JS_RETURN_LAYOUT_FROM_BITS((((uint64_t)(uint32_t)tag) << JSVAL_TAG_SHIFT) | payload);
 }
 
 static inline bool
 JSVAL_IS_DOUBLE_IMPL(jsval_layout l)
 {
     return l.asBits <= JSVAL_SHIFTED_TAG_MAX_DOUBLE;
 }
 
@@ -599,22 +642,20 @@ JSVAL_IS_INT32_IMPL(jsval_layout l)
 }
 
 static inline int32_t
 JSVAL_TO_INT32_IMPL(jsval_layout l)
 {
     return (int32_t)l.asBits;
 }
 
-static inline jsval_layout
+static inline JS_VALUE_CONSTEXPR jsval_layout
 INT32_TO_JSVAL_IMPL(int32_t i32)
 {
-    jsval_layout l;
-    l.asBits = ((uint64_t)(uint32_t)i32) | JSVAL_SHIFTED_TAG_INT32;
-    return l;
+    JS_RETURN_LAYOUT_FROM_BITS(((uint64_t)(uint32_t)i32) | JSVAL_SHIFTED_TAG_INT32);
 }
 
 static inline bool
 JSVAL_IS_NUMBER_IMPL(jsval_layout l)
 {
     return l.asBits < JSVAL_UPPER_EXCL_SHIFTED_TAG_OF_NUMBER_SET;
 }
 
@@ -807,20 +848,22 @@ JS_CANONICALIZE_NAN(double d)
         jsval_layout l;
         l.asBits = 0x7FF8000000000000LL;
         return l.asDouble;
     }
     return d;
 }
 
 static inline jsval_layout JSVAL_TO_IMPL(JS::Value v);
-static inline JS::Value IMPL_TO_JSVAL(jsval_layout l);
+static inline JS_VALUE_CONSTEXPR JS::Value IMPL_TO_JSVAL(jsval_layout l);
 
 namespace JS {
 
+static inline JS_VALUE_CONSTEXPR JS::Value UndefinedValue();
+
 /*
  * JS::Value is the interface for a single JavaScript Engine value.  A few
  * general notes on JS::Value:
  *
  * - JS::Value has setX() and isX() members for X in
  *
  *     { Int32, Double, String, Boolean, Undefined, Null, Object, Magic }
  *
@@ -853,16 +896,20 @@ namespace JS {
  */
 class Value
 {
   public:
     /*
      * N.B. the default constructor leaves Value unitialized. Adding a default
      * constructor prevents Value from being stored in a union.
      */
+#if defined(JS_VALUE_IS_CONSTEXPR)
+    Value() = default;
+    Value(const Value& v) = default;
+#endif
 
     /*** Mutators ***/
 
     void setNull() {
         data.asBits = BUILD_JSVAL(JSVAL_TAG_NULL, 0).asBits;
     }
 
     void setUndefined() {
@@ -1157,25 +1204,30 @@ class Value
   // (bug 689101); the same is true for SPARC as well (bug 737344).  More
   // precisely, we don't want Value return values compiled as out params.
   private:
 #endif
 
     jsval_layout data;
 
   private:
+#if defined(JS_VALUE_IS_CONSTEXPR)
+    JS_VALUE_CONSTEXPR Value(jsval_layout layout) : data(layout) {}
+#endif
+
     void staticAssertions() {
         JS_STATIC_ASSERT(sizeof(JSValueType) == 1);
         JS_STATIC_ASSERT(sizeof(JSValueTag) == 4);
         JS_STATIC_ASSERT(sizeof(JSWhyMagic) <= 4);
         JS_STATIC_ASSERT(sizeof(Value) == 8);
     }
 
     friend jsval_layout (::JSVAL_TO_IMPL)(Value);
-    friend Value (::IMPL_TO_JSVAL)(jsval_layout l);
+    friend Value JS_VALUE_CONSTEXPR (::IMPL_TO_JSVAL)(jsval_layout l);
+    friend Value JS_VALUE_CONSTEXPR (JS::UndefinedValue)();
 };
 
 inline bool
 IsPoisonedValue(const Value &v)
 {
     if (v.isString())
         return IsPoisonedPtr(v.toString());
     if (v.isObject())
@@ -1188,22 +1240,26 @@ IsPoisonedValue(const Value &v)
 static inline Value
 NullValue()
 {
     Value v;
     v.setNull();
     return v;
 }
 
-static inline Value
+static inline JS_VALUE_CONSTEXPR Value
 UndefinedValue()
 {
-    Value v;
+#if defined(JS_VALUE_IS_CONSTEXPR)
+    return Value(BUILD_JSVAL(JSVAL_TAG_UNDEFINED, 0));
+#else
+    JS::Value v;
     v.setUndefined();
     return v;
+#endif
 }
 
 static inline Value
 Int32Value(int32_t i32)
 {
     Value v;
     v.setInt32(i32);
     return v;
@@ -1603,22 +1659,26 @@ class RootedBase<JS::Value> : public Mut
 } // namespace js
 
 inline jsval_layout
 JSVAL_TO_IMPL(JS::Value v)
 {
     return v.data;
 }
 
-inline JS::Value
+inline JS_VALUE_CONSTEXPR JS::Value
 IMPL_TO_JSVAL(jsval_layout l)
 {
+#if defined(JS_VALUE_IS_CONSTEXPR)
+    return JS::Value(l);
+#else
     JS::Value v;
     v.data = l;
     return v;
+#endif
 }
 
 namespace JS {
 
 #ifndef __GNUC__
 /*
  * The default assignment operator for |struct C| has the signature:
  *
@@ -1686,17 +1746,17 @@ JSVAL_IS_INT(jsval v)
 
 static inline int32_t
 JSVAL_TO_INT(jsval v)
 {
     MOZ_ASSERT(JSVAL_IS_INT(v));
     return JSVAL_TO_INT32_IMPL(JSVAL_TO_IMPL(v));
 }
 
-static inline jsval
+static inline JS_VALUE_CONSTEXPR jsval
 INT_TO_JSVAL(int32_t i)
 {
     return IMPL_TO_JSVAL(INT32_TO_JSVAL_IMPL(i));
 }
 
 static inline bool
 JSVAL_IS_DOUBLE(jsval v)
 {
@@ -1707,39 +1767,45 @@ static inline double
 JSVAL_TO_DOUBLE(jsval v)
 {
     jsval_layout l;
     MOZ_ASSERT(JSVAL_IS_DOUBLE(v));
     l = JSVAL_TO_IMPL(v);
     return l.asDouble;
 }
 
-static inline jsval
+static inline JS_VALUE_CONSTEXPR jsval
 DOUBLE_TO_JSVAL(double d)
 {
     /*
      * This is a manually inlined version of:
      *    d = JS_CANONICALIZE_NAN(d);
      *    return IMPL_TO_JSVAL(DOUBLE_TO_JSVAL_IMPL(d));
      * because GCC from XCode 3.1.4 miscompiles the above code.
      */
+#if defined(JS_VALUE_IS_CONSTEXPR)
+    return IMPL_TO_JSVAL(MOZ_UNLIKELY(d != d)
+                         ? (jsval_layout) { .asBits = 0x7FF8000000000000LL }
+                         : (jsval_layout) { .asDouble = d });
+#else
     jsval_layout l;
     if (MOZ_UNLIKELY(d != d))
         l.asBits = 0x7FF8000000000000LL;
     else
         l.asDouble = d;
     return IMPL_TO_JSVAL(l);
+#endif
 }
 
-static inline jsval
+static inline JS_VALUE_CONSTEXPR jsval
 UINT_TO_JSVAL(uint32_t i)
 {
-    if (i <= JSVAL_INT_MAX)
-        return INT_TO_JSVAL((int32_t)i);
-    return DOUBLE_TO_JSVAL((double)i);
+    return (i <= JSVAL_INT_MAX
+            ? INT_TO_JSVAL((int32_t)i)
+            : DOUBLE_TO_JSVAL((double)i));
 }
 
 static inline bool
 JSVAL_IS_NUMBER(jsval v)
 {
     return JSVAL_IS_NUMBER_IMPL(JSVAL_TO_IMPL(v));
 }
 
@@ -1825,9 +1891,12 @@ PRIVATE_TO_JSVAL(void *ptr)
 
 static inline void *
 JSVAL_TO_PRIVATE(jsval v)
 {
     MOZ_ASSERT(JSVAL_IS_DOUBLE(v));
     return JSVAL_TO_PRIVATE_PTR_IMPL(JSVAL_TO_IMPL(v));
 }
 
+#undef JS_VALUE_IS_CONSTEXPR
+#undef JS_RETURN_LAYOUT_FROM_BITS
+
 #endif /* js_Value_h */