diff --git a/js/public/GCHashTable.h b/js/public/GCHashTable.h
--- a/js/public/GCHashTable.h
+++ b/js/public/GCHashTable.h
@@ -143,7 +143,6 @@ class GCHashMapOperations
     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(); }
 };
 
@@ -282,7 +281,6 @@ class GCHashSetOperations
     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(); }
 };
 
diff --git a/js/public/HashTable.h b/js/public/HashTable.h
--- a/js/public/HashTable.h
+++ b/js/public/HashTable.h
@@ -14,6 +14,7 @@
 #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"
@@ -34,6 +35,8 @@ 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.
@@ -208,7 +211,9 @@ class HashMap
 
     // 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 */
 
@@ -446,7 +451,9 @@ class HashSet
 
     // 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 */
 
@@ -819,7 +826,7 @@ class HashTable : private AllocPolicy
         Entry* entry_;
 #ifdef JS_DEBUG
         const HashTable* table_;
-        uint32_t generation;
+        Generation generation;
 #endif
 
       protected:
@@ -927,7 +934,7 @@ class HashTable : private AllocPolicy
 #ifdef JS_DEBUG
         const HashTable* table_;
         uint64_t mutationCount;
-        uint32_t generation;
+        Generation generation;
         bool validEntry;
 #endif
 
@@ -1076,9 +1083,9 @@ class HashTable : private AllocPolicy
     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
 
@@ -1175,9 +1182,9 @@ class HashTable : private AllocPolicy
   public:
     explicit HashTable(AllocPolicy ap)
       : AllocPolicy(ap)
-      , table(nullptr)
       , gen(0)
       , hashShift(sHashBits)
+      , table(nullptr)
       , entryCount(0)
       , removedCount(0)
 #ifdef JS_DEBUG
@@ -1616,10 +1623,10 @@ class HashTable : private AllocPolicy
         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
diff --git a/js/src/jit/IonCaches.cpp b/js/src/jit/IonCaches.cpp
--- a/js/src/jit/IonCaches.cpp
+++ b/js/src/jit/IonCaches.cpp
@@ -700,10 +700,10 @@ CheckDOMProxyExpandoDoesNotShadow(JSCont
         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;
diff --git a/js/src/jit/MacroAssembler.h b/js/src/jit/MacroAssembler.h
--- a/js/src/jit/MacroAssembler.h
+++ b/js/src/jit/MacroAssembler.h
@@ -822,6 +822,13 @@ class MacroAssembler : public MacroAssem
     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;
diff --git a/js/src/jit/SharedIC.cpp b/js/src/jit/SharedIC.cpp
--- a/js/src/jit/SharedIC.cpp
+++ b/js/src/jit/SharedIC.cpp
@@ -8,6 +8,7 @@
 
 #include "mozilla/Casting.h"
 #include "mozilla/DebugOnly.h"
+#include "mozilla/IntegerPrintfMacros.h"
 #include "mozilla/SizePrintfMacros.h"
 
 #include "jslibmath.h"
@@ -2212,10 +2213,10 @@ UpdateExistingGenerationalDOMProxyStub(I
                 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;
@@ -3843,9 +3844,9 @@ CheckDOMProxyExpandoDoesNotShadow(JSCont
         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);
@@ -3973,7 +3974,7 @@ ICGetPropCallDOMProxyNativeCompiler::get
     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;
diff --git a/js/src/jit/SharedIC.h b/js/src/jit/SharedIC.h
--- a/js/src/jit/SharedIC.h
+++ b/js/src/jit/SharedIC.h
@@ -3187,13 +3187,13 @@ class ICGetProp_CallDOMProxyWithGenerati
 {
   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,
@@ -3211,11 +3211,11 @@ class ICGetProp_CallDOMProxyWithGenerati
     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;
     }
 
diff --git a/js/src/jit/arm/Assembler-arm.h b/js/src/jit/arm/Assembler-arm.h
--- a/js/src/jit/arm/Assembler-arm.h
+++ b/js/src/jit/arm/Assembler-arm.h
@@ -1136,6 +1136,18 @@ class Operand
     }
 };
 
+inline Imm32
+Imm64::firstHalf() const
+{
+    return low();
+}
+
+inline Imm32
+Imm64::secondHalf() const
+{
+    return hi();
+}
+
 void
 PatchJump(CodeLocationJump& jump_, CodeLocationLabel label,
           ReprotectCode reprotect = DontReprotect);
diff --git a/js/src/jit/arm/MacroAssembler-arm-inl.h b/js/src/jit/arm/MacroAssembler-arm-inl.h
--- a/js/src/jit/arm/MacroAssembler-arm-inl.h
+++ b/js/src/jit/arm/MacroAssembler-arm-inl.h
@@ -516,6 +516,32 @@ MacroAssembler::branch32(Condition cond,
 }
 
 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);
diff --git a/js/src/jit/arm64/Assembler-arm64.h b/js/src/jit/arm64/Assembler-arm64.h
--- a/js/src/jit/arm64/Assembler-arm64.h
+++ b/js/src/jit/arm64/Assembler-arm64.h
@@ -502,6 +502,18 @@ GetTempRegForIntArg(uint32_t 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);
 
diff --git a/js/src/jit/arm64/MacroAssembler-arm64-inl.h b/js/src/jit/arm64/MacroAssembler-arm64-inl.h
--- a/js/src/jit/arm64/MacroAssembler-arm64-inl.h
+++ b/js/src/jit/arm64/MacroAssembler-arm64-inl.h
@@ -561,6 +561,27 @@ MacroAssembler::branch32(Condition cond,
 }
 
 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));
diff --git a/js/src/jit/mips-shared/Assembler-mips-shared.h b/js/src/jit/mips-shared/Assembler-mips-shared.h
--- a/js/src/jit/mips-shared/Assembler-mips-shared.h
+++ b/js/src/jit/mips-shared/Assembler-mips-shared.h
@@ -636,6 +636,18 @@ class Operand
     }
 };
 
+inline Imm32
+Imm64::firstHalf() const
+{
+    return low();
+}
+
+inline Imm32
+Imm64::secondHalf() const
+{
+    return hi();
+}
+
 void
 PatchJump(CodeLocationJump& jump_, CodeLocationLabel label,
           ReprotectCode reprotect = DontReprotect);
diff --git a/js/src/jit/mips32/MacroAssembler-mips32-inl.h b/js/src/jit/mips32/MacroAssembler-mips32-inl.h
--- a/js/src/jit/mips32/MacroAssembler-mips32-inl.h
+++ b/js/src/jit/mips32/MacroAssembler-mips32-inl.h
@@ -269,6 +269,32 @@ MacroAssembler::rshift64(Imm32 imm, Regi
 // 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);
diff --git a/js/src/jit/mips64/MacroAssembler-mips64-inl.h b/js/src/jit/mips64/MacroAssembler-mips64-inl.h
--- a/js/src/jit/mips64/MacroAssembler-mips64-inl.h
+++ b/js/src/jit/mips64/MacroAssembler-mips64-inl.h
@@ -208,6 +208,28 @@ MacroAssembler::rshift64(Imm32 imm, Regi
 // 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)
diff --git a/js/src/jit/shared/Assembler-shared.h b/js/src/jit/shared/Assembler-shared.h
--- a/js/src/jit/shared/Assembler-shared.h
+++ b/js/src/jit/shared/Assembler-shared.h
@@ -132,6 +132,17 @@ struct Imm64
 
     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
diff --git a/js/src/jit/x64/MacroAssembler-x64-inl.h b/js/src/jit/x64/MacroAssembler-x64-inl.h
--- a/js/src/jit/x64/MacroAssembler-x64-inl.h
+++ b/js/src/jit/x64/MacroAssembler-x64-inl.h
@@ -308,6 +308,28 @@ MacroAssembler::branch32(Condition cond,
 }
 
 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);
diff --git a/js/src/jit/x86-shared/Assembler-x86-shared.h b/js/src/jit/x86-shared/Assembler-x86-shared.h
--- a/js/src/jit/x86-shared/Assembler-x86-shared.h
+++ b/js/src/jit/x86-shared/Assembler-x86-shared.h
@@ -173,6 +173,18 @@ class Operand
     }
 };
 
+inline Imm32
+Imm64::firstHalf() const
+{
+    return low();
+}
+
+inline Imm32
+Imm64::secondHalf() const
+{
+    return hi();
+}
+
 class CPUInfo
 {
   public:
diff --git a/js/src/jit/x86/MacroAssembler-x86-inl.h b/js/src/jit/x86/MacroAssembler-x86-inl.h
--- a/js/src/jit/x86/MacroAssembler-x86-inl.h
+++ b/js/src/jit/x86/MacroAssembler-x86-inl.h
@@ -309,6 +309,32 @@ MacroAssembler::branch32(Condition cond,
 }
 
 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);
diff --git a/js/src/jsapi.h b/js/src/jsapi.h
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -307,10 +307,6 @@ class MOZ_RAII AutoHashMapRooter : prote
         return map.sizeOfIncludingThis(mallocSizeOf);
     }
 
-    uint32_t generation() const {
-        return map.generation();
-    }
-
     /************************************************** Shorthand operations */
 
     bool has(const Lookup& l) const {
@@ -422,10 +418,6 @@ class MOZ_RAII AutoHashSetRooter : prote
         return set.sizeOfIncludingThis(mallocSizeOf);
     }
 
-    uint32_t generation() const {
-        return set.generation();
-    }
-
     /************************************************** Shorthand operations */
 
     bool has(const Lookup& l) const {
diff --git a/js/src/jscntxt.h b/js/src/jscntxt.h
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -51,11 +51,11 @@ class MOZ_RAII AutoCycleDetector
     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
 };
 
diff --git a/js/src/jsfriendapi.h b/js/src/jsfriendapi.h
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -1222,7 +1222,7 @@ NukeCrossCompartmentWrappers(JSContext* 
  * * 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.
@@ -1251,7 +1251,7 @@ struct ExpandoAndGeneration {
   }
 
   JS::Heap<JS::Value> expando;
-  uint32_t generation;
+  uint64_t generation;
 };
 
 typedef enum DOMProxyShadowsResult {
diff --git a/js/src/jswatchpoint.cpp b/js/src/jswatchpoint.cpp
--- a/js/src/jswatchpoint.cpp
+++ b/js/src/jswatchpoint.cpp
@@ -27,15 +27,15 @@ 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;
diff --git a/js/src/vm/UbiNodeCensus.cpp b/js/src/vm/UbiNodeCensus.cpp
--- a/js/src/vm/UbiNodeCensus.cpp
+++ b/js/src/vm/UbiNodeCensus.cpp
@@ -733,7 +733,7 @@ ByAllocationStack::report(JSContext* cx,
 
 #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
diff --git a/mfbt/Opaque.h b/mfbt/Opaque.h
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 */
diff --git a/mfbt/moz.build b/mfbt/moz.build
--- a/mfbt/moz.build
+++ b/mfbt/moz.build
@@ -61,6 +61,7 @@ EXPORTS.mozilla = [
     'Move.h',
     'NullPtr.h',
     'NumericLimits.h',
+    'Opaque.h',
     'Pair.h',
     'PodOperations.h',
     'Poison.h',
