Bug 1143011 - Extract the has/add/take logic out of the register sets to distinguish between allocatable and live sets. r=jandem,Waldo
authorNicolas B. Pierron <nicolas.b.pierron@mozilla.com>
Sat, 28 Mar 2015 01:08:12 +0100
changeset 236352 b2904e8f07e7995697039160f4846420e1fdb770
parent 236351 bf414f68291cd6a77b35de593ab589915db56b48
child 236353 5092827680338c844b18a39b706f016278928961
push id28497
push userphilringnalda@gmail.com
push dateSun, 29 Mar 2015 03:30:34 +0000
treeherdermozilla-central@02f2f4c75007 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjandem, Waldo
bugs1143011
milestone39.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 1143011 - Extract the has/add/take logic out of the register sets to distinguish between allocatable and live sets. r=jandem,Waldo
js/src/jit/RegisterSets.h
js/src/jit/Registers.h
js/src/jit/arm/Architecture-arm.h
js/src/jit/mips/Architecture-mips.h
js/src/jit/none/Architecture-none.h
js/src/jit/x64/Architecture-x64.h
js/src/jit/x86/Architecture-x86.h
js/src/jsapi-tests/moz.build
js/src/jsapi-tests/testJitRegisterSet.cpp
mfbt/Attributes.h
--- a/js/src/jit/RegisterSets.h
+++ b/js/src/jit/RegisterSets.h
@@ -318,20 +318,23 @@ struct Int32Key {
     inline bool isConstant() const {
         return !isRegister_;
     }
 };
 
 template <typename T>
 class TypedRegisterSet
 {
+  public:
+    typedef T RegType;
     typedef typename T::SetType SetType;
+
+  private:
     SetType bits_;
 
-
   public:
     explicit MOZ_CONSTEXPR TypedRegisterSet(SetType bits)
       : bits_(bits)
     { }
 
     MOZ_CONSTEXPR TypedRegisterSet() : bits_(0)
     { }
     MOZ_CONSTEXPR TypedRegisterSet(const TypedRegisterSet<T> &set) : bits_(set.bits_)
@@ -362,184 +365,61 @@ class TypedRegisterSet
         return TypedRegisterSet(~in.bits_ & allocatableVolatile);
     }
     static inline TypedRegisterSet Volatile() {
         return TypedRegisterSet(T::Codes::AllocatableMask & T::Codes::VolatileMask);
     }
     static inline TypedRegisterSet NonVolatile() {
         return TypedRegisterSet(T::Codes::AllocatableMask & T::Codes::NonVolatileMask);
     }
-    bool has(T reg) const {
-        // When checking to see if a set has a register, we only want that exact
-        // register, not worrying about aliasing.
-        return !!(bits_ & (SetType(1) << reg.code()));
-    }
-    void addUnchecked(T reg) {
-        bits_ |= (SetType(1) << reg.code());
-    }
-    void addAllAliasedUnchecked(T reg) {
-        for (uint32_t a = 0; a < reg.numAliased(); a++) {
-            T tmp;
-            reg.aliased(a, &tmp);
-            bits_ |= (SetType(1) << tmp.code());
-        }
-    }
 
-    void add(T reg) {
-        // Make sure we don't add two overlapping registers.
-#ifdef DEBUG
-        for (uint32_t a = 0; a < reg.numAliased(); a++) {
-            T tmp;
-            reg.aliased(a, &tmp);
-            MOZ_ASSERT(!has(tmp));
-        }
-#endif
-        addUnchecked(reg);
-    }
-
-    void add(ValueOperand value) {
-#if defined(JS_NUNBOX32)
-        add(value.payloadReg());
-        add(value.typeReg());
-#elif defined(JS_PUNBOX64)
-        add(value.valueReg());
-#else
-#error "Bad architecture"
-#endif
-    }
-    // Determine if some register are still allocated.  This function should
-    // be used with the set of allocatable registers used for the initialization
-    // of the current set.
-    bool someAllocated(const TypedRegisterSet &allocatable) const {
-        return allocatable.bits_ & ~bits_;
-    }
     bool empty() const {
         return !bits_;
     }
-    void take(T reg) {
-        MOZ_ASSERT(has(reg));
-        takeUnchecked(reg);
+
+    bool hasRegisterIndex(T reg) const {
+        return !!(bits_ & (SetType(1) << reg.code()));
+    }
+    bool hasAllocatable(T reg) const {
+        return !(~bits_ & reg.alignedOrDominatedAliasedSet());
     }
-    void takeUnchecked(T reg) {
+
+    void addRegisterIndex(T reg) {
+        bits_ |= (SetType(1) << reg.code());
+    }
+    void addAllocatable(T reg) {
+        bits_ |= reg.alignedOrDominatedAliasedSet();
+    }
+
+
+    void takeRegisterIndex(T reg) {
         bits_ &= ~(SetType(1) << reg.code());
     }
-    void takeAllAliasedUnchecked(T reg) {
-        for (uint32_t a = 0; a < reg.numAliased(); a++) {
-            T tmp;
-            reg.aliased(a, &tmp);
-            takeUnchecked(tmp);
-        }
-    }
-    void take(ValueOperand value) {
-#if defined(JS_NUNBOX32)
-        take(value.payloadReg());
-        take(value.typeReg());
-#elif defined(JS_PUNBOX64)
-        take(value.valueReg());
-#else
-#error "Bad architecture"
-#endif
+    void takeAllocatable(T reg) {
+        bits_ &= ~reg.alignedOrDominatedAliasedSet();
     }
-    void takeUnchecked(ValueOperand value) {
-#if defined(JS_NUNBOX32)
-        takeUnchecked(value.payloadReg());
-        takeUnchecked(value.typeReg());
-#elif defined(JS_PUNBOX64)
-        takeUnchecked(value.valueReg());
-#else
-#error "Bad architecture"
-#endif
-    }
-    ValueOperand takeValueOperand() {
-#if defined(JS_NUNBOX32)
-        return ValueOperand(takeAny(), takeAny());
-#elif defined(JS_PUNBOX64)
-        return ValueOperand(takeAny());
-#else
-#error "Bad architecture"
-#endif
-    }
+
     T getAny() const {
         // The choice of first or last here is mostly arbitrary, as they are
         // about the same speed on popular architectures. We choose first, as
         // it has the advantage of using the "lower" registers more often. These
         // registers are sometimes more efficient (e.g. optimized encodings for
         // EAX on x86).
         return getFirst();
     }
-    T getAnyExcluding(T preclude) {
-        MOZ_ASSERT(!empty());
-        if (!has(preclude))
-            return getAny();
-
-        take(preclude);
-        MOZ_ASSERT(!empty());
-        T result = getAny();
-        add(preclude);
-        return result;
-    }
     T getFirst() const {
         MOZ_ASSERT(!empty());
         return T::FromCode(T::FirstBit(bits_));
     }
     T getLast() const {
         MOZ_ASSERT(!empty());
         int ireg = T::LastBit(bits_);
         return T::FromCode(ireg);
     }
-    T takeAny() {
-        MOZ_ASSERT(!empty());
-        T reg = getAny();
-        takeAllAliasedUnchecked(reg);
-        return reg;
-    }
-    T takeUnaliasedAny() {
-        // This variant is used by LinearScan for iterating over all registers.
-        MOZ_ASSERT(!empty());
-        T reg = getAny();
-        takeUnchecked(reg);
-        return reg;
-    }
-    T takeAnyExcluding(T preclude) {
-        T reg = getAnyExcluding(preclude);
-        takeAllAliasedUnchecked(reg);
-        return reg;
-    }
-    ValueOperand takeAnyValue() {
-#if defined(JS_NUNBOX32)
-        T type = takeAny();
-        T payload = takeAny();
-        return ValueOperand(type, payload);
-#elif defined(JS_PUNBOX64)
-        T reg = takeAny();
-        return ValueOperand(reg);
-#else
-#error "Bad architecture"
-#endif
-    }
-    T takeFirst() {
-        // This function is used to implement a forward register set iterator.
-        MOZ_ASSERT(!empty());
-        T reg = getFirst();
-        // The iterator is used by PushRegsInMask which might be called with
-        // AllAlllocatableRegister mask.  To avoid saving more than needed we
-        // should take aliased registers too.
-        takeAllAliasedUnchecked(reg);
-        return reg;
-    }
-    T takeLast() {
-        // This function is used to implement a backward register set iterator.
-        MOZ_ASSERT(!empty());
-        T reg = getLast();
-        takeAllAliasedUnchecked(reg);
-        return reg;
-    }
-    void clear() {
-        bits_ = 0;
-    }
+
     SetType bits() const {
         return bits_;
     }
     uint32_t size() const {
         return T::SetSize(bits_);
     }
     bool operator ==(const TypedRegisterSet<T> &other) const {
         return other.bits_ == bits_;
@@ -587,163 +467,632 @@ class RegisterSet {
     }
     static inline RegisterSet VolatileNot(const RegisterSet &in) {
         return RegisterSet(GeneralRegisterSet::VolatileNot(in.gpr_),
                            FloatRegisterSet::VolatileNot(in.fpu_));
     }
     static inline RegisterSet Volatile() {
         return RegisterSet(GeneralRegisterSet::Volatile(), FloatRegisterSet::Volatile());
     }
+
+    bool empty() const {
+        return fpu_.empty() && gpr_.empty();
+    }
+    bool emptyGeneral() const {
+        return gpr_.empty();
+    }
+    bool emptyFloat() const {
+        return fpu_.empty();
+    }
+    MOZ_CONSTEXPR GeneralRegisterSet gprs() const {
+        return gpr_;
+    }
+    GeneralRegisterSet &gprs() {
+        return gpr_;
+    }
+    MOZ_CONSTEXPR FloatRegisterSet fpus() const {
+        return fpu_;
+    }
+    FloatRegisterSet &fpus() {
+        return fpu_;
+    }
+    bool operator ==(const RegisterSet &other) const {
+        return other.gpr_ == gpr_ && other.fpu_ == fpu_;
+    }
+
+};
+
+// There are 2 use cases for register sets:
+//
+//   1. To serve as a pool of allocatable register. This is useful for working
+//      on the code produced by some stub where free registers are available, or
+//      when we can release some registers.
+//
+//   2. To serve as a list of typed registers. This is useful for working with
+//      live registers and to manipulate them with the proper instructions. This
+//      is used by the register allocator to fill the Safepoints.
+//
+// These 2 uses cases can be used on top of 3 different backend representation
+// of register sets, which are either GeneralRegisterSet, FloatRegisterSet, or
+// RegisterSet (for both). These classes are used to store the bit sets to
+// represent each register.
+//
+// Each use case defines an Accessor class, such as AllocatableSetAccessor or
+// LiveSetAccessor, which is parameterized with the type of the register
+// set. These accessors are in charge of manipulating the register set in a
+// consistent way.
+//
+// The RegSetCommonInterface class is used to wrap the accessors with convenient
+// shortcuts which are based on the accessors.
+//
+// Then, to avoid to many levels of complexity while using these interfaces,
+// shortcut templates are created to make it easy to distinguish between a
+// register set used for allocating registers, or a register set used for making
+// a collection of allocated (live) registers.
+//
+// This separation exists to prevent mixing LiveSet and AllocatableSet
+// manipulations of the same register set, and ensure safety while avoiding
+// false positive.
+
+template <typename RegisterSet>
+class AllocatableSet;
+
+template <typename RegisterSet>
+class LiveSet;
+
+// Base accessors classes have the minimal set of raw methods to manipulate the register set
+// given as parameter in a consistent manner.  These methods are:
+//
+//    - has: Returns if all the bits needed to take a register are present.
+//
+//    - takeUnchecked: Subtracts the bits used to represent the register in the
+//      register set.
+//
+//    - addUnchecked: Adds the bits used to represent the register in the
+//      register set.
+
+// The AllocatableSet accessors are used to make a pool of unused
+// registers. Taking or adding registers should consider the aliasing rules of
+// the architecture.  For example, on ARM, the following piece of code should
+// work fine, knowing that the double register |d0| is composed of float
+// registers |s0| and |s1|:
+//
+//     AllocatableFloatRegisterSet regs;
+//     regs.add(s0);
+//     regs.add(s1);
+//     // d0 is now available.
+//     regs.take(d0);
+//
+// These accessors are useful for allocating registers within the functions used
+// to generate stubs, trampolines, and inline caches (BaselineIC, IonCache).
+template <typename Set>
+class AllocatableSetAccessors
+{
+  public:
+    typedef Set RegSet;
+    typedef typename RegSet::RegType RegType;
+    typedef typename RegSet::SetType SetType;
+
+  protected:
+    RegSet set_;
+
+  public:
+    AllocatableSetAccessors() : set_() {}
+    explicit MOZ_CONSTEXPR AllocatableSetAccessors(SetType set) : set_(set) {}
+    explicit MOZ_CONSTEXPR AllocatableSetAccessors(RegSet set) : set_(set) {}
+
+    bool has(RegType reg) const {
+        return set_.hasAllocatable(reg);
+    }
+
+    void addUnchecked(RegType reg) {
+        set_.addAllocatable(reg);
+    }
+
+    void takeUnchecked(RegType reg) {
+        set_.takeAllocatable(reg);
+    }
+};
+
+// Specialization of the AllocatableSet accessors for the RegisterSet aggregate.
+template <>
+class AllocatableSetAccessors<RegisterSet>
+{
+  public:
+    typedef RegisterSet RegSet;
+    typedef AnyRegister RegType;
+    typedef char SetType;
+
+  protected:
+    RegisterSet set_;
+
+  public:
+    AllocatableSetAccessors() : set_() {}
+    explicit MOZ_CONSTEXPR AllocatableSetAccessors(SetType) = delete;
+    explicit MOZ_CONSTEXPR AllocatableSetAccessors(RegisterSet set) : set_(set) {}
+
     bool has(Register reg) const {
-        return gpr_.has(reg);
+        return set_.gprs().hasAllocatable(reg);
     }
     bool has(FloatRegister reg) const {
-        return fpu_.has(reg);
+        return set_.fpus().hasAllocatable(reg);
+    }
+
+    void addUnchecked(Register reg) {
+        set_.gprs().addAllocatable(reg);
+    }
+    void addUnchecked(FloatRegister reg) {
+        set_.fpus().addAllocatable(reg);
+    }
+
+    void takeUnchecked(Register reg) {
+        set_.gprs().takeAllocatable(reg);
+    }
+    void takeUnchecked(FloatRegister reg) {
+        set_.fpus().takeAllocatable(reg);
+    }
+};
+
+
+// The LiveSet accessors are used to collect a list of allocated
+// registers. Taking or adding a register should *not* consider the aliases, as
+// we care about interpreting the registers with the correct type.  For example,
+// on x64, where one float registers can be interpreted as an Int32x4, a Double,
+// or a Float, adding xmm0 as an Int32x4, does not make the register available
+// as a Double.
+//
+//     LiveFloatRegisterSet regs;
+//     regs.add(xmm0.asInt32x4());
+//     regs.take(xmm0); // Assert!
+//
+// These accessors are useful for recording the result of a register allocator,
+// such as what the Backtracking allocator do on the Safepoints.
+template <typename Set>
+class LiveSetAccessors
+{
+  public:
+    typedef Set RegSet;
+    typedef typename RegSet::RegType RegType;
+    typedef typename RegSet::SetType SetType;
+
+  protected:
+    RegSet set_;
+
+  public:
+    LiveSetAccessors() : set_() {}
+    explicit MOZ_CONSTEXPR LiveSetAccessors(SetType set) : set_(set) {}
+    explicit MOZ_CONSTEXPR LiveSetAccessors(RegSet set) : set_(set) {}
+
+    bool has(RegType reg) const {
+        return set_.hasRegisterIndex(reg);
+    }
+
+    void addUnchecked(RegType reg) {
+        set_.addRegisterIndex(reg);
+    }
+
+    void takeUnchecked(RegType reg) {
+        set_.takeRegisterIndex(reg);
+    }
+};
+
+// Specialization of the LiveSet accessors for the RegisterSet aggregate.
+template <>
+class LiveSetAccessors<RegisterSet>
+{
+  public:
+    typedef RegisterSet RegSet;
+    typedef AnyRegister RegType;
+    typedef char SetType;
+
+  protected:
+    RegisterSet set_;
+
+  public:
+    LiveSetAccessors() : set_() {}
+    explicit MOZ_CONSTEXPR LiveSetAccessors(SetType) = delete;
+    explicit MOZ_CONSTEXPR LiveSetAccessors(RegisterSet set) : set_(set) {}
+
+    bool has(Register reg) const {
+        return set_.gprs().hasRegisterIndex(reg);
+    }
+    bool has(FloatRegister reg) const {
+        return set_.fpus().hasRegisterIndex(reg);
+    }
+
+    void addUnchecked(Register reg) {
+        set_.gprs().addRegisterIndex(reg);
+    }
+    void addUnchecked(FloatRegister reg) {
+        set_.fpus().addRegisterIndex(reg);
+    }
+
+    void takeUnchecked(Register reg) {
+        set_.gprs().takeRegisterIndex(reg);
+    }
+    void takeUnchecked(FloatRegister reg) {
+        set_.fpus().takeRegisterIndex(reg);
     }
+};
+
+#define DEFINE_ACCESSOR_CONSTRUCTORS_(REGSET)                         \
+    typedef typename Parent::RegSet  RegSet;                          \
+    typedef typename Parent::RegType RegType;                         \
+    typedef typename Parent::SetType SetType;                         \
+                                                                      \
+    MOZ_CONSTEXPR_TMPL REGSET() : Parent() {}                         \
+    explicit MOZ_CONSTEXPR_TMPL REGSET(SetType set) : Parent(set) {}  \
+    explicit MOZ_CONSTEXPR_TMPL REGSET(RegSet set) : Parent(set) {}
+
+// This class adds checked accessors on top of the unchecked variants defined by
+// AllocatableSet and LiveSet accessors. Also it defines interface which are
+// specialized to the register set implementation, such as |getAny| and
+// |takeAny| variants.
+template <class Accessors, typename Set>
+class SpecializedRegSet : public Accessors
+{
+    typedef Accessors Parent;
+
+  public:
+    DEFINE_ACCESSOR_CONSTRUCTORS_(SpecializedRegSet)
+
+    SetType bits() const {
+        return this->Parent::set_.bits();
+    }
+
+    using Parent::has;
+
+    using Parent::addUnchecked;
+    void add(RegType reg) {
+        MOZ_ASSERT(!has(reg));
+        addUnchecked(reg);
+    }
+
+    using Parent::takeUnchecked;
+    void take(RegType reg) {
+        MOZ_ASSERT(has(reg));
+        takeUnchecked(reg);
+    }
+
+    RegType getAny() const {
+        return this->Parent::set_.getAny();
+    }
+    RegType getFirst() const {
+        return this->Parent::set_.getFirst();
+    }
+    RegType getLast() const {
+        return this->Parent::set_.getLast();
+    }
+
+    RegType getAnyExcluding(RegType preclude) {
+        if (!has(preclude))
+            return getAny();
+
+        take(preclude);
+        RegType result = getAny();
+        add(preclude);
+        return result;
+    }
+
+    RegType takeAny() {
+        RegType reg = getAny();
+        take(reg);
+        return reg;
+    }
+    RegType takeFirst() {
+        RegType reg = getFirst();
+        take(reg);
+        return reg;
+    }
+    RegType takeLast() {
+        RegType reg = getLast();
+        take(reg);
+        return reg;
+    }
+
+    ValueOperand takeAnyValue() {
+#if defined(JS_NUNBOX32)
+        return ValueOperand(takeAny(), takeAny());
+#elif defined(JS_PUNBOX64)
+        return ValueOperand(takeAny());
+#else
+#error "Bad architecture"
+#endif
+    }
+
+    RegType takeAnyExcluding(RegType preclude) {
+        RegType reg = getAnyExcluding(preclude);
+        take(reg);
+        return reg;
+    }
+};
+
+// Specialization of the accessors for the RegisterSet aggregate.
+template <class Accessors>
+class SpecializedRegSet<Accessors, RegisterSet> : public Accessors
+{
+    typedef Accessors Parent;
+
+  public:
+    DEFINE_ACCESSOR_CONSTRUCTORS_(SpecializedRegSet)
+
+    GeneralRegisterSet gprs() const {
+        return this->Parent::set_.gprs();
+    }
+    GeneralRegisterSet &gprs() {
+        return this->Parent::set_.gprs();
+    }
+    FloatRegisterSet fpus() const {
+        return this->Parent::set_.fpus();
+    }
+    FloatRegisterSet &fpus() {
+        return this->Parent::set_.fpus();
+    }
+
+    bool emptyGeneral() const {
+        return this->Parent::set_.emptyGeneral();
+    }
+    bool emptyFloat() const {
+        return this->Parent::set_.emptyFloat();
+    }
+
+
+    using Parent::has;
     bool has(AnyRegister reg) const {
         return reg.isFloat() ? has(reg.fpu()) : has(reg.gpr());
     }
+
+
+    using Parent::addUnchecked;
+    void addUnchecked(AnyRegister reg) {
+        if (reg.isFloat())
+            addUnchecked(reg.fpu());
+        else
+            addUnchecked(reg.gpr());
+    }
+
     void add(Register reg) {
-        gpr_.add(reg);
+        MOZ_ASSERT(!has(reg));
+        addUnchecked(reg);
     }
     void add(FloatRegister reg) {
-        fpu_.add(reg);
+        MOZ_ASSERT(!has(reg));
+        addUnchecked(reg);
+    }
+    void add(AnyRegister reg) {
+        if (reg.isFloat())
+            add(reg.fpu());
+        else
+            add(reg.gpr());
+    }
+
+    using Parent::takeUnchecked;
+    void takeUnchecked(AnyRegister reg) {
+        if (reg.isFloat())
+            takeUnchecked(reg.fpu());
+        else
+            takeUnchecked(reg.gpr());
+    }
+
+    void take(Register reg) {
+        MOZ_ASSERT(has(reg));
+        takeUnchecked(reg);
+    }
+    void take(FloatRegister reg) {
+        MOZ_ASSERT(has(reg));
+        takeUnchecked(reg);
+    }
+    void take(AnyRegister reg) {
+        if (reg.isFloat())
+            take(reg.fpu());
+        else
+            take(reg.gpr());
+    }
+
+    Register getAnyGeneral() const {
+        return this->Parent::set_.gprs().getAny();
+    }
+    FloatRegister getAnyFloat() const {
+        return this->Parent::set_.fpus().getAny();
     }
-    void add(AnyRegister any) {
-        if (any.isFloat())
-            add(any.fpu());
-        else
-            add(any.gpr());
+
+    Register takeAnyGeneral() {
+        Register reg = getAnyGeneral();
+        take(reg);
+        return reg;
+    }
+    FloatRegister takeAnyFloat() {
+        FloatRegister reg = getAnyFloat();
+        take(reg);
+        return reg;
+    }
+    ValueOperand takeAnyValue() {
+#if defined(JS_NUNBOX32)
+        return ValueOperand(takeAnyGeneral(), takeAnyGeneral());
+#elif defined(JS_PUNBOX64)
+        return ValueOperand(takeAnyGeneral());
+#else
+#error "Bad architecture"
+#endif
     }
+};
+
+
+// Interface which is common to all register set implementations. It overloads
+// |add|, |take| and |takeUnchecked| methods for types such as |ValueOperand|
+// and |TypedOrValueRegister|.
+template <class Accessors, typename Set>
+class CommonRegSet : public SpecializedRegSet<Accessors, Set>
+{
+    typedef SpecializedRegSet<Accessors, Set> Parent;
+
+  public:
+    DEFINE_ACCESSOR_CONSTRUCTORS_(CommonRegSet)
+
+    RegSet set() const {
+        return this->Parent::set_;
+    }
+    RegSet &set() {
+        return this->Parent::set_;
+    }
+
+    bool empty() const {
+        return this->Parent::set_.empty();
+    }
+
+    using Parent::add;
     void add(ValueOperand value) {
 #if defined(JS_NUNBOX32)
         add(value.payloadReg());
         add(value.typeReg());
 #elif defined(JS_PUNBOX64)
         add(value.valueReg());
 #else
 #error "Bad architecture"
 #endif
     }
     void add(TypedOrValueRegister reg) {
         if (reg.hasValue())
             add(reg.valueReg());
         else if (reg.hasTyped())
             add(reg.typedReg());
     }
-    void addUnchecked(Register reg) {
-        gpr_.addUnchecked(reg);
-    }
-    void addUnchecked(FloatRegister reg) {
-        fpu_.addUnchecked(reg);
-    }
-    void addUnchecked(AnyRegister any) {
-        if (any.isFloat())
-            addUnchecked(any.fpu());
-        else
-            addUnchecked(any.gpr());
-    }
-    void addAllAliasedUnchecked(const AnyRegister &reg) {
-        if (reg.isFloat())
-            fpu_.addAllAliasedUnchecked(reg.fpu());
-        else
-            gpr_.addAllAliasedUnchecked(reg.gpr());
-    }
 
-
-    bool empty(bool floats) const {
-        return floats ? fpu_.empty() : gpr_.empty();
-    }
-    FloatRegister takeFloat() {
-        return fpu_.takeAny();
-    }
-    FloatRegister takeUnaliasedFloat() {
-        return fpu_.takeUnaliasedAny();
-    }
-    Register takeGeneral() {
-        return gpr_.takeAny();
-    }
-    Register takeUnaliasedGeneral() {
-        return gpr_.takeUnaliasedAny();
-    }
-    ValueOperand takeValueOperand() {
+    using Parent::take;
+    void take(ValueOperand value) {
 #if defined(JS_NUNBOX32)
-        return ValueOperand(takeGeneral(), takeGeneral());
+        take(value.payloadReg());
+        take(value.typeReg());
 #elif defined(JS_PUNBOX64)
-        return ValueOperand(takeGeneral());
+        take(value.valueReg());
 #else
 #error "Bad architecture"
 #endif
     }
-    void take(AnyRegister reg) {
-        if (reg.isFloat())
-            fpu_.take(reg.fpu());
-        else
-            gpr_.take(reg.gpr());
-    }
-    void takeAllAliasedUnchecked(AnyRegister reg) {
-        if (reg.isFloat())
-            fpu_.takeAllAliasedUnchecked(reg.fpu());
-        else
-            gpr_.takeAllAliasedUnchecked(reg.gpr());
-    }
-    // This function is used by LinearScan to find a free register.
-    AnyRegister takeUnaliasedAny(bool isFloat) {
-        if (isFloat)
-            return AnyRegister(takeUnaliasedFloat());
-        return AnyRegister(takeUnaliasedGeneral());
-    }
-    void clear() {
-        gpr_.clear();
-        fpu_.clear();
-    }
-    MOZ_CONSTEXPR GeneralRegisterSet gprs() const {
-        return gpr_;
-    }
-    MOZ_CONSTEXPR FloatRegisterSet fpus() const {
-        return fpu_;
-    }
-    bool operator ==(const RegisterSet &other) const {
-        return other.gpr_ == gpr_ && other.fpu_ == fpu_;
+    void take(TypedOrValueRegister reg) {
+        if (reg.hasValue())
+            take(reg.valueReg());
+        else if (reg.hasTyped())
+            take(reg.typedReg());
     }
 
-    void takeUnchecked(Register reg) {
-        gpr_.takeUnchecked(reg);
-    }
-    void takeUnchecked(FloatRegister reg) {
-        fpu_.takeUnchecked(reg);
-    }
-    void takeUnchecked(AnyRegister reg) {
-        if (reg.isFloat())
-            fpu_.takeUnchecked(reg.fpu());
-        else
-            gpr_.takeUnchecked(reg.gpr());
-    }
+    using Parent::takeUnchecked;
     void takeUnchecked(ValueOperand value) {
-        gpr_.takeUnchecked(value);
+#if defined(JS_NUNBOX32)
+        takeUnchecked(value.payloadReg());
+        takeUnchecked(value.typeReg());
+#elif defined(JS_PUNBOX64)
+        takeUnchecked(value.valueReg());
+#else
+#error "Bad architecture"
+#endif
     }
     void takeUnchecked(TypedOrValueRegister reg) {
         if (reg.hasValue())
             takeUnchecked(reg.valueReg());
         else if (reg.hasTyped())
             takeUnchecked(reg.typedReg());
     }
 };
 
+
+// These classes do not provide any additional members, they only use their
+// constructors to forward to the common interface for all register sets.  The
+// only benefit of these classes is to provide user friendly names.
+template <typename Set>
+class LiveSet : public CommonRegSet<LiveSetAccessors<Set>, Set>
+{
+    typedef CommonRegSet<LiveSetAccessors<Set>, Set> Parent;
+
+  public:
+    DEFINE_ACCESSOR_CONSTRUCTORS_(LiveSet)
+};
+
+template <typename Set>
+class AllocatableSet : public CommonRegSet<AllocatableSetAccessors<Set>, Set>
+{
+    typedef CommonRegSet<AllocatableSetAccessors<Set>, Set> Parent;
+
+  public:
+    DEFINE_ACCESSOR_CONSTRUCTORS_(AllocatableSet)
+
+    LiveSet<Set> asLiveSet() const {
+        return LiveSet<Set>(this->set());
+    }
+};
+
+#define DEFINE_ACCESSOR_CONSTRUCTORS_FOR_REGISTERSET_(REGSET)               \
+    typedef Parent::RegSet  RegSet;                                         \
+    typedef Parent::RegType RegType;                                        \
+    typedef Parent::SetType SetType;                                        \
+                                                                            \
+    MOZ_CONSTEXPR_TMPL REGSET() : Parent() {}                               \
+    explicit MOZ_CONSTEXPR_TMPL REGSET(SetType) = delete;                   \
+    explicit MOZ_CONSTEXPR_TMPL REGSET(RegSet set) : Parent(set) {}         \
+    MOZ_CONSTEXPR_TMPL REGSET(GeneralRegisterSet gpr, FloatRegisterSet fpu) \
+      : Parent(RegisterSet(gpr, fpu))                                       \
+    {}                                                                      \
+    REGSET(REGSET<GeneralRegisterSet> gpr, REGSET<FloatRegisterSet> fpu)    \
+      : Parent(RegisterSet(gpr.set(), fpu.set()))                           \
+    {}
+
+template <>
+class LiveSet<RegisterSet>
+  : public CommonRegSet<LiveSetAccessors<RegisterSet>, RegisterSet>
+{
+    // Note: We have to provide a qualified name for LiveSetAccessors, as it is
+    // interpreted as being the specialized class name inherited from the parent
+    // class specialization.
+    typedef CommonRegSet<jit::LiveSetAccessors<RegisterSet>, RegisterSet> Parent;
+
+  public:
+    DEFINE_ACCESSOR_CONSTRUCTORS_FOR_REGISTERSET_(LiveSet)
+};
+
+template <>
+class AllocatableSet<RegisterSet>
+  : public CommonRegSet<AllocatableSetAccessors<RegisterSet>, RegisterSet>
+{
+    // Note: We have to provide a qualified name for AllocatableSetAccessors, as
+    // it is interpreted as being the specialized class name inherited from the
+    // parent class specialization.
+    typedef CommonRegSet<jit::AllocatableSetAccessors<RegisterSet>, RegisterSet> Parent;
+
+  public:
+    DEFINE_ACCESSOR_CONSTRUCTORS_FOR_REGISTERSET_(AllocatableSet)
+
+    LiveSet<RegisterSet> asLiveSet() const {
+        return LiveSet<RegisterSet>(this->set());
+    }
+};
+
+#undef DEFINE_ACCESSOR_CONSTRUCTORS_FOR_REGISTERSET_
+#undef DEFINE_ACCESSOR_CONSTRUCTORS_
+
+typedef AllocatableSet<GeneralRegisterSet> AllocatableGeneralRegisterSet;
+typedef AllocatableSet<FloatRegisterSet> AllocatableFloatRegisterSet;
+typedef AllocatableSet<RegisterSet> AllocatableRegisterSet;
+
+typedef LiveSet<GeneralRegisterSet> LiveGeneralRegisterSet;
+typedef LiveSet<FloatRegisterSet> LiveFloatRegisterSet;
+typedef LiveSet<RegisterSet> LiveRegisterSet;
+
 // iterates in whatever order happens to be convenient.
 // Use TypedRegisterBackwardIterator or TypedRegisterForwardIterator if a
 // specific order is required.
 template <typename T>
 class TypedRegisterIterator
 {
-    TypedRegisterSet<T> regset_;
+    LiveSet<TypedRegisterSet<T>> regset_;
 
   public:
     explicit TypedRegisterIterator(TypedRegisterSet<T> regset) : regset_(regset)
     { }
+    explicit TypedRegisterIterator(LiveSet<TypedRegisterSet<T>> regset) : regset_(regset)
+    { }
     TypedRegisterIterator(const TypedRegisterIterator &other) : regset_(other.regset_)
     { }
 
     bool more() const {
         return !regset_.empty();
     }
     TypedRegisterIterator<T> operator ++(int) {
         TypedRegisterIterator<T> old(*this);
@@ -758,21 +1107,23 @@ class TypedRegisterIterator
         return regset_.getAny();
     }
 };
 
 // iterates backwards, that is, rn to r0
 template <typename T>
 class TypedRegisterBackwardIterator
 {
-    TypedRegisterSet<T> regset_;
+    LiveSet<TypedRegisterSet<T>> regset_;
 
   public:
     explicit TypedRegisterBackwardIterator(TypedRegisterSet<T> regset) : regset_(regset)
     { }
+    explicit TypedRegisterBackwardIterator(LiveSet<TypedRegisterSet<T>> regset) : regset_(regset)
+    { }
     TypedRegisterBackwardIterator(const TypedRegisterBackwardIterator &other)
       : regset_(other.regset_)
     { }
 
     bool more() const {
         return !regset_.empty();
     }
     TypedRegisterBackwardIterator<T> operator ++(int) {
@@ -788,21 +1139,23 @@ class TypedRegisterBackwardIterator
         return regset_.getLast();
     }
 };
 
 // iterates forwards, that is r0 to rn
 template <typename T>
 class TypedRegisterForwardIterator
 {
-    TypedRegisterSet<T> regset_;
+    LiveSet<TypedRegisterSet<T>> regset_;
 
   public:
     explicit TypedRegisterForwardIterator(TypedRegisterSet<T> regset) : regset_(regset)
     { }
+    explicit TypedRegisterForwardIterator(LiveSet<TypedRegisterSet<T>> regset) : regset_(regset)
+    { }
     TypedRegisterForwardIterator(const TypedRegisterForwardIterator &other) : regset_(other.regset_)
     { }
 
     bool more() const {
         return !regset_.empty();
     }
     TypedRegisterForwardIterator<T> operator ++(int) {
         TypedRegisterForwardIterator<T> old(*this);
@@ -835,16 +1188,19 @@ class AnyRegisterIterator
       : geniter_(GeneralRegisterSet::All()), floatiter_(FloatRegisterSet::All())
     { }
     AnyRegisterIterator(GeneralRegisterSet genset, FloatRegisterSet floatset)
       : geniter_(genset), floatiter_(floatset)
     { }
     explicit AnyRegisterIterator(const RegisterSet &set)
       : geniter_(set.gpr_), floatiter_(set.fpu_)
     { }
+    explicit AnyRegisterIterator(const LiveSet<RegisterSet> &set)
+      : geniter_(set.gprs()), floatiter_(set.fpus())
+    { }
     AnyRegisterIterator(const AnyRegisterIterator &other)
       : geniter_(other.geniter_), floatiter_(other.floatiter_)
     { }
     bool more() const {
         return geniter_.more() || floatiter_.more();
     }
     AnyRegisterIterator operator ++(int) {
         AnyRegisterIterator old(*this);
--- a/js/src/jit/Registers.h
+++ b/js/src/jit/Registers.h
@@ -68,16 +68,21 @@ struct Register {
 
     // N.B. FloatRegister is an explicit outparam here because msvc-2010
     // miscompiled it on win64 when the value was simply returned.  This
     // now has an explicit outparam for compatability.
     void aliased(uint32_t aliasIdx, Register *ret) const {
         MOZ_ASSERT(aliasIdx == 0);
         *ret = *this;
     }
+
+    SetType alignedOrDominatedAliasedSet() const {
+        return SetType(1) << code_;
+    }
+
     static uint32_t SetSize(SetType x) {
         return Codes::SetSize(x);
     }
     static uint32_t FirstBit(SetType x) {
         return Codes::FirstBit(x);
     }
     static uint32_t LastBit(SetType x) {
         return Codes::LastBit(x);
--- a/js/src/jit/arm/Architecture-arm.h
+++ b/js/src/jit/arm/Architecture-arm.h
@@ -511,17 +511,47 @@ class VFPRegister
             MOZ_ASSERT(code_ < NumAliasedDoubles);
             *ret = singleOverlay(aliasIdx - 1);
             return;
         }
         MOZ_ASSERT((code_ & 1) == 0);
         *ret = doubleOverlay(aliasIdx - 1);
         return;
     }
+
     typedef FloatRegisters::SetType SetType;
+
+    // This function is used to ensure that Register set can take all Single
+    // registers, even if we are taking a mix of either double or single
+    // registers.
+    //
+    //   s0.alignedOrDominatedAliasedSet() == s0 | d0.
+    //   s1.alignedOrDominatedAliasedSet() == s1.
+    //   d0.alignedOrDominatedAliasedSet() == s0 | s1 | d0.
+    //
+    // This way the Allocator register set does not have to do any arithmetics
+    // to know if a register is available or not, as we have the following
+    // relations:
+    //
+    //   d0.alignedOrDominatedAliasedSet() ==
+    //       s0.alignedOrDominatedAliasedSet() | s1.alignedOrDominatedAliasedSet()
+    //
+    //   s0.alignedOrDominatedAliasedSet() & s1.alignedOrDominatedAliasedSet() == 0
+    //
+    SetType alignedOrDominatedAliasedSet() const {
+        if (isSingle()) {
+            if (code_ % 2 != 0)
+                return SetType(1) << code_;
+            return (SetType(1) << code_) | (SetType(1) << (32 + code_ / 2));
+        }
+
+        MOZ_ASSERT(isDouble());
+        return (SetType(0b11) << (code_ * 2)) | (SetType(1) << (32 + code_));
+    }
+
     static uint32_t SetSize(SetType x) {
         static_assert(sizeof(SetType) == 8, "SetType must be 64 bits");
         return mozilla::CountPopulation32(x);
     }
     static Code FromName(const char *name) {
         return FloatRegisters::FromName(name);
     }
     static TypedRegisterSet<VFPRegister> ReduceSetForPush(const TypedRegisterSet<VFPRegister> &s);
--- a/js/src/jit/mips/Architecture-mips.h
+++ b/js/src/jit/mips/Architecture-mips.h
@@ -485,16 +485,25 @@ class FloatRegister
         if (aliasIdx == 0) {
             *ret = *this;
             return;
         }
         MOZ_ASSERT(aliasIdx == 1);
         *ret = singleOverlay(aliasIdx - 1);
     }
     typedef FloatRegisters::SetType SetType;
+
+    SetType alignedOrDominatedAliasedSet() const {
+        if (isSingle())
+            return SetType(1) << code_;
+
+        MOZ_ASSERT(isDouble());
+        return SetType(0b11) << code_;
+    }
+
     static uint32_t SetSize(SetType x) {
         static_assert(sizeof(SetType) == 8, "SetType must be 64 bits");
         return mozilla::CountPopulation32(x);
     }
     static Code FromName(const char *name) {
         return FloatRegisters::FromName(name);
     }
     static TypedRegisterSet<FloatRegister> ReduceSetForPush(const TypedRegisterSet<FloatRegister> &s);
--- a/js/src/jit/none/Architecture-none.h
+++ b/js/src/jit/none/Architecture-none.h
@@ -118,16 +118,17 @@ struct FloatRegister
     bool operator == (FloatRegister) const { MOZ_CRASH(); }
     bool aliases(FloatRegister) const { MOZ_CRASH(); }
     uint32_t numAliased() const { MOZ_CRASH(); }
     void aliased(uint32_t, FloatRegister *) { MOZ_CRASH(); }
     bool equiv(FloatRegister) const { MOZ_CRASH(); }
     uint32_t size() const { MOZ_CRASH(); }
     uint32_t numAlignedAliased() const { MOZ_CRASH(); }
     void alignedAliased(uint32_t, FloatRegister *) { MOZ_CRASH(); }
+    SetType alignedOrDominatedAliasedSet() const { MOZ_CRASH(); }
     template <typename T> static T ReduceSetForPush(T) { MOZ_CRASH(); }
     uint32_t getRegisterDumpOffsetInBytes() { MOZ_CRASH(); }
     static uint32_t SetSize(SetType x) { MOZ_CRASH(); }
     static Code FromName(const char *name) { MOZ_CRASH(); }
 
     // This is used in static initializers, so produce a bogus value instead of crashing.
     static uint32_t GetPushSizeInBytes(const TypedRegisterSet<FloatRegister> &) { return 0; }
 };
--- a/js/src/jit/x64/Architecture-x64.h
+++ b/js/src/jit/x64/Architecture-x64.h
@@ -330,16 +330,20 @@ struct FloatRegister {
     void aliased(uint32_t aliasIdx, FloatRegister *ret) const {
         MOZ_ASSERT(aliasIdx < Codes::NumTypes);
         *ret = FloatRegister(reg_, Codes::ContentType((aliasIdx + type_) % Codes::NumTypes));
     }
     void alignedAliased(uint32_t aliasIdx, FloatRegister *ret) const {
         aliased(aliasIdx, ret);
     }
 
+    SetType alignedOrDominatedAliasedSet() const {
+        return Codes::Spread << reg_;
+    }
+
     static TypedRegisterSet<FloatRegister> ReduceSetForPush(const TypedRegisterSet<FloatRegister> &s);
     static uint32_t GetPushSizeInBytes(const TypedRegisterSet<FloatRegister> &s);
     uint32_t getRegisterDumpOffsetInBytes();
 };
 
 // Arm/D32 has double registers that can NOT be treated as float32
 // and this requires some dances in lowering.
 inline bool
--- a/js/src/jit/x86/Architecture-x86.h
+++ b/js/src/jit/x86/Architecture-x86.h
@@ -303,16 +303,20 @@ struct FloatRegister {
     void aliased(uint32_t aliasIdx, FloatRegister *ret) const {
         MOZ_ASSERT(aliasIdx < Codes::NumTypes);
         *ret = FloatRegister(reg_, Codes::ContentType((aliasIdx + type_) % Codes::NumTypes));
     }
     void alignedAliased(uint32_t aliasIdx, FloatRegister *ret) const {
         aliased(aliasIdx, ret);
     }
 
+    SetType alignedOrDominatedAliasedSet() const {
+        return Codes::Spread << reg_;
+    }
+
     static TypedRegisterSet<FloatRegister> ReduceSetForPush(const TypedRegisterSet<FloatRegister> &s);
     static uint32_t GetPushSizeInBytes(const TypedRegisterSet<FloatRegister> &s);
     uint32_t getRegisterDumpOffsetInBytes();
 };
 
 // Arm/D32 has double registers that can NOT be treated as float32
 // and this requires some dances in lowering.
 inline bool
--- a/js/src/jsapi-tests/moz.build
+++ b/js/src/jsapi-tests/moz.build
@@ -90,16 +90,17 @@ UNIFIED_SOURCES += [
 if CONFIG['ENABLE_ION']:
     UNIFIED_SOURCES += [
         'testJitDCEinGVN.cpp',
         'testJitFoldsTo.cpp',
         'testJitGVN.cpp',
         'testJitMoveEmitterCycles-mips.cpp',
         'testJitMoveEmitterCycles.cpp',
         'testJitRangeAnalysis.cpp',
+        'testJitRegisterSet.cpp',
         'testJitRValueAlloc.cpp',
     ]
 
 DEFINES['EXPORT_JS_API'] = True
 # Building against js_static requires that we declare mfbt sybols "exported"
 # on its behalf.
 DEFINES['IMPL_MFBT'] = True
 
new file mode 100644
--- /dev/null
+++ b/js/src/jsapi-tests/testJitRegisterSet.cpp
@@ -0,0 +1,138 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "jit/RegisterSets.h"
+
+#include "jsapi-tests/tests.h"
+
+using namespace js;
+using namespace js::jit;
+
+static bool
+CoPrime(size_t a, size_t b)
+{
+    if (b <= 1)
+        return a == 1 || b == 1;
+    return CoPrime(b, a % b);
+}
+
+// This macros are use to iterave over all registers in a large number of
+// non-looping sequences, which does not rely on the getFirst / getLast
+// functions.
+#define BEGIN_INDEX_WALK(RegTotal)                          \
+    static const size_t Total = RegTotal;                   \
+    for (size_t walk = 1; walk < RegTotal; walk += 2) {     \
+        if (!CoPrime(RegTotal, walk))                       \
+            continue;                                       \
+        for (size_t start = 0; start < RegTotal; start++) { \
+            size_t index = start;
+
+#define END_INDEX_WALK                                      \
+        }                                                   \
+    }
+
+#define FOR_ALL_REGISTERS(Register, reg)            \
+    do {                                            \
+        Register reg = Register::FromCode(index);
+
+#define END_FOR_ALL_REGISTERS                    \
+        index = (index + walk) % Total;          \
+    } while(index != start)
+
+BEGIN_TEST(testJitRegisterSet_GPR)
+{
+    BEGIN_INDEX_WALK(Registers::Total)
+
+    LiveSet<GeneralRegisterSet> liveRegs;
+    Allocator<GeneralRegisterSet> pool(GeneralRegisterSet::All());
+    CHECK(liveRegs.empty());
+    CHECK(pool.set() == GeneralRegisterSet::All());
+
+    FOR_ALL_REGISTERS(Register, reg) {
+
+        CHECK(!pool.has(reg) || !liveRegs.has(reg));
+        if (pool.has(reg)) {
+            CHECK(!liveRegs.has(reg));
+            pool.take(reg);
+            liveRegs.add(reg);
+            CHECK(liveRegs.has(reg));
+            CHECK(!pool.has(reg));
+        }
+        CHECK(!pool.has(reg) || !liveRegs.has(reg));
+
+    } END_FOR_ALL_REGISTERS;
+
+    CHECK(pool.empty());
+
+    FOR_ALL_REGISTERS(Register, reg) {
+
+        CHECK(!pool.has(reg) || !liveRegs.has(reg));
+        if (liveRegs.has(reg)) {
+            CHECK(!pool.has(reg));
+            liveRegs.take(reg);
+            pool.add(reg);
+            CHECK(pool.has(reg));
+            CHECK(!liveRegs.has(reg));
+        }
+        CHECK(!pool.has(reg) || !liveRegs.has(reg));
+
+    } END_FOR_ALL_REGISTERS;
+
+    CHECK(liveRegs.empty());
+    CHECK(pool.set() == GeneralRegisterSet::All());
+
+    END_INDEX_WALK
+    return true;
+}
+END_TEST(testJitRegisterSet_GPR)
+
+BEGIN_TEST(testJitRegisterSet_FPU)
+{
+    BEGIN_INDEX_WALK(FloatRegisters::Total)
+
+    LiveSet<FloatRegisterSet> liveRegs;
+    Allocator<FloatRegisterSet> pool(FloatRegisterSet::All());
+    CHECK(liveRegs.empty());
+    CHECK(pool.set() == FloatRegisterSet::All());
+
+    FOR_ALL_REGISTERS(FloatRegister, reg) {
+
+        CHECK(!pool.has(reg) || !liveRegs.has(reg));
+        if (pool.has(reg)) {
+            CHECK(!liveRegs.has(reg));
+            pool.take(reg);
+            liveRegs.add(reg);
+            CHECK(liveRegs.has(reg));
+            CHECK(!pool.has(reg));
+        }
+        CHECK(!pool.has(reg) || !liveRegs.has(reg));
+
+    } END_FOR_ALL_REGISTERS;
+
+    CHECK(pool.empty());
+
+    FOR_ALL_REGISTERS(FloatRegister, reg) {
+
+        CHECK(!pool.has(reg) || !liveRegs.has(reg));
+        if (liveRegs.has(reg)) {
+            CHECK(!pool.has(reg));
+            liveRegs.take(reg);
+            pool.add(reg);
+            CHECK(pool.has(reg));
+            CHECK(!liveRegs.has(reg));
+        }
+        CHECK(!pool.has(reg) || !liveRegs.has(reg));
+
+    } END_FOR_ALL_REGISTERS;
+
+    CHECK(liveRegs.empty());
+    CHECK(pool.set() == FloatRegisterSet::All());
+
+    END_INDEX_WALK
+    return true;
+}
+END_TEST(testJitRegisterSet_FPU)
--- a/mfbt/Attributes.h
+++ b/mfbt/Attributes.h
@@ -79,18 +79,21 @@
 #  if __has_attribute(noinline)
 #    define MOZ_HAVE_NEVER_INLINE        __attribute__((noinline))
 #  endif
 #  if __has_attribute(noreturn)
 #    define MOZ_HAVE_NORETURN            __attribute__((noreturn))
 #  endif
 #elif defined(__GNUC__)
 #  if defined(__GXX_EXPERIMENTAL_CXX0X__) || __cplusplus >= 201103L
-#      define MOZ_HAVE_CXX11_CONSTEXPR
-#      define MOZ_HAVE_EXPLICIT_CONVERSION
+#    define MOZ_HAVE_CXX11_CONSTEXPR
+#    if MOZ_GCC_VERSION_AT_LEAST(4, 8, 0)
+#      define MOZ_HAVE_CXX11_CONSTEXPR_IN_TEMPLATES
+#    endif
+#    define MOZ_HAVE_EXPLICIT_CONVERSION
 #  endif
 #  define MOZ_HAVE_NEVER_INLINE          __attribute__((noinline))
 #  define MOZ_HAVE_NORETURN              __attribute__((noreturn))
 #endif
 
 /*
  * When built with clang analyzer (a.k.a scan-build), define MOZ_HAVE_NORETURN
  * to mark some false positives
@@ -109,19 +112,25 @@
  * value may be computed at compile time.  It should be prefered to just
  * marking variables as MOZ_CONSTEXPR because if the compiler does not support
  * constexpr it will fall back to making the variable const, and some compilers
  * do not accept variables being marked both const and constexpr.
  */
 #ifdef MOZ_HAVE_CXX11_CONSTEXPR
 #  define MOZ_CONSTEXPR         constexpr
 #  define MOZ_CONSTEXPR_VAR     constexpr
+#  ifdef MOZ_HAVE_CXX11_CONSTEXPR_IN_TEMPLATES
+#    define MOZ_CONSTEXPR_TMPL  constexpr
+#  else
+#    define MOZ_CONSTEXPR_TMPL
+#  endif
 #else
 #  define MOZ_CONSTEXPR         /* no support */
 #  define MOZ_CONSTEXPR_VAR     const
+#  define MOZ_CONSTEXPR_TMPL
 #endif
 
 /*
  * MOZ_EXPLICIT_CONVERSION is a specifier on a type conversion
  * overloaded operator that declares that a C++11 compiler should restrict
  * this operator to allow only explicit type conversions, disallowing
  * implicit conversions.
  *