Bug 1246061. r=jandem, r=bz, r=luke, r=froydnj, a=lizzard
authorJeff Walden <jwalden@mit.edu>
Wed, 09 Mar 2016 00:37:20 -0800
changeset 325554 6ff2fe5a07d99d40cbf32485125efd484119514b
parent 325553 12c404b3d3e14842ac4ff0a604a9a37df6f013c7
child 325555 5629a22932f74f3e2d647c3462c32191a791afed
push id1128
push userjlund@mozilla.com
push dateWed, 01 Jun 2016 01:31:59 +0000
treeherdermozilla-release@fe0d30de989d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjandem, bz, luke, froydnj, lizzard
bugs1246061
milestone47.0a2
Bug 1246061. r=jandem, r=bz, r=luke, r=froydnj, a=lizzard
js/public/GCHashTable.h
js/public/HashTable.h
js/src/jit/IonCaches.cpp
js/src/jit/MacroAssembler.h
js/src/jit/SharedIC.cpp
js/src/jit/SharedIC.h
js/src/jit/arm/Assembler-arm.h
js/src/jit/arm/MacroAssembler-arm-inl.h
js/src/jit/arm64/Assembler-arm64.h
js/src/jit/arm64/MacroAssembler-arm64-inl.h
js/src/jit/mips-shared/Assembler-mips-shared.h
js/src/jit/mips32/MacroAssembler-mips32-inl.h
js/src/jit/mips64/MacroAssembler-mips64-inl.h
js/src/jit/shared/Assembler-shared.h
js/src/jit/x64/MacroAssembler-x64-inl.h
js/src/jit/x86-shared/Assembler-x86-shared.h
js/src/jit/x86/MacroAssembler-x86-inl.h
js/src/jsapi.h
js/src/jscntxt.h
js/src/jsfriendapi.h
js/src/jswatchpoint.cpp
js/src/vm/UbiNodeCensus.cpp
mfbt/Opaque.h
mfbt/moz.build
--- a/js/public/GCHashTable.h
+++ b/js/public/GCHashTable.h
@@ -138,17 +138,16 @@ class GCHashMapOperations
   public:
     bool initialized() const                   { return map().initialized(); }
     Ptr lookup(const Lookup& l) const          { return map().lookup(l); }
     AddPtr lookupForAdd(const Lookup& l) const { return map().lookupForAdd(l); }
     Range all() const                          { return map().all(); }
     bool empty() const                         { return map().empty(); }
     uint32_t count() const                     { return map().count(); }
     size_t capacity() const                    { return map().capacity(); }
-    uint32_t generation() const                { return map().generation(); }
     bool has(const Lookup& l) const            { return map().lookup(l).found(); }
 };
 
 template <typename Outer, typename... Args>
 class MutableGCHashMapOperations
   : public GCHashMapOperations<Outer, Args...>
 {
     using Map = GCHashMap<Args...>;
@@ -277,17 +276,16 @@ class GCHashSetOperations
   public:
     bool initialized() const                   { return set().initialized(); }
     Ptr lookup(const Lookup& l) const          { return set().lookup(l); }
     AddPtr lookupForAdd(const Lookup& l) const { return set().lookupForAdd(l); }
     Range all() const                          { return set().all(); }
     bool empty() const                         { return set().empty(); }
     uint32_t count() const                     { return set().count(); }
     size_t capacity() const                    { return set().capacity(); }
-    uint32_t generation() const                { return set().generation(); }
     bool has(const Lookup& l) const            { return set().lookup(l).found(); }
 };
 
 template <typename Outer, typename... Args>
 class MutableGCHashSetOperations
   : public GCHashSetOperations<Outer, Args...>
 {
     using Set = GCHashSet<Args...>;
--- a/js/public/HashTable.h
+++ b/js/public/HashTable.h
@@ -9,16 +9,17 @@
 
 #include "mozilla/Alignment.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/Casting.h"
 #include "mozilla/HashFunctions.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/Move.h"
+#include "mozilla/Opaque.h"
 #include "mozilla/PodOperations.h"
 #include "mozilla/ReentrancyGuard.h"
 #include "mozilla/TemplateLib.h"
 #include "mozilla/TypeTraits.h"
 #include "mozilla/UniquePtr.h"
 
 #include "js/Utility.h"
 
@@ -29,16 +30,18 @@ template <class> struct DefaultHasher;
 template <class, class> class HashMapEntry;
 namespace detail {
     template <class T> class HashTableEntry;
     template <class T, class HashPolicy, class AllocPolicy> class HashTable;
 } // namespace detail
 
 /*****************************************************************************/
 
+using Generation = mozilla::Opaque<uint64_t>;
+
 // A JS-friendly, STL-like container providing a hash-based map from keys to
 // values. In particular, HashMap calls constructors and destructors of all
 // objects added so non-PODs may be used safely.
 //
 // Key/Value requirements:
 //  - movable, destructible, assignable
 // HashPolicy requirements:
 //  - see Hash Policy section below
@@ -203,17 +206,19 @@ class HashMap
         return impl.sizeOfExcludingThis(mallocSizeOf);
     }
     size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
         return mallocSizeOf(this) + impl.sizeOfExcludingThis(mallocSizeOf);
     }
 
     // If |generation()| is the same before and after a HashMap operation,
     // pointers into the table remain valid.
-    uint32_t generation() const                       { return impl.generation(); }
+    Generation generation() const {
+        return impl.generation();
+    }
 
     /************************************************** Shorthand operations */
 
     bool has(const Lookup& l) const {
         return impl.lookup(l).found();
     }
 
     // Overwrite existing value with v. Return false on oom.
@@ -441,17 +446,19 @@ class HashSet
         return impl.sizeOfExcludingThis(mallocSizeOf);
     }
     size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
         return mallocSizeOf(this) + impl.sizeOfExcludingThis(mallocSizeOf);
     }
 
     // If |generation()| is the same before and after a HashSet operation,
     // pointers into the table remain valid.
-    uint32_t generation() const                       { return impl.generation(); }
+    Generation generation() const {
+        return impl.generation();
+    }
 
     /************************************************** Shorthand operations */
 
     bool has(const Lookup& l) const {
         return impl.lookup(l).found();
     }
 
     // Add |u| if it is not present already. Return false on oom.
@@ -814,17 +821,17 @@ class HashTable : private AllocPolicy
     // table operations unless |generation()| is tested.
     class Ptr
     {
         friend class HashTable;
 
         Entry* entry_;
 #ifdef JS_DEBUG
         const HashTable* table_;
-        uint32_t generation;
+        Generation generation;
 #endif
 
       protected:
         Ptr(Entry& entry, const HashTable& tableArg)
           : entry_(&entry)
 #ifdef JS_DEBUG
           , table_(&tableArg)
           , generation(tableArg.generation())
@@ -922,17 +929,17 @@ class HashTable : private AllocPolicy
                 ++cur;
         }
 
         Entry* cur;
         Entry* end;
 #ifdef JS_DEBUG
         const HashTable* table_;
         uint64_t mutationCount;
-        uint32_t generation;
+        Generation generation;
         bool validEntry;
 #endif
 
       public:
         Range()
           : cur(nullptr)
           , end(nullptr)
 #ifdef JS_DEBUG
@@ -1071,19 +1078,19 @@ class HashTable : private AllocPolicy
     // HashTable is not copyable or assignable
     HashTable(const HashTable&) = delete;
     void operator=(const HashTable&) = delete;
 
   private:
     static const size_t CAP_BITS = 30;
 
   public:
-    Entry*      table;                 // entry storage
-    uint32_t    gen:24;                 // entry storage generation number
-    uint32_t    hashShift:8;            // multiplicative hash shift
+    uint64_t    gen:56;                 // entry storage generation number
+    uint64_t    hashShift:8;            // multiplicative hash shift
+    Entry*      table;                  // entry storage
     uint32_t    entryCount;             // number of entries in table
     uint32_t    removedCount;           // removed entry sentinels in table
 
 #ifdef JS_DEBUG
     uint64_t     mutationCount;
     mutable bool mEntered;
     // Note that some updates to these stats are not thread-safe. See the
     // comment on the three-argument overloading of HashTable::lookup().
@@ -1170,19 +1177,19 @@ class HashTable : private AllocPolicy
         for (Entry* e = oldTable; e < end; ++e)
             e->destroyIfLive();
         alloc.free_(oldTable);
     }
 
   public:
     explicit HashTable(AllocPolicy ap)
       : AllocPolicy(ap)
-      , table(nullptr)
       , gen(0)
       , hashShift(sHashBits)
+      , table(nullptr)
       , entryCount(0)
       , removedCount(0)
 #ifdef JS_DEBUG
       , mutationCount(0)
       , mEntered(false)
 #endif
     {}
 
@@ -1611,20 +1618,20 @@ class HashTable : private AllocPolicy
     }
 
     uint32_t capacity() const
     {
         MOZ_ASSERT(table);
         return JS_BIT(sHashBits - hashShift);
     }
 
-    uint32_t generation() const
+    Generation generation() const
     {
         MOZ_ASSERT(table);
-        return gen;
+        return Generation(gen);
     }
 
     size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const
     {
         return mallocSizeOf(table);
     }
 
     size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const
--- a/js/src/jit/IonCaches.cpp
+++ b/js/src/jit/IonCaches.cpp
@@ -695,20 +695,20 @@ CheckDOMProxyExpandoDoesNotShadow(JSCont
                    tempVal);
 
     if (!expandoVal.isObject() && !expandoVal.isUndefined()) {
         masm.branchTestValue(Assembler::NotEqual, tempVal, expandoVal, &failDOMProxyCheck);
 
         ExpandoAndGeneration* expandoAndGeneration = (ExpandoAndGeneration*)expandoVal.toPrivate();
         masm.movePtr(ImmPtr(expandoAndGeneration), tempVal.scratchReg());
 
-        masm.branch32(Assembler::NotEqual,
+        masm.branch64(Assembler::NotEqual,
                       Address(tempVal.scratchReg(),
                               ExpandoAndGeneration::offsetOfGeneration()),
-                      Imm32(expandoAndGeneration->generation),
+                      Imm64(expandoAndGeneration->generation),
                       &failDOMProxyCheck);
 
         expandoVal = expandoAndGeneration->expando;
         masm.loadValue(Address(tempVal.scratchReg(),
                                ExpandoAndGeneration::offsetOfExpando()),
                        tempVal);
     }
 
--- a/js/src/jit/MacroAssembler.h
+++ b/js/src/jit/MacroAssembler.h
@@ -817,16 +817,23 @@ class MacroAssembler : public MacroAssem
     inline void branch32(Condition cond, const BaseIndex& lhs, Imm32 rhs, Label* label) PER_SHARED_ARCH;
 
     inline void branch32(Condition cond, const Operand& lhs, Register rhs, Label* label) PER_SHARED_ARCH;
     inline void branch32(Condition cond, const Operand& lhs, Imm32 rhs, Label* label) PER_SHARED_ARCH;
 
     inline void branch32(Condition cond, wasm::SymbolicAddress lhs, Imm32 rhs, Label* label)
         DEFINED_ON(arm, arm64, mips_shared, x86, x64);
 
+    inline void branch64(Condition cond, const Address& lhs, Imm64 val, Label* label) PER_ARCH;
+
+    // Compare the value at |lhs| with the value at |rhs|.  The scratch
+    // register *must not* be the base of |lhs| or |rhs|.
+    inline void branch64(Condition cond, const Address& lhs, const Address& rhs, Register scratch,
+                         Label* label) PER_ARCH;
+
     inline void branchPtr(Condition cond, Register lhs, Register rhs, Label* label) PER_SHARED_ARCH;
     inline void branchPtr(Condition cond, Register lhs, Imm32 rhs, Label* label) PER_SHARED_ARCH;
     inline void branchPtr(Condition cond, Register lhs, ImmPtr rhs, Label* label) PER_SHARED_ARCH;
     inline void branchPtr(Condition cond, Register lhs, ImmGCPtr rhs, Label* label) PER_SHARED_ARCH;
     inline void branchPtr(Condition cond, Register lhs, ImmWord rhs, Label* label) PER_SHARED_ARCH;
 
     inline void branchPtr(Condition cond, const Address& lhs, Register rhs, Label* label) PER_SHARED_ARCH;
     inline void branchPtr(Condition cond, const Address& lhs, ImmPtr rhs, Label* label) PER_SHARED_ARCH;
--- a/js/src/jit/SharedIC.cpp
+++ b/js/src/jit/SharedIC.cpp
@@ -3,16 +3,17 @@
  * 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/SharedIC.h"
 
 #include "mozilla/Casting.h"
 #include "mozilla/DebugOnly.h"
+#include "mozilla/IntegerPrintfMacros.h"
 #include "mozilla/SizePrintfMacros.h"
 
 #include "jslibmath.h"
 #include "jstypes.h"
 
 #include "gc/Policy.h"
 #include "jit/BaselineDebugModeOSR.h"
 #include "jit/BaselineIC.h"
@@ -2208,20 +2209,20 @@ UpdateExistingGenerationalDOMProxyStub(I
     MOZ_ASSERT(!expandoSlot.isObject() && !expandoSlot.isUndefined());
     ExpandoAndGeneration* expandoAndGeneration = (ExpandoAndGeneration*)expandoSlot.toPrivate();
     for (ICStubConstIterator iter = stub->beginChainConst(); !iter.atEnd(); iter++) {
         if (iter->isGetProp_CallDOMProxyWithGenerationNative()) {
             ICGetProp_CallDOMProxyWithGenerationNative* updateStub =
                 iter->toGetProp_CallDOMProxyWithGenerationNative();
             if (updateStub->expandoAndGeneration() == expandoAndGeneration) {
                 // Update generation
-                uint32_t generation = expandoAndGeneration->generation;
+                uint64_t generation = expandoAndGeneration->generation;
                 JitSpew(JitSpew_BaselineIC,
                         "  Updating existing stub with generation, old value: %i, "
-                        "new value: %i", updateStub->generation(),
+                        "new value: %" PRIu64 "", updateStub->generation(),
                         generation);
                 updateStub->setGeneration(generation);
                 return true;
             }
         }
     }
     return false;
 }
@@ -3840,19 +3841,19 @@ CheckDOMProxyExpandoDoesNotShadow(JSCont
 
     if (expandoAndGenerationAddr) {
         MOZ_ASSERT(generationAddr);
 
         masm.loadPtr(*expandoAndGenerationAddr, tempVal.scratchReg());
         masm.branchPrivatePtr(Assembler::NotEqual, expandoAddr, tempVal.scratchReg(),
                               &failDOMProxyCheck);
 
-        masm.load32(*generationAddr, scratch);
-        masm.branch32(Assembler::NotEqual,
-                      Address(tempVal.scratchReg(), offsetof(ExpandoAndGeneration, generation)),
+        masm.branch64(Assembler::NotEqual,
+                      Address(tempVal.scratchReg(), ExpandoAndGeneration::offsetOfGeneration()),
+                      *generationAddr,
                       scratch, &failDOMProxyCheck);
 
         masm.loadValue(Address(tempVal.scratchReg(), 0), tempVal);
     } else {
         masm.loadValue(expandoAddr, tempVal);
     }
 
     // If the incoming object does not have an expando object then we're sure we're not
@@ -3970,17 +3971,17 @@ ICStub*
 ICGetPropCallDOMProxyNativeCompiler::getStub(ICStubSpace* space)
 {
     RootedShape shape(cx, proxy_->maybeShape());
     RootedShape holderShape(cx, holder_->as<NativeObject>().lastProperty());
 
     Value expandoSlot = GetProxyExtra(proxy_, GetDOMProxyExpandoSlot());
     RootedShape expandoShape(cx, nullptr);
     ExpandoAndGeneration* expandoAndGeneration;
-    int32_t generation;
+    uint64_t generation;
     Value expandoVal;
     if (kind == ICStub::GetProp_CallDOMProxyNative) {
         expandoVal = expandoSlot;
         expandoAndGeneration = nullptr;  // initialize to silence GCC warning
         generation = 0;  // initialize to silence GCC warning
     } else {
         MOZ_ASSERT(kind == ICStub::GetProp_CallDOMProxyWithGenerationNative);
         MOZ_ASSERT(!expandoSlot.isObject() && !expandoSlot.isUndefined());
--- a/js/src/jit/SharedIC.h
+++ b/js/src/jit/SharedIC.h
@@ -3206,23 +3206,23 @@ class ICGetProp_CallDOMProxyNative : pub
                                                ICStub* firstMonitorStub,
                                                ICGetProp_CallDOMProxyNative& other);
 };
 
 class ICGetProp_CallDOMProxyWithGenerationNative : public ICGetPropCallDOMProxyNativeStub
 {
   protected:
     ExpandoAndGeneration* expandoAndGeneration_;
-    uint32_t generation_;
+    uint64_t generation_;
 
   public:
     ICGetProp_CallDOMProxyWithGenerationNative(JitCode* stubCode, ICStub* firstMonitorStub,
                                                Shape* shape,
                                                ExpandoAndGeneration* expandoAndGeneration,
-                                               uint32_t generation, Shape* expandoShape,
+                                               uint64_t generation, Shape* expandoShape,
                                                JSObject* holder, Shape* holderShape,
                                                JSFunction* getter, uint32_t pcOffset)
       : ICGetPropCallDOMProxyNativeStub(ICStub::GetProp_CallDOMProxyWithGenerationNative,
                                         stubCode, firstMonitorStub, shape,
                                         expandoShape, holder, holderShape, getter, pcOffset),
         expandoAndGeneration_(expandoAndGeneration),
         generation_(generation)
     {
@@ -3230,21 +3230,21 @@ class ICGetProp_CallDOMProxyWithGenerati
 
     static ICGetProp_CallDOMProxyWithGenerationNative*
     Clone(JSContext* cx, ICStubSpace* space, ICStub* firstMonitorStub,
           ICGetProp_CallDOMProxyWithGenerationNative& other);
 
     void* expandoAndGeneration() const {
         return expandoAndGeneration_;
     }
-    uint32_t generation() const {
+    uint64_t generation() const {
         return generation_;
     }
 
-    void setGeneration(uint32_t value) {
+    void setGeneration(uint64_t value) {
         generation_ = value;
     }
 
     static size_t offsetOfInternalStruct() {
         return offsetof(ICGetProp_CallDOMProxyWithGenerationNative, expandoAndGeneration_);
     }
     static size_t offsetOfGeneration() {
         return offsetof(ICGetProp_CallDOMProxyWithGenerationNative, generation_);
--- a/js/src/jit/arm/Assembler-arm.h
+++ b/js/src/jit/arm/Assembler-arm.h
@@ -1131,16 +1131,28 @@ class Operand
     DTRAddr toDTRAddr() const {
         return DTRAddr(baseReg(), DtrOffImm(offset));
     }
     VFPAddr toVFPAddr() const {
         return VFPAddr(baseReg(), VFPOffImm(offset));
     }
 };
 
+inline Imm32
+Imm64::firstHalf() const
+{
+    return low();
+}
+
+inline Imm32
+Imm64::secondHalf() const
+{
+    return hi();
+}
+
 void
 PatchJump(CodeLocationJump& jump_, CodeLocationLabel label,
           ReprotectCode reprotect = DontReprotect);
 
 static inline void
 PatchBackedge(CodeLocationJump& jump_, CodeLocationLabel label, JitRuntime::BackedgeTarget target)
 {
     PatchJump(jump_, label);
--- a/js/src/jit/arm/MacroAssembler-arm-inl.h
+++ b/js/src/jit/arm/MacroAssembler-arm-inl.h
@@ -511,16 +511,42 @@ void
 MacroAssembler::branch32(Condition cond, wasm::SymbolicAddress lhs, Imm32 rhs, Label* label)
 {
     ScratchRegisterScope scratch(*this);
     loadPtr(lhs, scratch);
     branch32(cond, scratch, rhs, label);
 }
 
 void
+MacroAssembler::branch64(Condition cond, const Address& lhs, Imm64 val, Label* label)
+{
+    MOZ_ASSERT(cond == Assembler::NotEqual,
+               "other condition codes not supported");
+
+    branch32(cond, lhs, val.firstHalf(), label);
+    branch32(cond, Address(lhs.base, lhs.offset + sizeof(uint32_t)), val.secondHalf(), label);
+}
+
+void
+MacroAssembler::branch64(Condition cond, const Address& lhs, const Address& rhs, Register scratch,
+                         Label* label)
+{
+    MOZ_ASSERT(cond == Assembler::NotEqual,
+               "other condition codes not supported");
+    MOZ_ASSERT(lhs.base != scratch);
+    MOZ_ASSERT(rhs.base != scratch);
+
+    load32(rhs, scratch);
+    branch32(cond, lhs, scratch, label);
+
+    load32(Address(rhs.base, rhs.offset + sizeof(uint32_t)), scratch);
+    branch32(cond, Address(lhs.base, lhs.offset + sizeof(uint32_t)), scratch, label);
+}
+
+void
 MacroAssembler::branchPtr(Condition cond, Register lhs, Register rhs, Label* label)
 {
     branch32(cond, lhs, rhs, label);
 }
 
 void
 MacroAssembler::branchPtr(Condition cond, Register lhs, Imm32 rhs, Label* label)
 {
--- a/js/src/jit/arm64/Assembler-arm64.h
+++ b/js/src/jit/arm64/Assembler-arm64.h
@@ -497,16 +497,28 @@ GetTempRegForIntArg(uint32_t usedIntArgs
     // can allocate.
     usedIntArgs -= NumIntArgRegs;
     if (usedIntArgs >= NumCallTempNonArgRegs)
         return false;
     *out = CallTempNonArgRegs[usedIntArgs];
     return true;
 }
 
+inline Imm32
+Imm64::firstHalf() const
+{
+    return low();
+}
+
+inline Imm32
+Imm64::secondHalf() const
+{
+    return hi();
+}
+
 void PatchJump(CodeLocationJump& jump_, CodeLocationLabel label,
                ReprotectCode reprotect = DontReprotect);
 
 static inline void
 PatchBackedge(CodeLocationJump& jump_, CodeLocationLabel label, JitRuntime::BackedgeTarget target)
 {
     PatchJump(jump_, label);
 }
--- a/js/src/jit/arm64/MacroAssembler-arm64-inl.h
+++ b/js/src/jit/arm64/MacroAssembler-arm64-inl.h
@@ -556,16 +556,37 @@ MacroAssembler::branch32(Condition cond,
 {
     vixl::UseScratchRegisterScope temps(this);
     const Register scratch = temps.AcquireX().asUnsized();
     movePtr(lhs, scratch);
     branch32(cond, Address(scratch, 0), rhs, label);
 }
 
 void
+MacroAssembler::branch64(Condition cond, const Address& lhs, Imm64 val, Label* label)
+{
+    MOZ_ASSERT(cond == Assembler::NotEqual,
+               "other condition codes not supported");
+
+    branchPtr(cond, lhs, ImmWord(val.value), label);
+}
+
+void
+MacroAssembler::branch64(Condition cond, const Address& lhs, const Address& rhs, Register scratch,
+                         Label* label)
+{
+    MOZ_ASSERT(cond == Assembler::NotEqual,
+               "other condition codes not supported");
+    MOZ_ASSERT(lhs.base != scratch);
+    MOZ_ASSERT(rhs.base != scratch);
+
+    loadPtr(rhs, scratch);
+    branchPtr(cond, lhs, scratch, label);
+}
+void
 MacroAssembler::branchPtr(Condition cond, Register lhs, Register rhs, Label* label)
 {
     Cmp(ARMRegister(lhs, 64), ARMRegister(rhs, 64));
     B(label, cond);
 }
 
 void
 MacroAssembler::branchPtr(Condition cond, Register lhs, Imm32 rhs, Label* label)
--- a/js/src/jit/mips-shared/Assembler-mips-shared.h
+++ b/js/src/jit/mips-shared/Assembler-mips-shared.h
@@ -631,16 +631,28 @@ class Operand
         return reg;
     }
     Register baseReg() const {
         MOZ_ASSERT(tag == MEM);
         return Register::FromCode(reg);
     }
 };
 
+inline Imm32
+Imm64::firstHalf() const
+{
+    return low();
+}
+
+inline Imm32
+Imm64::secondHalf() const
+{
+    return hi();
+}
+
 void
 PatchJump(CodeLocationJump& jump_, CodeLocationLabel label,
           ReprotectCode reprotect = DontReprotect);
 
 void
 PatchBackedge(CodeLocationJump& jump_, CodeLocationLabel label, JitRuntime::BackedgeTarget target);
 
 typedef js::jit::AssemblerBuffer<1024, Instruction> MIPSBuffer;
--- a/js/src/jit/mips32/MacroAssembler-mips32-inl.h
+++ b/js/src/jit/mips32/MacroAssembler-mips32-inl.h
@@ -264,16 +264,42 @@ MacroAssembler::rshift64(Imm32 imm, Regi
     as_or(dest.low, dest.low, scratch);
     as_srl(dest.high, dest.high, imm.value);
 }
 
 // ===============================================================
 // Branch functions
 
 void
+MacroAssembler::branch64(Condition cond, const Address& lhs, Imm64 val, Label* label)
+{
+    MOZ_ASSERT(cond == Assembler::NotEqual,
+               "other condition codes not supported");
+
+    branch32(cond, lhs, val.firstHalf(), label);
+    branch32(cond, Address(lhs.base, lhs.offset + sizeof(uint32_t)), val.secondHalf(), label);
+}
+
+void
+MacroAssembler::branch64(Condition cond, const Address& lhs, const Address& rhs, Register scratch,
+                         Label* label)
+{
+    MOZ_ASSERT(cond == Assembler::NotEqual,
+               "other condition codes not supported");
+    MOZ_ASSERT(lhs.base != scratch);
+    MOZ_ASSERT(rhs.base != scratch);
+
+    load32(rhs, scratch);
+    branch32(cond, lhs, scratch, label);
+
+    load32(Address(rhs.base, rhs.offset + sizeof(uint32_t)), scratch);
+    branch32(cond, Address(lhs.base, lhs.offset + sizeof(uint32_t)), scratch, label);
+}
+
+void
 MacroAssembler::branchPrivatePtr(Condition cond, const Address& lhs, Register rhs, Label* label)
 {
     branchPtr(cond, lhs, rhs, label);
 }
 
 void
 MacroAssembler::branchTest64(Condition cond, Register64 lhs, Register64 rhs, Register temp,
                              Label* label)
--- a/js/src/jit/mips64/MacroAssembler-mips64-inl.h
+++ b/js/src/jit/mips64/MacroAssembler-mips64-inl.h
@@ -203,16 +203,38 @@ MacroAssembler::rshift64(Imm32 imm, Regi
 {
     ma_dsrl(dest.reg, dest.reg, imm);
 }
 
 // ===============================================================
 // Branch functions
 
 void
+MacroAssembler::branch64(Condition cond, const Address& lhs, Imm64 val, Label* label)
+{
+    MOZ_ASSERT(cond == Assembler::NotEqual,
+               "other condition codes not supported");
+
+    branchPtr(cond, lhs, ImmWord(val.value), label);
+}
+
+void
+MacroAssembler::branch64(Condition cond, const Address& lhs, const Address& rhs, Register scratch,
+                         Label* label)
+{
+    MOZ_ASSERT(cond == Assembler::NotEqual,
+               "other condition codes not supported");
+    MOZ_ASSERT(lhs.base != scratch);
+    MOZ_ASSERT(rhs.base != scratch);
+
+    loadPtr(rhs, scratch);
+    branchPtr(cond, lhs, scratch, label);
+}
+
+void
 MacroAssembler::branchPrivatePtr(Condition cond, const Address& lhs, Register rhs, Label* label)
 {
     if (rhs != ScratchRegister)
         movePtr(rhs, ScratchRegister);
     // Instead of unboxing lhs, box rhs and do direct comparison with lhs.
     rshiftPtr(Imm32(1), ScratchRegister);
     branchPtr(cond, lhs, ScratchRegister, label);
 }
--- a/js/src/jit/shared/Assembler-shared.h
+++ b/js/src/jit/shared/Assembler-shared.h
@@ -127,16 +127,27 @@ struct ImmWord
 
 // Used for 64-bit immediates which do not require relocation.
 struct Imm64
 {
     uint64_t value;
 
     explicit Imm64(uint64_t value) : value(value)
     { }
+
+    Imm32 low() const {
+        return Imm32(int32_t(value));
+    }
+
+    Imm32 hi() const {
+        return Imm32(int32_t(value >> 32));
+    }
+
+    inline Imm32 firstHalf() const;
+    inline Imm32 secondHalf() const;
 };
 
 #ifdef DEBUG
 static inline bool
 IsCompilingAsmJS()
 {
     // asm.js compilation pushes a JitContext with a null JSCompartment.
     return GetJitContext()->compartment == nullptr;
--- a/js/src/jit/x64/MacroAssembler-x64-inl.h
+++ b/js/src/jit/x64/MacroAssembler-x64-inl.h
@@ -303,16 +303,38 @@ void
 MacroAssembler::branch32(Condition cond, wasm::SymbolicAddress lhs, Imm32 rhs, Label* label)
 {
     ScratchRegisterScope scratch(*this);
     mov(lhs, scratch);
     branch32(cond, Address(scratch, 0), rhs, label);
 }
 
 void
+MacroAssembler::branch64(Condition cond, const Address& lhs, Imm64 val, Label* label)
+{
+    MOZ_ASSERT(cond == Assembler::NotEqual,
+               "other condition codes not supported");
+
+    branchPtr(cond, lhs, ImmWord(val.value), label);
+}
+
+void
+MacroAssembler::branch64(Condition cond, const Address& lhs, const Address& rhs, Register scratch,
+                         Label* label)
+{
+    MOZ_ASSERT(cond == Assembler::NotEqual,
+               "other condition codes not supported");
+    MOZ_ASSERT(lhs.base != scratch);
+    MOZ_ASSERT(rhs.base != scratch);
+
+    loadPtr(rhs, scratch);
+    branchPtr(cond, lhs, scratch, label);
+}
+
+void
 MacroAssembler::branchPtr(Condition cond, const AbsoluteAddress& lhs, Register rhs, Label* label)
 {
     ScratchRegisterScope scratch(*this);
     MOZ_ASSERT(rhs != scratch);
     if (X86Encoding::IsAddressImmediate(lhs.addr)) {
         branchPtrImpl(cond, Operand(lhs), rhs, label);
     } else {
         mov(ImmPtr(lhs.addr), scratch);
--- a/js/src/jit/x86-shared/Assembler-x86-shared.h
+++ b/js/src/jit/x86-shared/Assembler-x86-shared.h
@@ -168,16 +168,28 @@ class Operand
           case MEM_REG_DISP: return r.encoding() == base();
           case MEM_SCALE:    return r.encoding() == base() || r.encoding() == index();
           default: MOZ_CRASH("Unexpected Operand kind");
         }
         return false;
     }
 };
 
+inline Imm32
+Imm64::firstHalf() const
+{
+    return low();
+}
+
+inline Imm32
+Imm64::secondHalf() const
+{
+    return hi();
+}
+
 class CPUInfo
 {
   public:
     // As the SSE's were introduced in order, the presence of a later SSE implies
     // the presence of an earlier SSE. For example, SSE4_2 support implies SSE2 support.
     enum SSEVersion {
         UnknownSSE = 0,
         NoSSE = 1,
--- a/js/src/jit/x86/MacroAssembler-x86-inl.h
+++ b/js/src/jit/x86/MacroAssembler-x86-inl.h
@@ -304,16 +304,42 @@ MacroAssembler::branch32(Condition cond,
 void
 MacroAssembler::branch32(Condition cond, wasm::SymbolicAddress lhs, Imm32 rhs, Label* label)
 {
     cmpl(rhs, lhs);
     j(cond, label);
 }
 
 void
+MacroAssembler::branch64(Condition cond, const Address& lhs, Imm64 val, Label* label)
+{
+    MOZ_ASSERT(cond == Assembler::NotEqual,
+               "other condition codes not supported");
+
+    branch32(cond, lhs, val.firstHalf(), label);
+    branch32(cond, Address(lhs.base, lhs.offset + sizeof(uint32_t)), val.secondHalf(), label);
+}
+
+void
+MacroAssembler::branch64(Condition cond, const Address& lhs, const Address& rhs, Register scratch,
+                         Label* label)
+{
+    MOZ_ASSERT(cond == Assembler::NotEqual,
+               "other condition codes not supported");
+    MOZ_ASSERT(lhs.base != scratch);
+    MOZ_ASSERT(rhs.base != scratch);
+
+    load32(rhs, scratch);
+    branch32(cond, lhs, scratch, label);
+
+    load32(Address(rhs.base, rhs.offset + sizeof(uint32_t)), scratch);
+    branch32(cond, Address(lhs.base, lhs.offset + sizeof(uint32_t)), scratch, label);
+}
+
+void
 MacroAssembler::branchPtr(Condition cond, const AbsoluteAddress& lhs, Register rhs, Label* label)
 {
     branchPtrImpl(cond, lhs, rhs, label);
 }
 
 void
 MacroAssembler::branchPtr(Condition cond, const AbsoluteAddress& lhs, ImmWord rhs, Label* label)
 {
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -302,20 +302,16 @@ class MOZ_RAII AutoHashMapRooter : prote
 
     size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
         return map.sizeOfExcludingThis(mallocSizeOf);
     }
     size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
         return map.sizeOfIncludingThis(mallocSizeOf);
     }
 
-    uint32_t generation() const {
-        return map.generation();
-    }
-
     /************************************************** Shorthand operations */
 
     bool has(const Lookup& l) const {
         return map.has(l);
     }
 
     template<typename KeyInput, typename ValueInput>
     bool put(const KeyInput& k, const ValueInput& v) {
@@ -417,20 +413,16 @@ class MOZ_RAII AutoHashSetRooter : prote
 
     size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
         return set.sizeOfExcludingThis(mallocSizeOf);
     }
     size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
         return set.sizeOfIncludingThis(mallocSizeOf);
     }
 
-    uint32_t generation() const {
-        return set.generation();
-    }
-
     /************************************************** Shorthand operations */
 
     bool has(const Lookup& l) const {
         return set.has(l);
     }
 
     bool put(const T& t) {
         return set.put(t);
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -46,21 +46,21 @@ class MOZ_RAII AutoCycleDetector
 
     ~AutoCycleDetector();
 
     bool init();
 
     bool foundCycle() { return cyclic; }
 
   private:
+    Generation hashsetGenerationAtInit;
     JSContext* cx;
     RootedObject obj;
+    Set::AddPtr hashsetAddPointer;
     bool cyclic;
-    uint32_t hashsetGenerationAtInit;
-    Set::AddPtr hashsetAddPointer;
     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
 
 /* Updates references in the cycle detection set if the GC moves them. */
 extern void
 TraceCycleDetectionSet(JSTracer* trc, AutoCycleDetector::Set& set);
 
 struct AutoResolving;
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -1217,17 +1217,17 @@ NukeCrossCompartmentWrappers(JSContext* 
  *   and the expando object in the ExpandoAndGeneration has the property in
  *   question.
  * * If DoesntShadow is returned then the slot at listBaseExpandoSlot should
  *   either be undefined or point to an expando object that would contain the
  *   own property.
  * * If DoesntShadowUnique is returned then the slot at listBaseExpandoSlot
  *   should contain a private pointer to a ExpandoAndGeneration, which contains
  *   a JS::Value that should either be undefined or point to an expando object,
- *   and a uint32 value. If that value changes then the IC for getting a
+ *   and a uint64 value. If that value changes then the IC for getting a
  *   property will be invalidated.
  * * If Shadows is returned, that means the property is an own property of the
  *   proxy but doesn't live on the expando object.
  */
 
 struct ExpandoAndGeneration {
   ExpandoAndGeneration()
     : expando(JS::UndefinedValue()),
@@ -1246,17 +1246,17 @@ struct ExpandoAndGeneration {
   }
 
   static size_t offsetOfGeneration()
   {
       return offsetof(ExpandoAndGeneration, generation);
   }
 
   JS::Heap<JS::Value> expando;
-  uint32_t generation;
+  uint64_t generation;
 };
 
 typedef enum DOMProxyShadowsResult {
   ShadowCheckFailed,
   Shadows,
   DoesntShadow,
   DoesntShadowUnique,
   ShadowsViaDirectExpando,
--- a/js/src/jswatchpoint.cpp
+++ b/js/src/jswatchpoint.cpp
@@ -22,25 +22,25 @@ WatchKeyHasher::hash(const Lookup& key)
 {
     return MovableCellHasher<PreBarrieredObject>::hash(key.object) ^ HashId(key.id);
 }
 
 namespace {
 
 class AutoEntryHolder {
     typedef WatchpointMap::Map Map;
+    Generation gen;
     Map& map;
     Map::Ptr p;
-    uint32_t gen;
     RootedObject obj;
     RootedId id;
 
   public:
     AutoEntryHolder(JSContext* cx, Map& map, Map::Ptr p)
-      : map(map), p(p), gen(map.generation()), obj(cx, p->key().object), id(cx, p->key().id)
+      : gen(map.generation()), map(map), p(p), obj(cx, p->key().object), id(cx, p->key().id)
     {
         MOZ_ASSERT(!p->value().held);
         p->value().held = true;
     }
 
     ~AutoEntryHolder() {
         if (gen != map.generation())
             p = map.lookup(WatchKey(obj, id));
--- a/js/src/vm/UbiNodeCensus.cpp
+++ b/js/src/vm/UbiNodeCensus.cpp
@@ -728,17 +728,17 @@ ByAllocationStack::count(CountBase& coun
 
 bool
 ByAllocationStack::report(JSContext* cx, CountBase& countBase, MutableHandleValue report)
 {
     Count& count = static_cast<Count&>(countBase);
 
 #ifdef DEBUG
     // Check that nothing rehashes our table while we hold pointers into it.
-    uint32_t generation = count.table.generation();
+    Generation generation = count.table.generation();
 #endif
 
     // Build a vector of pointers to entries; sort by total; and then use
     // that to build the result object. This makes the ordering of entries
     // more interesting, and a little less non-deterministic.
     mozilla::Vector<Entry*> entries;
     if (!entries.reserve(count.table.count()))
         return false;
new file mode 100644
--- /dev/null
+++ b/mfbt/Opaque.h
@@ -0,0 +1,44 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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/. */
+
+/* An opaque integral type supporting only comparison operators. */
+
+#ifndef mozilla_Opaque_h
+#define mozilla_Opaque_h
+
+#include "mozilla/TypeTraits.h"
+
+namespace mozilla {
+
+/**
+ * Opaque<T> is a replacement for integral T in cases where only comparisons
+ * must be supported, and it's desirable to prevent accidental dependency on
+ * exact values.
+ */
+template<typename T>
+class Opaque final
+{
+  static_assert(mozilla::IsIntegral<T>::value,
+                "mozilla::Opaque only supports integral types");
+
+  T mValue;
+
+public:
+  Opaque() {}
+  explicit Opaque(T aValue) : mValue(aValue) {}
+
+  bool operator==(const Opaque& aOther) const {
+    return mValue == aOther.mValue;
+  }
+
+  bool operator!=(const Opaque& aOther) const {
+    return !(*this == aOther);
+  }
+};
+
+} // namespace mozilla
+
+#endif /* mozilla_Opaque_h */
--- a/mfbt/moz.build
+++ b/mfbt/moz.build
@@ -56,16 +56,17 @@ EXPORTS.mozilla = [
     'MathAlgorithms.h',
     'Maybe.h',
     'MaybeOneOf.h',
     'MemoryChecking.h',
     'MemoryReporting.h',
     'Move.h',
     'NullPtr.h',
     'NumericLimits.h',
+    'Opaque.h',
     'Pair.h',
     'PodOperations.h',
     'Poison.h',
     'Range.h',
     'RangedArray.h',
     'RangedPtr.h',
     'ReentrancyGuard.h',
     'RefCounted.h',