Merge inbound to mozilla-central. a=merge
authorTiberius Oros <toros@mozilla.com>
Tue, 03 Jul 2018 00:53:57 +0300
changeset 424720 7d20e7fae1039720f92db1a3a72bc2c7424b5f98
parent 424719 959983b7d19ef31d27b55e488d21743aec3af74c (current diff)
parent 424635 1ef50d759e7dc801a46eb8350cfeca82850a05bd (diff)
child 424721 e2dca9594b909bfe8bcd77186c270de9210bf23a
child 424752 d783a8ddd72d15bfef89a1e81f31f0dd35cfa276
push id104897
push usertoros@mozilla.com
push dateMon, 02 Jul 2018 22:00:15 +0000
treeherdermozilla-inbound@e2dca9594b90 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone63.0a1
first release with
nightly linux32
7d20e7fae103 / 63.0a1 / 20180702220046 / files
nightly linux64
7d20e7fae103 / 63.0a1 / 20180702220046 / files
nightly mac
7d20e7fae103 / 63.0a1 / 20180702220046 / files
nightly win32
7d20e7fae103 / 63.0a1 / 20180702220046 / files
nightly win64
7d20e7fae103 / 63.0a1 / 20180702220046 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge inbound to mozilla-central. a=merge
--- a/dom/messagechannel/MessagePort.cpp
+++ b/dom/messagechannel/MessagePort.cpp
@@ -171,19 +171,19 @@ private:
 NS_IMPL_CYCLE_COLLECTION_CLASS(MessagePort)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(MessagePort,
                                                 DOMEventTargetHelper)
   if (tmp->mPostMessageRunnable) {
     NS_IMPL_CYCLE_COLLECTION_UNLINK(mPostMessageRunnable->mPort);
   }
 
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mMessages);
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mMessagesForTheOtherPort);
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mUnshippedEntangledPort);
+
+  tmp->CloseForced();
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(MessagePort,
                                                   DOMEventTargetHelper)
   if (tmp->mPostMessageRunnable) {
     NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPostMessageRunnable->mPort);
   }
 
--- a/js/src/devtools/automation/variants/msan
+++ b/js/src/devtools/automation/variants/msan
@@ -4,11 +4,11 @@
     "debug": false,
     "compiler": "clang",
     "env": {
         "JITTEST_EXTRA_ARGS": "--jitflags=interp --ignore-timeouts={DIR}/cgc-jittest-timeouts.txt",
         "JSTESTS_EXTRA_ARGS": "--jitflags=interp --exclude-file={DIR}/cgc-jstests-slow.txt",
         "MSAN_OPTIONS": "external_symbolizer_path={TOOLTOOL_CHECKOUT}/clang/bin/llvm-symbolizer:log_path={OUTDIR}/sanitize_log"
     },
     "ignore-test-failures": "true",
-    "max-errors": 6,
+    "max-errors": 7,
     "use_minidump": false
 }
--- a/js/src/gc/Nursery.h
+++ b/js/src/gc/Nursery.h
@@ -49,18 +49,16 @@ class PlainObject;
 class NativeObject;
 class Nursery;
 struct NurseryChunk;
 class HeapSlot;
 class JSONPrinter;
 class MapObject;
 class SetObject;
 
-void SetGCZeal(JSRuntime*, uint8_t, uint32_t);
-
 namespace gc {
 class AutoMaybeStartBackgroundAllocation;
 class AutoTraceSession;
 struct Cell;
 class MinorCollectionTracer;
 class RelocationOverlay;
 struct TenureCountCache;
 enum class AllocKind : uint8_t;
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/wasm/gc/anyref-global-object.js
@@ -0,0 +1,94 @@
+if (!wasmGcEnabled() || typeof WebAssembly.Global !== 'function') {
+    quit(0);
+}
+
+// Dummy object.
+function Baguette(calories) {
+    this.calories = calories;
+}
+
+assertEq(new WebAssembly.Global({value: "anyref"}) instanceof WebAssembly.Global, true);
+
+(function() {
+    // Test initialization without a value.
+    let g = new WebAssembly.Global({value: "anyref"});
+    assertEq(g.value, null);
+    assertErrorMessage(() => g.value = 42, TypeError, /immutable global/);
+})();
+
+(function() {
+    // Test initialization with a value.
+    let g = new WebAssembly.Global({value: "anyref"}, null);
+    assertEq(g.value, null);
+    assertErrorMessage(() => g.value = 42, TypeError, /immutable global/);
+
+    let obj = {};
+    g = new WebAssembly.Global({value: "anyref"}, obj);
+    assertEq(g.value, obj);
+    assertErrorMessage(() => g.value = 42, TypeError, /immutable global/);
+
+    g = new WebAssembly.Global({value: "anyref"}, 1337);
+    assertEq(g.value instanceof Number, true);
+    assertEq(+g.value, 1337);
+
+    g = new WebAssembly.Global({value: "anyref"}, 13.37);
+    assertEq(g.value instanceof Number, true);
+    assertEq(+g.value, 13.37);
+
+    g = new WebAssembly.Global({value: "anyref"}, "string");
+    assertEq(g.value instanceof String, true);
+    assertEq(g.value.toString(), "string");
+
+    g = new WebAssembly.Global({value: "anyref"}, true);
+    assertEq(g.value instanceof Boolean, true);
+    assertEq(!!g.value, true);
+
+    g = new WebAssembly.Global({value: "anyref"}, Symbol("status"));
+    assertEq(g.value instanceof Symbol, true);
+    assertEq(g.value.toString(), "Symbol(status)");
+
+    assertErrorMessage(() => new WebAssembly.Global({value: "anyref"}, undefined),
+                       TypeError,
+                       "can't convert undefined to object");
+})();
+
+(function() {
+    // Test mutable property and assignment.
+    let g = new WebAssembly.Global({value: "anyref", mutable: true}, null);
+    assertEq(g.value, null);
+
+    let obj = { x: 42 };
+    g.value = obj;
+    assertEq(g.value, obj);
+    assertEq(g.value.x, 42);
+
+    obj = null;
+    assertEq(g.value.x, 42);
+
+    let otherObj = { y : 35 };
+    g.value = otherObj;
+    assertEq(g.value, otherObj);
+})();
+
+(function() {
+    // Test tracing.
+    let nom = new Baguette(1);
+    let g = new WebAssembly.Global({value: "anyref"}, nom);
+    nom = null;
+    gc();
+    assertEq(g.value.calories, 1);
+})();
+
+var global = new WebAssembly.Global({ value: "anyref", mutable: true }, null);
+
+// GCZeal mode 2 implies that every allocation (second parameter = every single
+// allocation) will trigger a full GC.
+gczeal(2, 1);
+
+{
+    let nomnom = new Baguette(42);
+    global.value = nomnom;
+    nomnom = null;
+}
+new Baguette();
+assertEq(global.value.calories, 42);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/wasm/gc/anyref-global-postbarrier.js
@@ -0,0 +1,72 @@
+if (!wasmGcEnabled()) {
+    quit(0);
+}
+
+const { startProfiling, endProfiling, assertEqPreciseStacks, isSingleStepProfilingEnabled } = WasmHelpers;
+
+// Dummy constructor.
+function Baguette(calories) {
+    this.calories = calories;
+}
+
+let exportsPlain = wasmEvalText(`(module
+    (global i32 (i32.const 42))
+    (global $g (mut anyref) (ref.null anyref))
+    (func (export "set") (param anyref) get_local 0 set_global $g)
+    (func (export "get") (result anyref) get_global $g)
+)`).exports;
+
+let exportsObj = wasmEvalText(`(module
+    (global $g (export "g") (mut anyref) (ref.null anyref))
+    (func (export "set") (param anyref) get_local 0 set_global $g)
+    (func (export "get") (result anyref) get_global $g)
+)`).exports;
+
+// 7 => Generational GC zeal.
+gczeal(7, 1);
+
+for (var i = 0; i < 100; i++) {
+    new Baguette(i);
+}
+
+function test(exports) {
+    // Test post-write barrier in wasm code.
+    {
+        let nomnom = new Baguette(15);
+        exports.set(nomnom);
+        nomnom = null;
+    }
+    new Baguette();
+    assertEq(exports.get().calories, 15);
+}
+
+test(exportsPlain);
+test(exportsObj);
+
+// Test stacks reported in profiling mode in a separate way, to not perturb
+// the behavior of the tested functions.
+if (!isSingleStepProfilingEnabled)
+    quit(0);
+
+enableGeckoProfiling();
+
+const EXPECTED_STACKS = [
+    ['', '!>', '0,!>', '<,0,!>', 'GC postbarrier,0,!>', '<,0,!>', '0,!>', '!>', ''],
+    ['', '!>', '0,!>', '!>', ''],
+];
+
+function testStacks(exports) {
+    // Test post-write barrier in wasm code.
+    {
+        let nomnom = new Baguette(15);
+        startProfiling();
+        exports.set(nomnom);
+        assertEqPreciseStacks(endProfiling(), EXPECTED_STACKS);
+        nomnom = null;
+    }
+    new Baguette();
+    assertEq(exports.get().calories, 15);
+}
+
+testStacks(exportsPlain);
+testStacks(exportsObj);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/wasm/gc/anyref-global-prebarrier.js
@@ -0,0 +1,39 @@
+if (!wasmGcEnabled()) {
+    quit(0);
+}
+
+const { startProfiling, endProfiling, assertEqPreciseStacks, isSingleStepProfilingEnabled } = WasmHelpers;
+
+let e = wasmEvalText(`(module
+    (global $g (mut anyref) (ref.null anyref))
+    (func (export "set") (param anyref) get_local 0 set_global $g)
+)`).exports;
+
+let obj = { field: null };
+
+// GCZeal mode 4 implies that prebarriers are being verified at many
+// locations in the interpreter, during interrupt checks, etc. It can be ultra
+// slow, so disable it with gczeal(0) when it's not strictly needed.
+gczeal(4, 1);
+e.set(obj);
+e.set(null);
+gczeal(0);
+
+if (!isSingleStepProfilingEnabled) {
+    quit(0);
+}
+
+enableGeckoProfiling();
+startProfiling();
+gczeal(4, 1);
+e.set(obj);
+gczeal(0);
+assertEqPreciseStacks(endProfiling(), [['', '!>', '0,!>', '!>', '']]);
+
+startProfiling();
+gczeal(4, 1);
+e.set(null);
+gczeal(0);
+
+// We're losing stack info in the prebarrier code.
+assertEqPreciseStacks(endProfiling(), [['', '!>', '0,!>', '', '0,!>', '!>', '']]);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/wasm/gc/anyref-val-tracing.js
@@ -0,0 +1,14 @@
+if (!wasmGcEnabled()) {
+    quit(0);
+}
+
+gczeal(14, 1);
+let { exports } = wasmEvalText(`(module
+    (global $anyref (import "glob" "anyref") anyref)
+    (func (export "get") (result anyref) get_global $anyref)
+)`, {
+    glob: {
+        anyref: { sentinel: "lol" },
+    }
+});
+assertEq(exports.get().sentinel, "lol");
--- a/js/src/jit-test/tests/wasm/gc/anyref.js
+++ b/js/src/jit-test/tests/wasm/gc/anyref.js
@@ -44,16 +44,18 @@ let simpleTests = [
     "(module (func (drop (ref.null anyref))))",
     "(module (func $test (local anyref)))",
     "(module (func $test (param anyref)))",
     "(module (func $test (result anyref) (ref.null anyref)))",
     "(module (func $test (block anyref (unreachable)) unreachable))",
     "(module (func $test (local anyref) (result i32) (ref.is_null (get_local 0))))",
     `(module (import "a" "b" (param anyref)))`,
     `(module (import "a" "b" (result anyref)))`,
+    `(module (global anyref (ref.null anyref)))`,
+    `(module (global (mut anyref) (ref.null anyref)))`,
 ];
 
 for (let src of simpleTests) {
     wasmEvalText(src, {a:{b(){}}});
     assertEq(validate(wasmTextToBinary(src)), true);
 }
 
 // Basic behavioral tests.
@@ -389,8 +391,83 @@ assertEq(exports.count_f(), 1);
 assertEq(exports.count_g(), 1);
 
 x = { i: 23 };
 assertEq(exports.table.get(3)(x), x);
 assertEq(x.i, 24);
 assertEq(x.newProp, "hello");
 assertEq(exports.count_f(), 1);
 assertEq(exports.count_g(), 1);
+
+// Globals.
+
+// Anyref globals in wasm modules.
+
+assertErrorMessage(() => wasmEvalText(`(module (global (import "glob" "anyref") anyref))`, { glob: { anyref: 42 } }),
+    WebAssembly.LinkError,
+    /import object field 'anyref' is not a Object-or-null/);
+
+assertErrorMessage(() => wasmEvalText(`(module (global (import "glob" "anyref") anyref))`, { glob: { anyref: new WebAssembly.Global({ value: 'i32' }, 42) } }),
+    WebAssembly.LinkError,
+    /imported global type mismatch/);
+
+assertErrorMessage(() => wasmEvalText(`(module (global (import "glob" "i32") i32))`, { glob: { i32: {} } }),
+    WebAssembly.LinkError,
+    /import object field 'i32' is not a Number/);
+
+imports = {
+    constants: {
+        imm_null: null,
+        imm_bread: new Baguette(321),
+        mut_null: new WebAssembly.Global({ value: "anyref", mutable: true }, null),
+        mut_bread: new WebAssembly.Global({ value: "anyref", mutable: true }, new Baguette(123))
+    }
+};
+
+exports = wasmEvalText(`(module
+    (global $g_imp_imm_null  (import "constants" "imm_null") anyref)
+    (global $g_imp_imm_bread (import "constants" "imm_bread") anyref)
+
+    (global $g_imp_mut_null   (import "constants" "mut_null") (mut anyref))
+    (global $g_imp_mut_bread  (import "constants" "mut_bread") (mut anyref))
+
+    (global $g_imm_null     anyref (ref.null anyref))
+    (global $g_imm_getglob  anyref (get_global $g_imp_imm_bread))
+    (global $g_mut         (mut anyref) (ref.null anyref))
+
+    (func (export "imm_null")      (result anyref) get_global $g_imm_null)
+    (func (export "imm_getglob")   (result anyref) get_global $g_imm_getglob)
+
+    (func (export "imp_imm_null")  (result anyref) get_global $g_imp_imm_null)
+    (func (export "imp_imm_bread") (result anyref) get_global $g_imp_imm_bread)
+    (func (export "imp_mut_null")  (result anyref) get_global $g_imp_mut_null)
+    (func (export "imp_mut_bread") (result anyref) get_global $g_imp_mut_bread)
+
+    (func (export "set_imp_null")  (param anyref) get_local 0 set_global $g_imp_mut_null)
+    (func (export "set_imp_bread") (param anyref) get_local 0 set_global $g_imp_mut_bread)
+
+    (func (export "set_mut") (param anyref) get_local 0 set_global $g_mut)
+    (func (export "get_mut") (result anyref) get_global $g_mut)
+)`, imports).exports;
+
+assertEq(exports.imp_imm_null(), imports.constants.imm_null);
+assertEq(exports.imp_imm_bread(), imports.constants.imm_bread);
+
+assertEq(exports.imm_null(), null);
+assertEq(exports.imm_getglob(), imports.constants.imm_bread);
+
+assertEq(exports.imp_mut_null(), imports.constants.mut_null.value);
+assertEq(exports.imp_mut_bread(), imports.constants.mut_bread.value);
+
+let brandNewBaguette = new Baguette(1000);
+exports.set_imp_null(brandNewBaguette);
+assertEq(exports.imp_mut_null(), brandNewBaguette);
+assertEq(exports.imp_mut_bread(), imports.constants.mut_bread.value);
+
+exports.set_imp_bread(null);
+assertEq(exports.imp_mut_null(), brandNewBaguette);
+assertEq(exports.imp_mut_bread(), null);
+
+assertEq(exports.get_mut(), null);
+let glutenFreeBaguette = new Baguette("calories-free bread");
+exports.set_mut(glutenFreeBaguette);
+assertEq(exports.get_mut(), glutenFreeBaguette);
+assertEq(exports.get_mut().calories, "calories-free bread");
--- a/js/src/jit-test/tests/wasm/gc/debugger.js
+++ b/js/src/jit-test/tests/wasm/gc/debugger.js
@@ -17,19 +17,24 @@ if (!wasmGcEnabled() || !wasmDebuggingIs
         (func (export "func") (result anyref) (param $ref anyref)
             get_local $ref
         )
       )
     `;
 
     g.eval(`
         var obj = { somekey: 'somevalue' };
-
         Debugger(parent).onEnterFrame = function(frame) {
             let v = frame.environment.getVariable('var0');
-            assertEq(typeof v === 'object', true);
-            assertEq(typeof v.somekey === 'string', true);
-            assertEq(v.somekey === 'somevalue', true);
+            assertEq(typeof v, 'object');
+
+            let prop = v.unwrap().getOwnPropertyDescriptor('somekey');
+            assertEq(typeof prop, 'object');
+            assertEq(typeof prop.value, 'string');
+            assertEq(prop.value, 'somevalue');
+
+            // Disable onEnterFrame hook.
+            Debugger(parent).onEnterFrame = undefined;
         };
+    `);
 
-        new WebAssembly.Instance(new WebAssembly.Module(wasmTextToBinary(\`${src}\`))).exports.func(obj);
-    `);
+    new WebAssembly.Instance(new WebAssembly.Module(wasmTextToBinary(`${src}`))).exports.func(g.obj);
 })();
--- a/js/src/jit/mips32/Assembler-mips32.h
+++ b/js/src/jit/mips32/Assembler-mips32.h
@@ -45,16 +45,17 @@ class ABIArgGenerator
         return usedArgSlots_ * sizeof(intptr_t);
     }
 };
 
 // These registers may be volatile or nonvolatile.
 static constexpr Register ABINonArgReg0 = t0;
 static constexpr Register ABINonArgReg1 = t1;
 static constexpr Register ABINonArgReg2 = t2;
+static constexpr Register ABINonArgReg3 = t3;
 
 // This register may be volatile or nonvolatile. Avoid f18 which is the
 // ScratchDoubleReg.
 static constexpr FloatRegister ABINonArgDoubleReg { FloatRegisters::f16, FloatRegister::Double };
 
 // These registers may be volatile or nonvolatile.
 // Note: these three registers are all guaranteed to be different
 static constexpr Register ABINonArgReturnReg0 = t0;
@@ -68,19 +69,20 @@ static constexpr Register ABINonArgRetur
 
 // TLS pointer argument register for WebAssembly functions. This must not alias
 // any other register used for passing function arguments or return values.
 // Preserved by WebAssembly functions.
 static constexpr Register WasmTlsReg = s5;
 
 // Registers used for asm.js/wasm table calls. These registers must be disjoint
 // from the ABI argument registers, WasmTlsReg and each other.
-static constexpr Register WasmTableCallScratchReg = ABINonArgReg0;
-static constexpr Register WasmTableCallSigReg = ABINonArgReg1;
-static constexpr Register WasmTableCallIndexReg = ABINonArgReg2;
+static constexpr Register WasmTableCallScratchReg0 = ABINonArgReg0;
+static constexpr Register WasmTableCallScratchReg1 = ABINonArgReg1;
+static constexpr Register WasmTableCallSigReg = ABINonArgReg2;
+static constexpr Register WasmTableCallIndexReg = ABINonArgReg3;
 
 static constexpr Register JSReturnReg_Type = a3;
 static constexpr Register JSReturnReg_Data = a2;
 static constexpr Register64 ReturnReg64(v1, v0);
 static constexpr FloatRegister ReturnFloat32Reg = { FloatRegisters::f0, FloatRegister::Single };
 static constexpr FloatRegister ReturnDoubleReg = { FloatRegisters::f0, FloatRegister::Double };
 static constexpr FloatRegister ScratchFloat32Reg = { FloatRegisters::f18, FloatRegister::Single };
 static constexpr FloatRegister ScratchDoubleReg = { FloatRegisters::f18, FloatRegister::Double };
--- a/js/src/jit/mips64/Assembler-mips64.h
+++ b/js/src/jit/mips64/Assembler-mips64.h
@@ -38,16 +38,17 @@ class ABIArgGenerator
         return (usedArgSlots_ - 8) * sizeof(int64_t);
     }
 };
 
 // These registers may be volatile or nonvolatile.
 static constexpr Register ABINonArgReg0 = t0;
 static constexpr Register ABINonArgReg1 = t1;
 static constexpr Register ABINonArgReg2 = t2;
+static constexpr Register ABINonArgReg3 = t3;
 
 // This register may be volatile or nonvolatile. Avoid f23 which is the
 // ScratchDoubleReg.
 static constexpr FloatRegister ABINonArgDoubleReg { FloatRegisters::f21, FloatRegisters::Double };
 
 // These registers may be volatile or nonvolatile.
 // Note: these three registers are all guaranteed to be different
 static constexpr Register ABINonArgReturnReg0 = t0;
@@ -61,19 +62,20 @@ static constexpr Register ABINonArgRetur
 
 // TLS pointer argument register for WebAssembly functions. This must not alias
 // any other register used for passing function arguments or return values.
 // Preserved by WebAssembly functions.
 static constexpr Register WasmTlsReg = s5;
 
 // Registers used for wasm table calls. These registers must be disjoint
 // from the ABI argument registers, WasmTlsReg and each other.
-static constexpr Register WasmTableCallScratchReg = ABINonArgReg0;
-static constexpr Register WasmTableCallSigReg = ABINonArgReg1;
-static constexpr Register WasmTableCallIndexReg = ABINonArgReg2;
+static constexpr Register WasmTableCallScratchReg0 = ABINonArgReg0;
+static constexpr Register WasmTableCallScratchReg1 = ABINonArgReg1;
+static constexpr Register WasmTableCallSigReg = ABINonArgReg2;
+static constexpr Register WasmTableCallIndexReg = ABINonArgReg3;
 
 static constexpr Register JSReturnReg = v1;
 static constexpr Register JSReturnReg_Type = JSReturnReg;
 static constexpr Register JSReturnReg_Data = JSReturnReg;
 static constexpr Register64 ReturnReg64(ReturnReg);
 static constexpr FloatRegister ReturnFloat32Reg = { FloatRegisters::f0, FloatRegisters::Single };
 static constexpr FloatRegister ReturnDoubleReg = { FloatRegisters::f0, FloatRegisters::Double };
 static constexpr FloatRegister ScratchFloat32Reg = { FloatRegisters::f23, FloatRegisters::Single };
--- a/js/src/wasm/AsmJS.cpp
+++ b/js/src/wasm/AsmJS.cpp
@@ -132,18 +132,18 @@ class AsmJSGlobal
   private:
     struct CacheablePod {
         Which which_;
         union V {
             struct {
                 VarInitKind initKind_;
                 union U {
                     ValType importType_;
-                    Val val_;
-                    U() : val_(Val()) {}
+                    LitVal val_;
+                    U() : val_(LitVal()) {}
                 } u;
             } var;
             uint32_t ffiIndex_;
             Scalar::Type viewType_;
             AsmJSMathBuiltinFunction mathBuiltinFunc_;
             AsmJSAtomicsBuiltinFunction atomicsBuiltinFunc_;
             SimdType simdCtorType_;
             struct {
@@ -173,17 +173,17 @@ class AsmJSGlobal
     }
     Which which() const {
         return pod.which_;
     }
     VarInitKind varInitKind() const {
         MOZ_ASSERT(pod.which_ == Variable);
         return pod.u.var.initKind_;
     }
-    Val varInitVal() const {
+    LitVal varInitVal() const {
         MOZ_ASSERT(pod.which_ == Variable);
         MOZ_ASSERT(pod.u.var.initKind_ == InitConstant);
         return pod.u.var.u.val_;
     }
     ValType varInitImportType() const {
         MOZ_ASSERT(pod.which_ == Variable);
         MOZ_ASSERT(pod.u.var.initKind_ == InitImport);
         return pod.u.var.u.importType_;
@@ -818,17 +818,17 @@ ParseVarOrConstStatement(AsmJSParser& pa
 //  negative int: [-2^31, 0)
 //  big unsigned: [2^31, 2^32)
 //  out of range: otherwise
 // Lastly, a literal may be a float literal which is any double or integer
 // literal coerced with Math.fround.
 //
 // This class distinguishes between signed and unsigned integer SIMD types like
 // Int32x4 and Uint32x4, and so does Type below. The wasm ValType and ExprType
-// enums, and the wasm::Val class do not.
+// enums, and the wasm::LitVal class do not.
 class NumLit
 {
   public:
     enum Which {
         Fixnum,
         NegativeInt,
         BigUnsigned,
         Double,
@@ -943,43 +943,43 @@ class NumLit
           case NumLit::Float32x4:
             return simdValue() == SimdConstant::SplatX4(0.f);
           case NumLit::OutOfRangeInt:
             MOZ_CRASH("can't be here because of valid() check above");
         }
         return false;
     }
 
-    Val value() const {
+    LitVal value() const {
         switch (which_) {
           case NumLit::Fixnum:
           case NumLit::NegativeInt:
           case NumLit::BigUnsigned:
-            return Val(toUint32());
+            return LitVal(toUint32());
           case NumLit::Float:
-            return Val(toFloat());
+            return LitVal(toFloat());
           case NumLit::Double:
-            return Val(toDouble());
+            return LitVal(toDouble());
           case NumLit::Int8x16:
           case NumLit::Uint8x16:
-            return Val(simdValue().asInt8x16());
+            return LitVal(simdValue().asInt8x16());
           case NumLit::Int16x8:
           case NumLit::Uint16x8:
-            return Val(simdValue().asInt16x8());
+            return LitVal(simdValue().asInt16x8());
           case NumLit::Int32x4:
           case NumLit::Uint32x4:
-            return Val(simdValue().asInt32x4());
+            return LitVal(simdValue().asInt32x4());
           case NumLit::Float32x4:
-            return Val(simdValue().asFloat32x4());
+            return LitVal(simdValue().asFloat32x4());
           case NumLit::Bool8x16:
-            return Val(simdValue().asInt8x16(), ValType::B8x16);
+            return LitVal(simdValue().asInt8x16(), ValType::B8x16);
           case NumLit::Bool16x8:
-            return Val(simdValue().asInt16x8(), ValType::B16x8);
+            return LitVal(simdValue().asInt16x8(), ValType::B16x8);
           case NumLit::Bool32x4:
-            return Val(simdValue().asInt32x4(), ValType::B32x4);
+            return LitVal(simdValue().asInt32x4(), ValType::B32x4);
           case NumLit::OutOfRangeInt:;
         }
         MOZ_CRASH("bad literal");
     }
 };
 
 // Represents the type of a general asm.js expression.
 //
@@ -2282,17 +2282,17 @@ class MOZ_STACK_CLASS JS_HAZ_ROOTED Modu
         if (table.defined())
             return false;
 
         table.define();
 
         for (uint32_t& index : elems)
             index += funcImportMap_.count();
 
-        return env_.elemSegments.emplaceBack(tableIndex, InitExpr(Val(uint32_t(0))), std::move(elems));
+        return env_.elemSegments.emplaceBack(tableIndex, InitExpr(LitVal(uint32_t(0))), std::move(elems));
     }
     bool declareImport(PropertyName* name, FuncType&& sig, unsigned ffiIndex, uint32_t* importIndex) {
         FuncImportMap::AddPtr p = funcImportMap_.lookupForAdd(NamedSig::Lookup(name, sig));
         if (p) {
             *importIndex = p->value();
             return true;
         }
 
@@ -7666,105 +7666,106 @@ HasPureCoercion(JSContext* cx, HandleVal
     {
         return true;
     }
 
     return false;
 }
 
 static bool
-ValidateGlobalVariable(JSContext* cx, const AsmJSGlobal& global, HandleValue importVal, Val* val)
+ValidateGlobalVariable(JSContext* cx, const AsmJSGlobal& global, HandleValue importVal,
+                       Maybe<LitVal>* val)
 {
     switch (global.varInitKind()) {
       case AsmJSGlobal::InitConstant:
-        *val = global.varInitVal();
+        val->emplace(global.varInitVal());
         return true;
 
       case AsmJSGlobal::InitImport: {
         RootedValue v(cx);
         if (!GetDataProperty(cx, importVal, global.field(), &v))
             return false;
 
         if (!v.isPrimitive() && !HasPureCoercion(cx, v))
             return LinkFail(cx, "Imported values must be primitives");
 
         switch (global.varInitImportType().code()) {
           case ValType::I32: {
             int32_t i32;
             if (!ToInt32(cx, v, &i32))
                 return false;
-            *val = Val(uint32_t(i32));
+            val->emplace(uint32_t(i32));
             return true;
           }
           case ValType::I64:
             MOZ_CRASH("int64");
           case ValType::F32: {
             float f;
             if (!RoundFloat32(cx, v, &f))
                 return false;
-            *val = Val(f);
+            val->emplace(f);
             return true;
           }
           case ValType::F64: {
             double d;
             if (!ToNumber(cx, v, &d))
                 return false;
-            *val = Val(d);
+            val->emplace(d);
             return true;
           }
           case ValType::I8x16: {
             SimdConstant simdConstant;
             if (!ToSimdConstant<Int8x16>(cx, v, &simdConstant))
                 return false;
-            *val = Val(simdConstant.asInt8x16());
+            val->emplace(simdConstant.asInt8x16());
             return true;
           }
           case ValType::I16x8: {
             SimdConstant simdConstant;
             if (!ToSimdConstant<Int16x8>(cx, v, &simdConstant))
                 return false;
-            *val = Val(simdConstant.asInt16x8());
+            val->emplace(simdConstant.asInt16x8());
             return true;
           }
           case ValType::I32x4: {
             SimdConstant simdConstant;
             if (!ToSimdConstant<Int32x4>(cx, v, &simdConstant))
                 return false;
-            *val = Val(simdConstant.asInt32x4());
+            val->emplace(simdConstant.asInt32x4());
             return true;
           }
           case ValType::F32x4: {
             SimdConstant simdConstant;
             if (!ToSimdConstant<Float32x4>(cx, v, &simdConstant))
                 return false;
-            *val = Val(simdConstant.asFloat32x4());
+            val->emplace(simdConstant.asFloat32x4());
             return true;
           }
           case ValType::B8x16: {
             SimdConstant simdConstant;
             if (!ToSimdConstant<Bool8x16>(cx, v, &simdConstant))
                 return false;
             // Bool8x16 uses the same data layout as Int8x16.
-            *val = Val(simdConstant.asInt8x16());
+            val->emplace(simdConstant.asInt8x16());
             return true;
           }
           case ValType::B16x8: {
             SimdConstant simdConstant;
             if (!ToSimdConstant<Bool16x8>(cx, v, &simdConstant))
                 return false;
             // Bool16x8 uses the same data layout as Int16x8.
-            *val = Val(simdConstant.asInt16x8());
+            val->emplace(simdConstant.asInt16x8());
             return true;
           }
           case ValType::B32x4: {
             SimdConstant simdConstant;
             if (!ToSimdConstant<Bool32x4>(cx, v, &simdConstant))
                 return false;
             // Bool32x4 uses the same data layout as Int32x4.
-            *val = Val(simdConstant.asInt32x4());
+            val->emplace(simdConstant.asInt32x4());
             return true;
           }
           case ValType::Ref:
           case ValType::AnyRef: {
             MOZ_CRASH("not available in asm.js");
           }
         }
       }
@@ -8128,29 +8129,30 @@ CheckBuffer(JSContext* cx, const AsmJSMe
     }
 
     MOZ_ASSERT(buffer->isPreparedForAsmJS());
     return true;
 }
 
 static bool
 GetImports(JSContext* cx, const AsmJSMetadata& metadata, HandleValue globalVal,
-           HandleValue importVal, MutableHandle<FunctionVector> funcImports, ValVector* valImports)
+           HandleValue importVal, MutableHandle<FunctionVector> funcImports,
+           MutableHandleValVector valImports)
 {
     Rooted<FunctionVector> ffis(cx, FunctionVector(cx));
     if (!ffis.resize(metadata.numFFIs))
         return false;
 
     for (const AsmJSGlobal& global : metadata.asmJSGlobals) {
         switch (global.which()) {
           case AsmJSGlobal::Variable: {
-            Val val;
-            if (!ValidateGlobalVariable(cx, global, importVal, &val))
+            Maybe<LitVal> litVal;
+            if (!ValidateGlobalVariable(cx, global, importVal, &litVal))
                 return false;
-            if (!valImports->append(val))
+            if (!valImports.append(Val(*litVal)))
                 return false;
             break;
           }
           case AsmJSGlobal::FFI:
             if (!ValidateFFI(cx, global, importVal, &ffis))
                 return false;
             break;
           case AsmJSGlobal::ArrayView:
@@ -8203,25 +8205,26 @@ TryInstantiate(JSContext* cx, CallArgs a
         if (!CheckBuffer(cx, metadata, bufferVal, &buffer))
             return false;
 
         memory = WasmMemoryObject::create(cx, buffer, nullptr);
         if (!memory)
             return false;
     }
 
-    ValVector valImports;
+    RootedValVector valImports(cx);
     Rooted<FunctionVector> funcs(cx, FunctionVector(cx));
     if (!GetImports(cx, metadata, globalVal, importVal, &funcs, &valImports))
         return false;
 
     Rooted<WasmGlobalObjectVector> globalObjs(cx);
 
     RootedWasmTableObject table(cx);
-    if (!module.instantiate(cx, funcs, table, memory, valImports, globalObjs.get(), nullptr, instanceObj))
+    if (!module.instantiate(cx, funcs, table, memory, valImports, globalObjs.get(), nullptr,
+                            instanceObj))
         return false;
 
     exportObj.set(&instanceObj->exportsObj());
     return true;
 }
 
 static bool
 HandleInstantiationFailure(JSContext* cx, CallArgs args, const AsmJSMetadata& metadata)
--- a/js/src/wasm/WasmAST.h
+++ b/js/src/wasm/WasmAST.h
@@ -552,25 +552,25 @@ class AstDrop : public AstExpr
     {}
     AstExpr& value() const {
         return value_;
     }
 };
 
 class AstConst : public AstExpr
 {
-    const Val val_;
+    const LitVal val_;
 
   public:
     static const AstExprKind Kind = AstExprKind::Const;
-    explicit AstConst(Val val)
+    explicit AstConst(LitVal val)
       : AstExpr(Kind, ExprType::Limit),
         val_(val)
     {}
-    Val val() const { return val_; }
+    LitVal val() const { return val_; }
 };
 
 class AstGetLocal : public AstExpr
 {
     AstRef local_;
 
   public:
     static const AstExprKind Kind = AstExprKind::GetLocal;
--- a/js/src/wasm/WasmBaselineCompile.cpp
+++ b/js/src/wasm/WasmBaselineCompile.cpp
@@ -134,16 +134,17 @@
 # include "jit/mips32/Assembler-mips32.h"
 #endif
 #if defined(JS_CODEGEN_MIPS64)
 # include "jit/mips-shared/Assembler-mips-shared.h"
 # include "jit/mips64/Assembler-mips64.h"
 #endif
 
 #include "wasm/WasmGenerator.h"
+#include "wasm/WasmInstance.h"
 #include "wasm/WasmOpIter.h"
 #include "wasm/WasmSignalHandlers.h"
 #include "wasm/WasmValidate.h"
 
 #include "jit/MacroAssembler-inl.h"
 
 using mozilla::DebugOnly;
 using mozilla::FloorLog2;
@@ -4390,17 +4391,16 @@ class BaseCompiler final : public BaseCo
 
     //////////////////////////////////////////////////////////////////////
     //
     // Global variable access.
 
     Address addressOfGlobalVar(const GlobalDesc& global, RegI32 tmp)
     {
         uint32_t globalToTlsOffset = offsetof(TlsData, globalArea) + global.offset();
-
         masm.loadWasmTlsRegFromFrame(tmp);
         if (global.isIndirect()) {
             masm.loadPtr(Address(tmp, globalToTlsOffset), tmp);
             return Address(tmp, 0);
         }
         return Address(tmp, globalToTlsOffset);
     }
 
@@ -5716,16 +5716,91 @@ class BaseCompiler final : public BaseCo
     void branchTo(Assembler::Condition c, RegI64 lhs, RegI64 rhs, Label* l) {
         masm.branch64(c, lhs, rhs, l);
     }
 
     void branchTo(Assembler::Condition c, RegI64 lhs, Imm64 rhs, Label* l) {
         masm.branch64(c, lhs, rhs, l);
     }
 
+#ifdef ENABLE_WASM_GC
+    // The following couple of functions emit a GC pre-write barrier. This is
+    // needed when we replace a member field with a new value, and the previous
+    // field value might have no other referents. The field might belong to an
+    // object or be a stack slot or a register or a heap allocated value.
+    //
+    // let obj = { field: previousValue };
+    // obj.field = newValue; // previousValue must be marked with a pre-barrier.
+    //
+    // Implementing a pre-barrier looks like this:
+    // - call `testNeedPreBarrier` with a fresh label.
+    // - user code must put the address of the field we're about to clobber in
+    // PreBarrierReg (to avoid explicit pushing/popping).
+    // - call `emitPreBarrier`, which binds the label.
+
+    void testNeedPreBarrier(Label* skipBarrier) {
+        MOZ_ASSERT(!skipBarrier->used());
+        MOZ_ASSERT(!skipBarrier->bound());
+
+        // If no incremental GC has started, we don't need the barrier.
+        ScratchPtr scratch(*this);
+        masm.loadWasmTlsRegFromFrame(scratch);
+        masm.loadPtr(Address(scratch, offsetof(TlsData, addressOfNeedsIncrementalBarrier)), scratch);
+        masm.branchTest32(Assembler::Zero, Address(scratch, 0), Imm32(0x1), skipBarrier);
+    }
+
+    void emitPreBarrier(RegPtr valueAddr, Label* skipBarrier) {
+        MOZ_ASSERT(valueAddr == PreBarrierReg);
+
+        // If the previous value is null, we don't need the barrier.
+        ScratchPtr scratch(*this);
+        masm.loadPtr(Address(valueAddr, 0), scratch);
+        masm.branchTestPtr(Assembler::Zero, scratch, scratch, skipBarrier);
+
+        // Call the barrier. This assumes PreBarrierReg contains the address of
+        // the stored value.
+        masm.loadWasmTlsRegFromFrame(scratch);
+        masm.loadPtr(Address(scratch, offsetof(TlsData, instance)), scratch);
+        masm.loadPtr(Address(scratch, Instance::offsetOfPreBarrierCode()), scratch);
+        masm.call(scratch);
+
+        masm.bind(skipBarrier);
+    }
+
+    // This emits a GC post-write barrier. This is needed to ensure that the GC
+    // is aware of slots of tenured things containing references to nursery
+    // values. Pass None for object when the field's owner object is known to
+    // be tenured or heap-allocated.
+
+    void emitPostBarrier(const Maybe<RegPtr>& object, RegPtr setValue, PostBarrierArg arg) {
+        Label skipBarrier;
+
+        // If the set value is null, no barrier.
+        masm.branchTestPtr(Assembler::Zero, setValue, setValue, &skipBarrier);
+
+        RegPtr scratch = needRef();
+        if (object) {
+            // If the object value isn't tenured, no barrier.
+            masm.branchPtrInNurseryChunk(Assembler::Equal, *object, scratch, &skipBarrier);
+        }
+
+        // If the set value is tenured, no barrier.
+        masm.branchPtrInNurseryChunk(Assembler::NotEqual, setValue, scratch, &skipBarrier);
+
+        freeRef(scratch);
+
+        // Need a barrier.
+        uint32_t bytecodeOffset = iter_.lastOpcodeOffset();
+        pushI32(arg.rawPayload());
+        emitInstanceCall(bytecodeOffset, SigPI_, ExprType::Void, SymbolicAddress::PostBarrier);
+
+        masm.bind(&skipBarrier);
+    }
+#endif
+
     // Emit a conditional branch that optionally and optimally cleans up the CPU
     // stack before we branch.
     //
     // Cond is either Assembler::Condition or Assembler::DoubleCondition.
     //
     // Lhs is RegI32, RegI64, or RegF32, or RegF64.
     //
     // Rhs is either the same as Lhs, or an immediate expression compatible with
@@ -8248,30 +8323,33 @@ BaseCompiler::emitGetGlobal()
         return false;
 
     if (deadCode_)
         return true;
 
     const GlobalDesc& global = env_.globals[id];
 
     if (global.isConstant()) {
-        Val value = global.constantValue();
+        LitVal value = global.constantValue();
         switch (value.type().code()) {
           case ValType::I32:
             pushI32(value.i32());
             break;
           case ValType::I64:
             pushI64(value.i64());
             break;
           case ValType::F32:
             pushF32(value.f32());
             break;
           case ValType::F64:
             pushF64(value.f64());
             break;
+          case ValType::AnyRef:
+            pushRef(intptr_t(value.ptr()));
+            break;
           default:
             MOZ_CRASH("Global constant type");
         }
         return true;
     }
 
     switch (global.type().code()) {
       case ValType::I32: {
@@ -8297,16 +8375,23 @@ BaseCompiler::emitGetGlobal()
       }
       case ValType::F64: {
         RegF64 rv = needF64();
         ScratchI32 tmp(*this);
         masm.loadDouble(addressOfGlobalVar(global, tmp), rv);
         pushF64(rv);
         break;
       }
+      case ValType::AnyRef: {
+        RegPtr rv = needRef();
+        ScratchI32 tmp(*this);
+        masm.loadPtr(addressOfGlobalVar(global, tmp), rv);
+        pushRef(rv);
+        break;
+      }
       default:
         MOZ_CRASH("Global variable type");
         break;
     }
     return true;
 }
 
 bool
@@ -8346,16 +8431,43 @@ BaseCompiler::emitSetGlobal()
       }
       case ValType::F64: {
         RegF64 rv = popF64();
         ScratchI32 tmp(*this);
         masm.storeDouble(rv, addressOfGlobalVar(global, tmp));
         freeF64(rv);
         break;
       }
+#ifdef ENABLE_WASM_GC
+      case ValType::AnyRef: {
+        Label skipBarrier;
+        testNeedPreBarrier(&skipBarrier);
+
+        RegPtr valueAddr(PreBarrierReg);
+        needRef(valueAddr);
+        {
+            ScratchI32 tmp(*this);
+            masm.computeEffectiveAddress(addressOfGlobalVar(global, tmp), valueAddr);
+        }
+        emitPreBarrier(valueAddr, &skipBarrier);
+        freeRef(valueAddr);
+
+        RegPtr rv = popRef();
+        {
+            // Actual store.
+            ScratchI32 tmp(*this);
+            masm.storePtr(rv, addressOfGlobalVar(global, tmp));
+        }
+
+        emitPostBarrier(Nothing(), rv, PostBarrierArg::Global(id));
+
+        freeRef(rv);
+        break;
+      }
+#endif
       default:
         MOZ_CRASH("Global variable type");
         break;
     }
     return true;
 }
 
 // Bounds check elimination.
--- a/js/src/wasm/WasmBuiltins.cpp
+++ b/js/src/wasm/WasmBuiltins.cpp
@@ -672,16 +672,20 @@ AddressOf(SymbolicAddress imm, ABIFuncti
         *abiType = Args_General3;
         return FuncCast(Instance::wake, *abiType);
       case SymbolicAddress::MemCopy:
         *abiType = Args_General4;
         return FuncCast(Instance::memCopy, *abiType);
       case SymbolicAddress::MemFill:
         *abiType = Args_General4;
         return FuncCast(Instance::memFill, *abiType);
+      case SymbolicAddress::PostBarrier:
+        *abiType = Args_General2;
+        static_assert(sizeof(PostBarrierArg) == sizeof(uint32_t), "passed arg is a u32");
+        return FuncCast(Instance::postBarrier, *abiType);
 #if defined(JS_CODEGEN_MIPS32)
       case SymbolicAddress::js_jit_gAtomic64Lock:
         return &js::jit::gAtomic64Lock;
 #endif
       case SymbolicAddress::Limit:
         break;
     }
 
@@ -750,16 +754,17 @@ wasm::NeedsBuiltinThunk(SymbolicAddress 
       case SymbolicAddress::CurrentMemory:
       case SymbolicAddress::WaitI32:
       case SymbolicAddress::WaitI64:
       case SymbolicAddress::Wake:
       case SymbolicAddress::CoerceInPlace_JitEntry:
       case SymbolicAddress::ReportInt64JSCall:
       case SymbolicAddress::MemCopy:
       case SymbolicAddress::MemFill:
+      case SymbolicAddress::PostBarrier:
         return true;
       case SymbolicAddress::Limit:
         break;
     }
 
     MOZ_CRASH("unexpected symbolic address");
 }
 
--- a/js/src/wasm/WasmDebug.cpp
+++ b/js/src/wasm/WasmDebug.cpp
@@ -393,17 +393,17 @@ DebugState::debugGetResultType(uint32_t 
 }
 
 bool
 DebugState::getGlobal(Instance& instance, uint32_t globalIndex, MutableHandleValue vp)
 {
     const GlobalDesc& global = metadata().globals[globalIndex];
 
     if (global.isConstant()) {
-        Val value = global.constantValue();
+        LitVal value = global.constantValue();
         switch (value.type().code()) {
           case ValType::I32:
             vp.set(Int32Value(value.i32()));
             break;
           case ValType::I64:
           // Just display as a Number; it's ok if we lose some precision
             vp.set(NumberValue((double)value.i64()));
             break;
--- a/js/src/wasm/WasmFrameIter.cpp
+++ b/js/src/wasm/WasmFrameIter.cpp
@@ -1262,16 +1262,18 @@ ThunkedNativeToDescription(SymbolicAddre
       case SymbolicAddress::CoerceInPlace_JitEntry:
         return "out-of-line coercion for jit entry arguments (in wasm)";
       case SymbolicAddress::ReportInt64JSCall:
         return "jit call to int64 wasm function";
       case SymbolicAddress::MemCopy:
         return "call to native memory.copy function";
       case SymbolicAddress::MemFill:
         return "call to native memory.fill function";
+      case SymbolicAddress::PostBarrier:
+        return "call to native GC postbarrier (in wasm)";
 #if defined(JS_CODEGEN_MIPS32)
       case SymbolicAddress::js_jit_gAtomic64Lock:
         MOZ_CRASH();
 #endif
       case SymbolicAddress::Limit:
         break;
     }
     return "?";
--- a/js/src/wasm/WasmInstance.cpp
+++ b/js/src/wasm/WasmInstance.cpp
@@ -20,16 +20,17 @@
 
 #include "jit/AtomicOperations.h"
 #include "jit/BaselineJIT.h"
 #include "jit/InlinableNatives.h"
 #include "jit/JitCommon.h"
 #include "wasm/WasmBuiltins.h"
 #include "wasm/WasmModule.h"
 
+#include "gc/StoreBuffer-inl.h"
 #include "vm/ArrayBufferObject-inl.h"
 #include "vm/JSObject-inl.h"
 
 using namespace js;
 using namespace js::jit;
 using namespace js::wasm;
 using mozilla::BitwiseCast;
 
@@ -407,25 +408,22 @@ Instance::wake(Instance* instance, uint3
 /* static */ int32_t
 Instance::memCopy(Instance* instance, uint32_t destByteOffset, uint32_t srcByteOffset, uint32_t len)
 {
     WasmMemoryObject* mem = instance->memory();
     uint32_t memLen = mem->volatileMemoryLength();
 
     // Knowing that len > 0 below simplifies the wraparound checks.
     if (len == 0) {
-
         // Even though the length is zero, we must check for a valid offset.
         if (destByteOffset < memLen && srcByteOffset < memLen)
             return 0;
 
         // else fall through to failure case
-
     } else {
-
         ArrayBufferObjectMaybeShared& arrBuf = mem->buffer();
         uint8_t* rawBuf = arrBuf.dataPointerEither().unwrap();
 
         // Here, we know that |len - 1| cannot underflow.
         typedef CheckedInt<uint32_t> CheckedU32;
         CheckedU32 highest_destOffset = CheckedU32(destByteOffset) + CheckedU32(len - 1);
         CheckedU32 highest_srcOffset = CheckedU32(srcByteOffset) + CheckedU32(len - 1);
 
@@ -448,56 +446,74 @@ Instance::memCopy(Instance* instance, ui
 /* static */ int32_t
 Instance::memFill(Instance* instance, uint32_t byteOffset, uint32_t value, uint32_t len)
 {
     WasmMemoryObject* mem = instance->memory();
     uint32_t memLen = mem->volatileMemoryLength();
 
     // Knowing that len > 0 below simplifies the wraparound check.
     if (len == 0) {
-
         // Even though the length is zero, we must check for a valid offset.
         if (byteOffset < memLen)
             return 0;
 
         // else fall through to failure case
-
     } else {
-
         ArrayBufferObjectMaybeShared& arrBuf = mem->buffer();
         uint8_t* rawBuf = arrBuf.dataPointerEither().unwrap();
 
         // Here, we know that |len - 1| cannot underflow.
         typedef CheckedInt<uint32_t> CheckedU32;
         CheckedU32 highest_offset = CheckedU32(byteOffset) + CheckedU32(len - 1);
 
         if (highest_offset.isValid() &&     // wraparound check
             highest_offset.value() < memLen)     // range check
         {
             memset(rawBuf + byteOffset, int(value), size_t(len));
             return 0;
         }
         // else fall through to failure case
-
     }
 
     JSContext* cx = TlsContext.get();
     JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_WASM_OUT_OF_BOUNDS);
     return -1;
 }
 
+/* static */ void
+Instance::postBarrier(Instance* instance, PostBarrierArg arg)
+{
+    gc::Cell** cell = nullptr;
+    switch (arg.type()) {
+      case PostBarrierArg::Type::Global: {
+        const GlobalDesc& global = instance->metadata().globals[arg.globalIndex()];
+        MOZ_ASSERT(!global.isConstant());
+        MOZ_ASSERT(global.type().isRefOrAnyRef());
+        uint8_t* globalAddr = instance->globalData() + global.offset();
+        if (global.isIndirect())
+            globalAddr = *(uint8_t**)globalAddr;
+        MOZ_ASSERT(*(JSObject**)globalAddr, "shouldn't call postbarrier if null");
+        cell = (gc::Cell**) globalAddr;
+        break;
+      }
+    }
+
+    MOZ_ASSERT(cell);
+    TlsContext.get()->runtime()->gc.storeBuffer().putCell(cell);
+}
+
 Instance::Instance(JSContext* cx,
                    Handle<WasmInstanceObject*> object,
                    SharedCode code,
                    UniqueDebugState debug,
                    UniqueTlsData tlsDataIn,
                    HandleWasmMemoryObject memory,
                    SharedTableVector&& tables,
                    Handle<FunctionVector> funcImports,
-                   const ValVector& globalImportValues,
+                   HandleValVector globalImportValues,
                    const WasmGlobalObjectVector& globalObjs)
   : realm_(cx->realm()),
     object_(object),
     code_(code),
     debug_(std::move(debug)),
     tlsData_(std::move(tlsDataIn)),
     memory_(memory),
     tables_(std::move(tables)),
@@ -513,16 +529,20 @@ Instance::Instance(JSContext* cx,
 #ifndef WASM_HUGE_MEMORY
     tlsData()->boundsCheckLimit = memory ? memory->buffer().wasmBoundsCheckLimit() : 0;
 #endif
     tlsData()->instance = this;
     tlsData()->realm = realm_;
     tlsData()->cx = cx;
     tlsData()->resetInterrupt(cx);
     tlsData()->jumpTable = code_->tieringJumpTable();
+#ifdef ENABLE_WASM_GC
+    tlsData()->addressOfNeedsIncrementalBarrier =
+        (uint8_t*)cx->compartment()->zone()->addressOfNeedsIncrementalBarrier();
+#endif
 
     Tier callerTier = code_->bestTier();
 
     for (size_t i = 0; i < metadata(callerTier).funcImports.length(); i++) {
         HandleFunction f = funcImports[i];
         const FuncImport& fi = metadata(callerTier).funcImports[i];
         FuncImportTls& import = funcImportTls(fi);
         if (!isAsmJS() && IsExportedWasmFunction(f)) {
@@ -566,42 +586,43 @@ Instance::Instance(JSContext* cx,
 
         uint8_t* globalAddr = globalData() + global.offset();
         switch (global.kind()) {
           case GlobalKind::Import: {
             size_t imported = global.importIndex();
             if (global.isIndirect())
                 *(void**)globalAddr = globalObjs[imported]->cell();
             else
-                globalImportValues[imported].writePayload(globalAddr);
+                globalImportValues[imported].get().writePayload(globalAddr);
             break;
           }
           case GlobalKind::Variable: {
             const InitExpr& init = global.initExpr();
             switch (init.kind()) {
               case InitExpr::Kind::Constant: {
                 if (global.isIndirect())
                     *(void**)globalAddr = globalObjs[i]->cell();
                 else
-                    init.val().writePayload(globalAddr);
+                    Val(init.val()).writePayload(globalAddr);
                 break;
               }
               case InitExpr::Kind::GetGlobal: {
                 const GlobalDesc& imported = metadata().globals[init.globalIndex()];
 
                 // Global-ref initializers cannot reference mutable globals, so
                 // the source global should never be indirect.
                 MOZ_ASSERT(!imported.isIndirect());
 
+                RootedVal dest(cx, globalImportValues[imported.importIndex()].get());
                 if (global.isIndirect()) {
                     void* address = globalObjs[i]->cell();
                     *(void**)globalAddr = address;
-                    globalImportValues[imported.importIndex()].writePayload((uint8_t*)address);
+                    dest.get().writePayload((uint8_t*)address);
                 } else {
-                    globalImportValues[imported.importIndex()].writePayload(globalAddr);
+                    dest.get().writePayload(globalAddr);
                 }
                 break;
               }
             }
             break;
           }
           case GlobalKind::Constant: {
             MOZ_CRASH("skipped at the top");
@@ -636,16 +657,17 @@ Instance::init(JSContext* cx)
         }
     }
 
     JitRuntime* jitRuntime = cx->runtime()->getJitRuntime(cx);
     if (!jitRuntime)
         return false;
     jsJitArgsRectifier_ = jitRuntime->getArgumentsRectifier();
     jsJitExceptionHandler_ = jitRuntime->getExceptionTail();
+    preBarrierCode_ = jitRuntime->preBarrier(MIRType::Object);
     return true;
 }
 
 Instance::~Instance()
 {
     realm_->wasm.unregisterInstance(*this);
 
     const FuncImportVector& funcImports = metadata(code().stableTier()).funcImports;
@@ -703,16 +725,26 @@ Instance::tracePrivate(JSTracer* trc)
     // OK to just do one tier here; though the tiers have different funcImports
     // tables, they share the tls object.
     for (const FuncImport& fi : metadata(code().stableTier()).funcImports)
         TraceNullableEdge(trc, &funcImportTls(fi).obj, "wasm import");
 
     for (const SharedTable& table : tables_)
         table->trace(trc);
 
+#ifdef ENABLE_WASM_GC
+    for (const GlobalDesc& global : code().metadata().globals) {
+        // Indirect anyref global get traced by the owning WebAssembly.Global.
+        if (global.type() != ValType::AnyRef || global.isConstant() || global.isIndirect())
+            continue;
+        GCPtrObject* obj = (GCPtrObject*)(globalData() + global.offset());
+        TraceNullableEdge(trc, obj, "wasm anyref global");
+    }
+#endif
+
     TraceNullableEdge(trc, &memory_, "wasm buffer");
 }
 
 void
 Instance::trace(JSTracer* trc)
 {
     // Technically, instead of having this method, the caller could use
     // Instance::object() to get the owning WasmInstanceObject to mark,
--- a/js/src/wasm/WasmInstance.h
+++ b/js/src/wasm/WasmInstance.h
@@ -42,16 +42,19 @@ namespace wasm {
 // their code.
 
 class Instance
 {
     JS::Realm* const                realm_;
     ReadBarrieredWasmInstanceObject object_;
     jit::TrampolinePtr              jsJitArgsRectifier_;
     jit::TrampolinePtr              jsJitExceptionHandler_;
+#ifdef ENABLE_WASM_GC
+    jit::TrampolinePtr              preBarrierCode_;
+#endif
     const SharedCode                code_;
     const UniqueDebugState          debug_;
     const UniqueTlsData             tlsData_;
     GCPtrWasmMemoryObject           memory_;
     SharedTableVector               tables_;
     bool                            enterFrameTrapsEnabled_;
 
     // Internal helpers:
@@ -70,17 +73,17 @@ class Instance
     Instance(JSContext* cx,
              HandleWasmInstanceObject object,
              SharedCode code,
              UniqueDebugState debug,
              UniqueTlsData tlsData,
              HandleWasmMemoryObject memory,
              SharedTableVector&& tables,
              Handle<FunctionVector> funcImports,
-             const ValVector& globalImportValues,
+             HandleValVector globalImportValues,
              const WasmGlobalObjectVector& globalObjs);
     ~Instance();
     bool init(JSContext* cx);
     void trace(JSTracer* trc);
 
     JS::Realm* realm() const { return realm_; }
     const Code& code() const { return *code_; }
     const CodeTier& code(Tier t) const { return code_->codeTier(t); }
@@ -103,16 +106,21 @@ class Instance
 #endif
 
     static constexpr size_t offsetOfJSJitArgsRectifier() {
         return offsetof(Instance, jsJitArgsRectifier_);
     }
     static constexpr size_t offsetOfJSJitExceptionHandler() {
         return offsetof(Instance, jsJitExceptionHandler_);
     }
+#ifdef ENABLE_WASM_GC
+    static constexpr size_t offsetOfPreBarrierCode() {
+        return offsetof(Instance, preBarrierCode_);
+    }
+#endif
 
     // This method returns a pointer to the GC object that owns this Instance.
     // Instances may be reached via weak edges (e.g., Compartment::instances_)
     // so this perform a read-barrier on the returned object unless the barrier
     // is explicitly waived.
 
     WasmInstanceObject* object() const;
     WasmInstanceObject* objectUnbarriered() const;
@@ -166,16 +174,17 @@ class Instance
     static int32_t callImport_ref(Instance*, int32_t, int32_t, uint64_t*);
     static uint32_t growMemory_i32(Instance* instance, uint32_t delta);
     static uint32_t currentMemory_i32(Instance* instance);
     static int32_t wait_i32(Instance* instance, uint32_t byteOffset, int32_t value, int64_t timeout);
     static int32_t wait_i64(Instance* instance, uint32_t byteOffset, int64_t value, int64_t timeout);
     static int32_t wake(Instance* instance, uint32_t byteOffset, int32_t count);
     static int32_t memCopy(Instance* instance, uint32_t destByteOffset, uint32_t srcByteOffset, uint32_t len);
     static int32_t memFill(Instance* instance, uint32_t byteOffset, uint32_t value, uint32_t len);
+    static void postBarrier(Instance* instance, PostBarrierArg arg);
 };
 
 typedef UniquePtr<Instance> UniqueInstance;
 
 } // namespace wasm
 } // namespace js
 
 #endif // wasm_instance_h
--- a/js/src/wasm/WasmIonCompile.cpp
+++ b/js/src/wasm/WasmIonCompile.cpp
@@ -2298,17 +2298,17 @@ EmitGetGlobal(FunctionCompiler& f)
 
     const GlobalDesc& global = f.env().globals[id];
     if (!global.isConstant()) {
         f.iter().setResult(f.loadGlobalVar(global.offset(), !global.isMutable(),
                                            global.isIndirect(), ToMIRType(global.type())));
         return true;
     }
 
-    Val value = global.constantValue();
+    LitVal value = global.constantValue();
     MIRType mirType = ToMIRType(value.type());
 
     MDefinition* result;
     switch (value.type().code()) {
       case ValType::I32:
         result = f.constant(Int32Value(value.i32()), mirType);
         break;
       case ValType::I64:
--- a/js/src/wasm/WasmJS.cpp
+++ b/js/src/wasm/WasmJS.cpp
@@ -40,16 +40,26 @@
 #include "wasm/WasmSignalHandlers.h"
 #include "wasm/WasmStubs.h"
 #include "wasm/WasmValidate.h"
 
 #include "vm/ArrayBufferObject-inl.h"
 #include "vm/JSObject-inl.h"
 #include "vm/NativeObject-inl.h"
 
+#define WASM_CRASH_IF_SIMD_TYPES \
+    case ValType::I8x16: \
+    case ValType::B8x16: \
+    case ValType::I16x8: \
+    case ValType::B16x8: \
+    case ValType::I32x4: \
+    case ValType::B32x4: \
+    case ValType::F32x4: \
+      MOZ_CRASH("unexpected SIMD type")
+
 using namespace js;
 using namespace js::jit;
 using namespace js::wasm;
 
 using mozilla::CheckedInt;
 using mozilla::Nothing;
 using mozilla::RangedPtr;
 
@@ -105,59 +115,81 @@ bool
 wasm::HasSupport(JSContext* cx)
 {
     return cx->options().wasm() &&
            HasCompilerSupport(cx) &&
            HasAvailableCompilerTier(cx);
 }
 
 static bool
-ToWebAssemblyValue(JSContext* cx, ValType targetType, HandleValue v, Val* val)
+ToWebAssemblyValue(JSContext* cx, ValType targetType, HandleValue v, MutableHandleVal val)
 {
     switch (targetType.code()) {
       case ValType::I32: {
         int32_t i32;
         if (!ToInt32(cx, v, &i32))
             return false;
-        *val = Val(uint32_t(i32));
+        val.set(Val(uint32_t(i32)));
         return true;
       }
       case ValType::F32: {
         double d;
         if (!ToNumber(cx, v, &d))
             return false;
-        *val = Val(float(d));
+        val.set(Val(float(d)));
         return true;
       }
       case ValType::F64: {
         double d;
         if (!ToNumber(cx, v, &d))
             return false;
-        *val = Val(d);
+        val.set(Val(d));
         return true;
       }
-      default: {
-        MOZ_CRASH("unexpected import value type, caller must guard");
+      case ValType::AnyRef: {
+        if (v.isNull()) {
+            val.set(Val(nullptr));
+        } else {
+            JSObject* obj = ToObject(cx, v);
+            if (!obj)
+                return false;
+            MOZ_ASSERT(obj->compartment() == cx->compartment());
+            val.set(Val(obj));
+        }
+        return true;
+      }
+      WASM_CRASH_IF_SIMD_TYPES;
+      case ValType::Ref:
+      case ValType::I64: {
+        break;
       }
     }
+    MOZ_CRASH("unexpected import value type, caller must guard");
 }
 
 static Value
 ToJSValue(const Val& val)
 {
     switch (val.type().code()) {
       case ValType::I32:
         return Int32Value(val.i32());
       case ValType::F32:
         return DoubleValue(JS::CanonicalizeNaN(double(val.f32())));
       case ValType::F64:
         return DoubleValue(JS::CanonicalizeNaN(val.f64()));
-      default:
-        MOZ_CRASH("unexpected type when translating to a JS value");
+      case ValType::AnyRef:
+        if (!val.ptr())
+            return NullValue();
+        return ObjectValue(*(JSObject*)val.ptr());
+      WASM_CRASH_IF_SIMD_TYPES;
+      case ValType::Ref:
+      case ValType::I64:
+        break;
     }
+    MOZ_CRASH("unexpected type when translating to a JS value");
 }
 
 // ============================================================================
 // Imports
 
 static bool
 ThrowBadImportArg(JSContext* cx)
 {
@@ -186,17 +218,17 @@ GetProperty(JSContext* cx, HandleObject 
 static bool
 GetImports(JSContext* cx,
            const Module& module,
            HandleObject importObj,
            MutableHandle<FunctionVector> funcImports,
            MutableHandleWasmTableObject tableImport,
            MutableHandleWasmMemoryObject memoryImport,
            WasmGlobalObjectVector& globalObjs,
-           ValVector* globalImportValues)
+           MutableHandleValVector globalImportValues)
 {
     const ImportVector& imports = module.imports();
     if (!imports.empty() && !importObj)
         return ThrowBadImportArg(cx);
 
     const Metadata& metadata = module.metadata();
 
     uint32_t globalIndex = 0;
@@ -238,21 +270,21 @@ GetImports(JSContext* cx,
             if (!v.isObject() || !v.toObject().is<WasmMemoryObject>())
                 return ThrowBadImportType(cx, import.field.get(), "Memory");
 
             MOZ_ASSERT(!memoryImport);
             memoryImport.set(&v.toObject().as<WasmMemoryObject>());
             break;
           }
           case DefinitionKind::Global: {
-            Val val;
             const uint32_t index = globalIndex++;
             const GlobalDesc& global = globals[index];
             MOZ_ASSERT(global.importIndex() == index);
 
+            RootedVal val(cx);
             if (v.isObject() && v.toObject().is<WasmGlobalObject>()) {
                 RootedWasmGlobalObject obj(cx, &v.toObject().as<WasmGlobalObject>());
 
                 if (obj->isMutable() != global.isMutable()) {
                     JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_MUT_LINK);
                     return false;
                 }
                 if (obj->type() != global.type()) {
@@ -260,36 +292,42 @@ GetImports(JSContext* cx,
                     return false;
                 }
 
                 if (globalObjs.length() <= index && !globalObjs.resize(index + 1)) {
                     ReportOutOfMemory(cx);
                     return false;
                 }
                 globalObjs[index] = obj;
-                val = obj->val();
-            } else
-            if (v.isNumber()) {
+                obj->val(&val);
+            } else {
+                if (IsNumberType(global.type())) {
+                    if (!v.isNumber())
+                        return ThrowBadImportType(cx, import.field.get(), "Number");
+                } else {
+                    MOZ_ASSERT(global.type().isRefOrAnyRef());
+                    if (!v.isNull() && !v.isObject())
+                        return ThrowBadImportType(cx, import.field.get(), "Object-or-null");
+                }
+
                 if (global.type() == ValType::I64) {
                     JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_I64_LINK);
                     return false;
                 }
 
                 if (global.isMutable()) {
                     JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_MUT_LINK);
                     return false;
                 }
 
                 if (!ToWebAssemblyValue(cx, global.type(), v, &val))
                     return false;
-            } else {
-                return ThrowBadImportType(cx, import.field.get(), "Number");
             }
 
-            if (!globalImportValues->append(val))
+            if (!globalImportValues.append(val))
                 return false;
 
             break;
           }
         }
     }
 
     MOZ_ASSERT(globalIndex == globals.length() || !globals[globalIndex].isImport());
@@ -354,21 +392,22 @@ wasm::Eval(JSContext* cx, Handle<TypedAr
         return false;
     }
 
     Rooted<FunctionVector> funcs(cx, FunctionVector(cx));
     RootedWasmTableObject table(cx);
     RootedWasmMemoryObject memory(cx);
     Rooted<WasmGlobalObjectVector> globalObjs(cx);
 
-    ValVector globals;
+    RootedValVector globals(cx);
     if (!GetImports(cx, *module, importObj, &funcs, &table, &memory, globalObjs.get(), &globals))
         return false;
 
-    return module->instantiate(cx, funcs, table, memory, globals, globalObjs.get(), nullptr, instanceObj);
+    return module->instantiate(cx, funcs, table, memory, globals, globalObjs.get(), nullptr,
+                               instanceObj);
 }
 
 // ============================================================================
 // Common functions
 
 // '[EnforceRange] unsigned long' types are coerced with
 //    ConvertToInt(v, 32, 'unsigned')
 // defined in Web IDL Section 3.2.4.9.
@@ -1044,17 +1083,17 @@ WasmInstanceObject::trace(JSTracer* trc,
 WasmInstanceObject::create(JSContext* cx,
                            SharedCode code,
                            UniqueDebugState debug,
                            UniqueTlsData tlsData,
                            HandleWasmMemoryObject memory,
                            SharedTableVector&& tables,
                            Handle<FunctionVector> funcImports,
                            const GlobalDescVector& globals,
-                           const ValVector& globalImportValues,
+                           HandleValVector globalImportValues,
                            const WasmGlobalObjectVector& globalObjs,
                            HandleObject proto)
 {
     UniquePtr<ExportMap> exports = js::MakeUnique<ExportMap>();
     if (!exports || !exports->init()) {
         ReportOutOfMemory(cx);
         return nullptr;
     }
@@ -1151,21 +1190,22 @@ Instantiate(JSContext* cx, const Module&
 {
     RootedObject instanceProto(cx, &cx->global()->getPrototype(JSProto_WasmInstance).toObject());
 
     Rooted<FunctionVector> funcs(cx, FunctionVector(cx));
     RootedWasmTableObject table(cx);
     RootedWasmMemoryObject memory(cx);
     Rooted<WasmGlobalObjectVector> globalObjs(cx);
 
-    ValVector globals;
+    RootedValVector globals(cx);
     if (!GetImports(cx, module, importObj, &funcs, &table, &memory, globalObjs.get(), &globals))
         return false;
 
-    return module.instantiate(cx, funcs, table, memory, globals, globalObjs.get(), instanceProto, instanceObj);
+    return module.instantiate(cx, funcs, table, memory, globals, globalObjs.get(), instanceProto,
+                              instanceObj);
 }
 
 /* static */ bool
 WasmInstanceObject::construct(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     if (!ThrowIfNotConstructing(cx, args, "Instance"))
@@ -2097,59 +2137,101 @@ const ClassOps WasmGlobalObject::classOp
     nullptr, /* enumerate */
     nullptr, /* newEnumerate */
     nullptr, /* resolve */
     nullptr, /* mayResolve */
     WasmGlobalObject::finalize,
     nullptr, /* call */
     nullptr, /* hasInstance */
     nullptr, /* construct */
-    nullptr  /* trace */
+    WasmGlobalObject::trace
 };
 
 const Class WasmGlobalObject::class_ =
 {
     "WebAssembly.Global",
     JSCLASS_HAS_RESERVED_SLOTS(WasmGlobalObject::RESERVED_SLOTS) |
     JSCLASS_BACKGROUND_FINALIZE,
     &WasmGlobalObject::classOps_
 };
 
 /* static */ void
+WasmGlobalObject::trace(JSTracer* trc, JSObject* obj)
+{
+    WasmGlobalObject* global = reinterpret_cast<WasmGlobalObject*>(obj);
+    switch (global->type().code()) {
+      case ValType::AnyRef:
+        if (global->cell()->ptr)
+            TraceManuallyBarrieredEdge(trc, &global->cell()->ptr, "wasm anyref global");
+        break;
+      case ValType::I32:
+      case ValType::F32:
+      case ValType::I64:
+      case ValType::F64:
+        break;
+      WASM_CRASH_IF_SIMD_TYPES;
+      case ValType::Ref:
+        MOZ_CRASH("Ref NYI");
+    }
+}
+
+/* static */ void
 WasmGlobalObject::finalize(FreeOp*, JSObject* obj)
 {
     WasmGlobalObject* global = reinterpret_cast<WasmGlobalObject*>(obj);
     js_delete(global->cell());
 }
 
 /* static */ WasmGlobalObject*
-WasmGlobalObject::create(JSContext* cx, const Val& val, bool isMutable)
+WasmGlobalObject::create(JSContext* cx, HandleVal hval, bool isMutable)
 {
-    UniquePtr<Cell> cell = js::MakeUnique<Cell>();
-    if (!cell)
-        return nullptr;
-
-    switch (val.type().code()) {
-      case ValType::I32: cell->i32 = val.i32(); break;
-      case ValType::I64: cell->i64 = val.i64(); break;
-      case ValType::F32: cell->f32 = val.f32(); break;
-      case ValType::F64: cell->f64 = val.f64(); break;
-      default:           MOZ_CRASH();
-    }
-
     RootedObject proto(cx, &cx->global()->getPrototype(JSProto_WasmGlobal).toObject());
 
     AutoSetNewObjectMetadata metadata(cx);
     RootedWasmGlobalObject obj(cx, NewObjectWithGivenProto<WasmGlobalObject>(cx, proto));
     if (!obj)
         return nullptr;
 
+    MOZ_ASSERT(obj->isTenured(), "assumed by set_global post barriers");
+
+    // It's simpler to initialize the cell after the object has been created,
+    // to avoid needing to root the cell before the object creation.
+
+    Cell* cell = js_new<Cell>();
+    if (!cell)
+        return nullptr;
+
+    const Val& val = hval.get();
+    switch (val.type().code()) {
+      case ValType::I32:
+        cell->i32 = val.i32();
+        break;
+      case ValType::I64:
+        cell->i64 = val.i64();
+        break;
+      case ValType::F32:
+        cell->f32 = val.f32();
+        break;
+      case ValType::F64:
+        cell->f64 = val.f64();
+        break;
+      case ValType::AnyRef:
+        MOZ_ASSERT(!cell->ptr, "no prebarriers needed");
+        cell->ptr = val.ptr();
+        if (cell->ptr)
+            JSObject::writeBarrierPost(&cell->ptr, nullptr, cell->ptr);
+        break;
+      WASM_CRASH_IF_SIMD_TYPES;
+      case ValType::Ref:
+        MOZ_CRASH("Ref NYI");
+    }
+
     obj->initReservedSlot(TYPE_SLOT, Int32Value(int32_t(val.type().bitsUnsafe())));
     obj->initReservedSlot(MUTABLE_SLOT, JS::BooleanValue(isMutable));
-    obj->initReservedSlot(CELL_SLOT, PrivateValue(cell.release()));
+    obj->initReservedSlot(CELL_SLOT, PrivateValue(cell));
 
     return obj;
 }
 
 /* static */ bool
 WasmGlobalObject::construct(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
@@ -2185,42 +2267,46 @@ WasmGlobalObject::construct(JSContext* c
     } else if (args.length() == 1 && StringEqualsAscii(typeLinearStr, "i64")) {
         // For the time being, i64 is allowed only if there is not an
         // initializing value.
         globalType = ValType::I64;
     } else if (StringEqualsAscii(typeLinearStr, "f32")) {
         globalType = ValType::F32;
     } else if (StringEqualsAscii(typeLinearStr, "f64")) {
         globalType = ValType::F64;
+#ifdef ENABLE_WASM_GC
+    } else if (cx->options().wasmGc() && StringEqualsAscii(typeLinearStr, "anyref")) {
+        globalType = ValType::AnyRef;
+#endif
     } else {
         JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_GLOBAL_TYPE);
         return false;
     }
 
     RootedValue mutableVal(cx);
     if (!JS_GetProperty(cx, obj, "mutable", &mutableVal))
         return false;
 
     bool isMutable = ToBoolean(mutableVal);
 
     // Extract the initial value, or provide a suitable default.
-    // Guard against control flow mistakes below failing to set |globalVal|.
-    Val globalVal = Val(uint32_t(0));
+    RootedVal globalVal(cx);
     if (args.length() >= 2) {
         RootedValue valueVal(cx, args.get(1));
-
         if (!ToWebAssemblyValue(cx, globalType, valueVal, &globalVal))
             return false;
     } else {
         switch (globalType.code()) {
-          case ValType::I32: /* set above */               break;
-          case ValType::I64: globalVal = Val(uint64_t(0)); break;
-          case ValType::F32: globalVal = Val(float(0.0));  break;
-          case ValType::F64: globalVal = Val(double(0.0)); break;
-          default: MOZ_CRASH();
+          case ValType::I32:    globalVal = Val(uint32_t(0)); break;
+          case ValType::I64:    globalVal = Val(uint64_t(0)); break;
+          case ValType::F32:    globalVal = Val(float(0.0));  break;
+          case ValType::F64:    globalVal = Val(double(0.0)); break;
+          case ValType::AnyRef: globalVal = Val(nullptr);     break;
+          WASM_CRASH_IF_SIMD_TYPES;
+          case ValType::Ref:    MOZ_CRASH("Ref NYI");
         }
     }
 
     WasmGlobalObject* global = WasmGlobalObject::create(cx, globalVal, isMutable);
     if (!global)
         return false;
 
     args.rval().setObject(*global);
@@ -2235,24 +2321,27 @@ IsGlobal(HandleValue v)
 
 /* static */ bool
 WasmGlobalObject::valueGetterImpl(JSContext* cx, const CallArgs& args)
 {
     switch (args.thisv().toObject().as<WasmGlobalObject>().type().code()) {
       case ValType::I32:
       case ValType::F32:
       case ValType::F64:
-        args.rval().set(args.thisv().toObject().as<WasmGlobalObject>().value());
+      case ValType::AnyRef:
+        args.rval().set(args.thisv().toObject().as<WasmGlobalObject>().value(cx));
         return true;
       case ValType::I64:
         JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_I64_TYPE);
         return false;
-      default:
-        MOZ_CRASH();
+      WASM_CRASH_IF_SIMD_TYPES;
+      case ValType::Ref:
+        MOZ_CRASH("Ref NYI");
     }
+    MOZ_CRASH();
 }
 
 /* static */ bool
 WasmGlobalObject::valueGetter(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     return CallNonGenericMethod<IsGlobal, valueGetterImpl>(cx, args);
 }
@@ -2266,26 +2355,44 @@ WasmGlobalObject::valueSetterImpl(JSCont
         return false;
     }
 
     if (global->type() == ValType::I64) {
         JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_I64_TYPE);
         return false;
     }
 
-    Val val;
+    RootedVal val(cx);
     if (!ToWebAssemblyValue(cx, global->type(), args.get(0), &val))
         return false;
 
     Cell* cell = global->cell();
     switch (global->type().code()) {
-      case ValType::I32: cell->i32 = val.i32(); break;
-      case ValType::F32: cell->f32 = val.f32(); break;
-      case ValType::F64: cell->f64 = val.f64(); break;
-      default:           MOZ_CRASH();
+      case ValType::I32:
+        cell->i32 = val.get().i32();
+        break;
+      case ValType::F32:
+        cell->f32 = val.get().f32();
+        break;
+      case ValType::F64:
+        cell->f64 = val.get().f64();
+        break;
+      case ValType::AnyRef: {
+        JSObject* prevPtr = cell->ptr;
+        JSObject::writeBarrierPre(prevPtr);
+        cell->ptr = val.get().ptr();
+        if (cell->ptr)
+            JSObject::writeBarrierPost(&cell->ptr, prevPtr, cell->ptr);
+        break;
+      }
+      WASM_CRASH_IF_SIMD_TYPES;
+      case ValType::I64:
+        MOZ_CRASH("unexpected i64 when setting global's value");
+      case ValType::Ref:
+        MOZ_CRASH("Ref NYI");
     }
 
     args.rval().setUndefined();
     return true;
 }
 
 /* static */ bool
 WasmGlobalObject::valueSetter(JSContext* cx, unsigned argc, Value* vp)
@@ -2317,36 +2424,39 @@ WasmGlobalObject::type() const
 }
 
 bool
 WasmGlobalObject::isMutable() const
 {
     return getReservedSlot(MUTABLE_SLOT).toBoolean();
 }
 
-Val
-WasmGlobalObject::val() const
+void
+WasmGlobalObject::val(MutableHandleVal outval) const
 {
     Cell* cell = this->cell();
-    Val val;
     switch (type().code()) {
-      case ValType::I32: val = Val(uint32_t(cell->i32)); break;
-      case ValType::I64: val = Val(uint64_t(cell->i64)); break;
-      case ValType::F32: val = Val(cell->f32); break;
-      case ValType::F64: val = Val(cell->f64); break;
-      default:           MOZ_CRASH();
+      case ValType::I32:    outval.set(Val(uint32_t(cell->i32))); return;
+      case ValType::I64:    outval.set(Val(uint64_t(cell->i64))); return;
+      case ValType::F32:    outval.set(Val(cell->f32));           return;
+      case ValType::F64:    outval.set(Val(cell->f64));           return;
+      case ValType::AnyRef: outval.set(Val(cell->ptr));           return;
+      WASM_CRASH_IF_SIMD_TYPES;
+      case ValType::Ref:    MOZ_CRASH("Ref NYI");
     }
-    return val;
+    MOZ_CRASH("unexpected Global type");
 }
 
 Value
-WasmGlobalObject::value() const
+WasmGlobalObject::value(JSContext* cx) const
 {
     // ToJSValue crashes on I64; this is desirable.
-    return ToJSValue(val());
+    RootedVal result(cx);
+    val(&result);
+    return ToJSValue(result.get());
 }
 
 WasmGlobalObject::Cell*
 WasmGlobalObject::cell() const
 {
     return reinterpret_cast<Cell*>(getReservedSlot(CELL_SLOT).toPrivate());
 }
 
--- a/js/src/wasm/WasmJS.h
+++ b/js/src/wasm/WasmJS.h
@@ -121,46 +121,50 @@ class WasmModuleObject : public NativeOb
 class WasmGlobalObject : public NativeObject
 {
     static const unsigned TYPE_SLOT = 0;
     static const unsigned MUTABLE_SLOT = 1;
     static const unsigned CELL_SLOT = 2;
 
     static const ClassOps classOps_;
     static void finalize(FreeOp*, JSObject* obj);
+    static void trace(JSTracer* trc, JSObject* obj);
 
     static bool valueGetterImpl(JSContext* cx, const CallArgs& args);
     static bool valueGetter(JSContext* cx, unsigned argc, Value* vp);
     static bool valueSetterImpl(JSContext* cx, const CallArgs& args);
     static bool valueSetter(JSContext* cx, unsigned argc, Value* vp);
 
   public:
     // For exposed globals the Cell holds the value of the global; the
     // instance's global area holds a pointer to the Cell.
     union Cell {
-        int32_t i32;
-        int64_t i64;
-        float   f32;
-        double  f64;
+        int32_t   i32;
+        int64_t   i64;
+        float     f32;
+        double    f64;
+        JSObject* ptr;
+        Cell() : i64(0) {}
+        ~Cell() {}
     };
 
     static const unsigned RESERVED_SLOTS = 3;
     static const Class class_;
     static const JSPropertySpec properties[];
     static const JSFunctionSpec methods[];
     static const JSFunctionSpec static_methods[];
     static bool construct(JSContext*, unsigned, Value*);
 
-    static WasmGlobalObject* create(JSContext* cx, const wasm::Val& value, bool isMutable);
+    static WasmGlobalObject* create(JSContext* cx, wasm::HandleVal value, bool isMutable);
 
     wasm::ValType type() const;
-    wasm::Val val() const;
+    void val(wasm::MutableHandleVal outval) const;
     bool isMutable() const;
     // value() will MOZ_CRASH if the type is int64
-    Value value() const;
+    Value value(JSContext* cx) const;
     Cell* cell() const;
 };
 
 // The class of WebAssembly.Instance. Each WasmInstanceObject owns a
 // wasm::Instance. These objects are used both as content-facing JS objects and
 // as internal implementation details of asm.js.
 
 class WasmInstanceObject : public NativeObject
@@ -209,17 +213,17 @@ class WasmInstanceObject : public Native
     static WasmInstanceObject* create(JSContext* cx,
                                       RefPtr<const wasm::Code> code,
                                       UniquePtr<wasm::DebugState> debug,
                                       wasm::UniqueTlsData tlsData,
                                       HandleWasmMemoryObject memory,
                                       Vector<RefPtr<wasm::Table>, 0, SystemAllocPolicy>&& tables,
                                       Handle<FunctionVector> funcImports,
                                       const wasm::GlobalDescVector& globals,
-                                      const wasm::ValVector& globalImportValues,
+                                      wasm::HandleValVector globalImportValues,
                                       const WasmGlobalObjectVector& globalObjs,
                                       HandleObject proto);
     void initExportsObj(JSObject& exportsObj);
 
     wasm::Instance& instance() const;
     JSObject& exportsObj() const;
 
     static bool getExportedFunction(JSContext* cx,
--- a/js/src/wasm/WasmModule.cpp
+++ b/js/src/wasm/WasmModule.cpp
@@ -716,34 +716,34 @@ Module::extractCode(JSContext* cx, Tier 
     if (!JS_DefineProperty(cx, result, "segments", value, JSPROP_ENUMERATE))
         return false;
 
     vp.setObject(*result);
     return true;
 }
 
 static uint32_t
-EvaluateInitExpr(const ValVector& globalImportValues, InitExpr initExpr)
+EvaluateInitExpr(HandleValVector globalImportValues, InitExpr initExpr)
 {
     switch (initExpr.kind()) {
       case InitExpr::Kind::Constant:
         return initExpr.val().i32();
       case InitExpr::Kind::GetGlobal:
-        return globalImportValues[initExpr.globalIndex()].i32();
+        return globalImportValues[initExpr.globalIndex()].get().i32();
     }
 
     MOZ_CRASH("bad initializer expression");
 }
 
 bool
 Module::initSegments(JSContext* cx,
                      HandleWasmInstanceObject instanceObj,
                      Handle<FunctionVector> funcImports,
                      HandleWasmMemoryObject memoryObj,
-                     const ValVector& globalImportValues) const
+                     HandleValVector globalImportValues) const
 {
     Instance& instance = instanceObj->instance();
     const SharedTableVector& tables = instance.tables();
 
     Tier tier = code().bestTier();
 
     // Perform all error checks up front so that this function does not perform
     // partial initialization if an error is reported.
@@ -1005,63 +1005,69 @@ Module::instantiateTable(JSContext* cx, 
                 return false;
             }
         }
     }
 
     return true;
 }
 
-static Val
-ExtractGlobalValue(const ValVector& globalImportValues, uint32_t globalIndex, const GlobalDesc& global)
+static void
+ExtractGlobalValue(HandleValVector globalImportValues, uint32_t globalIndex,
+                   const GlobalDesc& global, MutableHandleVal result)
 {
     switch (global.kind()) {
       case GlobalKind::Import: {
-        return globalImportValues[globalIndex];
+        result.set(Val(globalImportValues[globalIndex]));
+        return;
       }
       case GlobalKind::Variable: {
         const InitExpr& init = global.initExpr();
         switch (init.kind()) {
           case InitExpr::Kind::Constant:
-            return init.val();
+            result.set(Val(init.val()));
+            return;
           case InitExpr::Kind::GetGlobal:
-            return globalImportValues[init.globalIndex()];
+            result.set(Val(globalImportValues[init.globalIndex()]));
+            return;
         }
         break;
       }
       case GlobalKind::Constant: {
-        return global.constantValue();
+        result.set(Val(global.constantValue()));
+        return;
       }
     }
     MOZ_CRASH("Not a global value");
 }
 
 static bool
-EnsureGlobalObject(JSContext* cx, const ValVector& globalImportValues, size_t globalIndex,
+EnsureGlobalObject(JSContext* cx, HandleValVector globalImportValues, size_t globalIndex,
                    const GlobalDesc& global, WasmGlobalObjectVector& globalObjs)
 {
     if (globalIndex < globalObjs.length() && globalObjs[globalIndex])
         return true;
 
-    Val val = ExtractGlobalValue(globalImportValues, globalIndex, global);
+    RootedVal val(cx);
+    ExtractGlobalValue(globalImportValues, globalIndex, global, &val);
     RootedWasmGlobalObject go(cx, WasmGlobalObject::create(cx, val, global.isMutable()));
     if (!go)
         return false;
 
     if (globalObjs.length() <= globalIndex && !globalObjs.resize(globalIndex + 1)) {
         ReportOutOfMemory(cx);
         return false;
     }
 
     globalObjs[globalIndex] = go;
     return true;
 }
 
 bool
-Module::instantiateGlobals(JSContext* cx, const ValVector& globalImportValues,
+Module::instantiateGlobals(JSContext* cx, HandleValVector globalImportValues,
                            WasmGlobalObjectVector& globalObjs) const
 {
     // If there are exported globals that aren't in globalObjs because they
     // originate in this module or because they were immutable imports that came
     // in as primitive values then we must create cells in the globalObjs for
     // them here, as WasmInstanceObject::create() and CreateExportObject() will
     // need the cells to exist.
 
@@ -1183,17 +1189,17 @@ CreateExportObject(JSContext* cx,
     return true;
 }
 
 bool
 Module::instantiate(JSContext* cx,
                     Handle<FunctionVector> funcImports,
                     HandleWasmTableObject tableImport,
                     HandleWasmMemoryObject memoryImport,
-                    const ValVector& globalImportValues,
+                    HandleValVector globalImportValues,
                     WasmGlobalObjectVector& globalObjs,
                     HandleObject instanceProto,
                     MutableHandleWasmInstanceObject instance) const
 {
     if (!instantiateFunctions(cx, funcImports))
         return false;
 
     RootedWasmMemoryObject memory(cx, memoryImport);
--- a/js/src/wasm/WasmModule.h
+++ b/js/src/wasm/WasmModule.h
@@ -146,23 +146,23 @@ class Module : public JS::WasmModule
 
     mutable Atomic<bool>    codeIsBusy_;
 
     bool instantiateFunctions(JSContext* cx, Handle<FunctionVector> funcImports) const;
     bool instantiateMemory(JSContext* cx, MutableHandleWasmMemoryObject memory) const;
     bool instantiateTable(JSContext* cx,
                           MutableHandleWasmTableObject table,
                           SharedTableVector* tables) const;
-    bool instantiateGlobals(JSContext* cx, const ValVector& globalImportValues,
+    bool instantiateGlobals(JSContext* cx, HandleValVector globalImportValues,
                             WasmGlobalObjectVector& globalObjs) const;
     bool initSegments(JSContext* cx,
                       HandleWasmInstanceObject instance,
                       Handle<FunctionVector> funcImports,
                       HandleWasmMemoryObject memory,
-                      const ValVector& globalImportValues) const;
+                      HandleValVector globalImportValues) const;
 
     class Tier2GeneratorTaskImpl;
     void notifyCompilationListeners();
 
   public:
     Module(Assumptions&& assumptions,
            const Code& code,
            UniqueConstBytes unlinkedCodeForDebugging,
@@ -202,17 +202,17 @@ class Module : public JS::WasmModule
     uint32_t codeLength(Tier t) const { return code_->segment(t).length(); }
 
     // Instantiate this module with the given imports:
 
     bool instantiate(JSContext* cx,
                      Handle<FunctionVector> funcImports,
                      HandleWasmTableObject tableImport,
                      HandleWasmMemoryObject memoryImport,
-                     const ValVector& globalImportValues,
+                     HandleValVector globalImportValues,
                      WasmGlobalObjectVector& globalObjs,
                      HandleObject instanceProto,
                      MutableHandleWasmInstanceObject instanceObj) const;
 
     // Tier-2 compilation may be initiated after the Module is constructed at
     // most once, ideally before any client can attempt to serialize the Module.
     // When tier-2 compilation completes, ModuleGenerator calls finishTier2()
     // from a helper thread, passing tier-variant data which will be installed
--- a/js/src/wasm/WasmTextToBinary.cpp
+++ b/js/src/wasm/WasmTextToBinary.cpp
@@ -2110,17 +2110,17 @@ ParseNaNLiteral(WasmParseContext& c, Was
         // Produce the spec's default NaN.
         value = (Traits::kSignificandBits + 1) >> 1;
     }
 
     value = (isNegated ? Traits::kSignBit : 0) | Traits::kExponentBits | value;
 
     Float flt;
     BitwiseCast(value, &flt);
-    return new (c.lifo) AstConst(Val(flt));
+    return new (c.lifo) AstConst(LitVal(flt));
 
   error:
     c.ts.generateError(token, c.error);
     return nullptr;
 }
 
 template <typename Float>
 static bool
@@ -2262,17 +2262,17 @@ ParseFloatLiteral(WasmParseContext& c, W
       case WasmToken::UnsignedInteger: result = token.uint(); break;
       case WasmToken::SignedInteger:   result = token.sint(); break;
       case WasmToken::NegativeZero:    result = -0.; break;
       case WasmToken::Float:           break;
       default:                         c.ts.generateError(token, c.error); return nullptr;
     }
 
     if (token.kind() != WasmToken::Float)
-        return new (c.lifo) AstConst(Val(Float(result)));
+        return new (c.lifo) AstConst(LitVal(Float(result)));
 
     const char16_t* begin = token.begin();
     const char16_t* end = token.end();
     const char16_t* cur = begin;
 
     bool isNegated = false;
     if (*cur == '-' || *cur == '+')
         isNegated = *cur++ == '-';
@@ -2312,51 +2312,51 @@ ParseFloatLiteral(WasmParseContext& c, W
         c.lifo.release(mark);
         break;
       }
     }
 
     if (isNegated)
         result = -result;
 
-    return new (c.lifo) AstConst(Val(Float(result)));
+    return new (c.lifo) AstConst(LitVal(Float(result)));
 }
 
 static AstConst*
 ParseConst(WasmParseContext& c, WasmToken constToken)
 {
     WasmToken val = c.ts.get();
     switch (constToken.valueType().code()) {
       case ValType::I32: {
         switch (val.kind()) {
           case WasmToken::Index:
-            return new(c.lifo) AstConst(Val(val.index()));
+            return new(c.lifo) AstConst(LitVal(val.index()));
           case WasmToken::SignedInteger: {
             CheckedInt<int32_t> sint = val.sint();
             if (!sint.isValid())
                 break;
-            return new(c.lifo) AstConst(Val(uint32_t(sint.value())));
+            return new(c.lifo) AstConst(LitVal(uint32_t(sint.value())));
           }
           case WasmToken::NegativeZero:
-            return new(c.lifo) AstConst(Val(uint32_t(0)));
+            return new(c.lifo) AstConst(LitVal(uint32_t(0)));
           default:
             break;
         }
         break;
       }
       case ValType::I64: {
         switch (val.kind()) {
           case WasmToken::Index:
-            return new(c.lifo) AstConst(Val(uint64_t(val.index())));
+            return new(c.lifo) AstConst(LitVal(uint64_t(val.index())));
           case WasmToken::UnsignedInteger:
-            return new(c.lifo) AstConst(Val(val.uint()));
+            return new(c.lifo) AstConst(LitVal(val.uint()));
           case WasmToken::SignedInteger:
-            return new(c.lifo) AstConst(Val(uint64_t(val.sint())));
+            return new(c.lifo) AstConst(LitVal(uint64_t(val.sint())));
           case WasmToken::NegativeZero:
-            return new(c.lifo) AstConst(Val(uint64_t(0)));
+            return new(c.lifo) AstConst(LitVal(uint64_t(0)));
           default:
             break;
         }
         break;
       }
       case ValType::F32: {
         return ParseFloatLiteral<float>(c, val);
       }
@@ -3589,17 +3589,17 @@ ParseMemory(WasmParseContext& c, AstModu
         size_t totalLength = 0;
         while (c.ts.getIf(WasmToken::Text, &data)) {
             if (!fragments.append(data.text()))
                 return false;
             totalLength += data.text().length();
         }
 
         if (fragments.length()) {
-            AstExpr* offset = new(c.lifo) AstConst(Val(uint32_t(0)));
+            AstExpr* offset = new(c.lifo) AstConst(LitVal(uint32_t(0)));
             if (!offset)
                 return false;
 
             AstDataSegment* segment = new(c.lifo) AstDataSegment(offset, std::move(fragments));
             if (!segment || !module->append(segment))
                 return false;
 
             pages = AlignBytes<size_t>(totalLength, PageSize) / PageSize;
@@ -3909,17 +3909,17 @@ ParseTable(WasmParseContext& c, WasmToke
 
     uint32_t numElements = uint32_t(elems.length());
     if (numElements != elems.length())
         return false;
 
     if (!module->addTable(name, Limits(numElements, Some(numElements), Shareable::False)))
         return false;
 
-    auto* zero = new(c.lifo) AstConst(Val(uint32_t(0)));
+    auto* zero = new(c.lifo) AstConst(LitVal(uint32_t(0)));
     if (!zero)
         return false;
 
     AstElemSegment* segment = new(c.lifo) AstElemSegment(zero, std::move(elems));
     return segment && module->append(segment);
 }
 
 static AstElemSegment*
--- a/js/src/wasm/WasmTypes.cpp
+++ b/js/src/wasm/WasmTypes.cpp
@@ -55,16 +55,37 @@ static_assert(MaxMemoryInitialPages <= A
 // All plausible targets must be able to do at least IEEE754 double
 // loads/stores, hence the lower limit of 8.  Some Intel processors support
 // AVX-512 loads/stores, hence the upper limit of 64.
 static_assert(MaxMemoryAccessSize >= 8,  "MaxMemoryAccessSize too low");
 static_assert(MaxMemoryAccessSize <= 64, "MaxMemoryAccessSize too high");
 static_assert((MaxMemoryAccessSize & (MaxMemoryAccessSize-1)) == 0,
               "MaxMemoryAccessSize is not a power of two");
 
+Val::Val(const LitVal& val)
+{
+    type_ = val.type();
+    switch (type_.code()) {
+      case ValType::I32: u.i32_ = val.i32(); return;
+      case ValType::F32: u.f32_ = val.f32(); return;
+      case ValType::I64: u.i64_ = val.i64(); return;
+      case ValType::F64: u.f64_ = val.f64(); return;
+      case ValType::I8x16:
+      case ValType::B8x16:
+      case ValType::I16x8:
+      case ValType::B16x8:
+      case ValType::I32x4:
+      case ValType::F32x4:
+      case ValType::B32x4: memcpy(&u, val.rawSimd(), jit::Simd128DataSize); return;
+      case ValType::AnyRef: u.ptr_ = val.ptr(); return;
+      case ValType::Ref: break;
+    }
+    MOZ_CRASH();
+}
+
 void
 Val::writePayload(uint8_t* dst) const
 {
     switch (type_.code()) {
       case ValType::I32:
       case ValType::F32:
         memcpy(dst, &u.i32_, sizeof(u.i32_));
         return;
@@ -78,19 +99,37 @@ Val::writePayload(uint8_t* dst) const
       case ValType::F32x4:
       case ValType::B8x16:
       case ValType::B16x8:
       case ValType::B32x4:
         memcpy(dst, &u, jit::Simd128DataSize);
         return;
       case ValType::Ref:
       case ValType::AnyRef:
-        // TODO
-        MOZ_CRASH("writing imported value of Ref/AnyRef in global NYI");
+        MOZ_ASSERT(*(JSObject**)dst == nullptr, "should be null so no need for a pre-barrier");
+        memcpy(dst, &u.ptr_, sizeof(JSObject*));
+        // Either the written location is in the global data section in the
+        // WasmInstanceObject, or the Cell of a WasmGlobalObject:
+        // - WasmInstanceObjects are always tenured and u.ptr_ may point to a
+        // nursery object, so we need a post-barrier since the global data of
+        // an instance is effectively a field of the WasmInstanceObject.
+        // - WasmGlobalObjects are always tenured, and they have a Cell field,
+        // so a post-barrier may be needed for the same reason as above.
+        if (u.ptr_)
+            JSObject::writeBarrierPost((JSObject**)dst, nullptr, u.ptr_);
+        return;
     }
+    MOZ_CRASH("unexpected Val type");
+}
+
+void
+Val::trace(JSTracer* trc)
+{
+    if (type_.isValid() && type_ == ValType::AnyRef && u.ptr_)
+        TraceManuallyBarrieredEdge(trc, &u.ptr_, "wasm anyref global");
 }
 
 bool
 wasm::IsRoundingFunction(SymbolicAddress callee, jit::RoundingMode* mode)
 {
     switch (callee) {
       case SymbolicAddress::FloorD:
       case SymbolicAddress::FloorF:
--- a/js/src/wasm/WasmTypes.h
+++ b/js/src/wasm/WasmTypes.h
@@ -502,17 +502,17 @@ SizeOf(ValType vt)
       case ValType::I32x4:
       case ValType::F32x4:
       case ValType::B8x16:
       case ValType::B16x8:
       case ValType::B32x4:
         return 16;
       case ValType::AnyRef:
       case ValType::Ref:
-        MOZ_CRASH("unexpected ref/anyref");
+        return sizeof(intptr_t);
     }
     MOZ_CRASH("Invalid ValType");
 }
 
 static inline bool
 IsSimdType(ValType vt)
 {
     switch (vt.code()) {
@@ -761,70 +761,79 @@ enum class Shareable
 };
 
 enum class HasGcTypes
 {
     False,
     True
 };
 
-// The Val class represents a single WebAssembly value of a given value type,
-// mostly for the purpose of numeric literals and initializers. A Val does not
-// directly map to a JS value since there is not (currently) a precise
-// representation of i64 values. A Val may contain non-canonical NaNs since,
+// The LitVal class represents a single WebAssembly value of a given value
+// type, mostly for the purpose of numeric literals and initializers. A LitVal
+// does not directly map to a JS value since there is not (currently) a precise
+// representation of i64 values. A LitVal may contain non-canonical NaNs since,
 // within WebAssembly, floats are not canonicalized. Canonicalization must
 // happen at the JS boundary.
 
-class Val
+class LitVal
 {
+  protected:
     ValType type_;
     union U {
-        uint32_t i32_;
-        uint64_t i64_;
-        float f32_;
-        double f64_;
-        I8x16 i8x16_;
-        I16x8 i16x8_;
-        I32x4 i32x4_;
-        F32x4 f32x4_;
+        uint32_t  i32_;
+        uint64_t  i64_;
+        float     f32_;
+        double    f64_;
+        I8x16     i8x16_;
+        I16x8     i16x8_;
+        I32x4     i32x4_;
+        F32x4     f32x4_;
+        JSObject* ptr_;
     } u;
 
   public:
-    Val() = default;
-
-    explicit Val(uint32_t i32) : type_(ValType::I32) { u.i32_ = i32; }
-    explicit Val(uint64_t i64) : type_(ValType::I64) { u.i64_ = i64; }
-
-    explicit Val(float f32) : type_(ValType::F32) { u.f32_ = f32; }
-    explicit Val(double f64) : type_(ValType::F64) { u.f64_ = f64; }
-
-    explicit Val(const I8x16& i8x16, ValType type = ValType::I8x16) : type_(type) {
+    LitVal() : type_(), u{} {}
+
+    explicit LitVal(uint32_t i32) : type_(ValType::I32) { u.i32_ = i32; }
+    explicit LitVal(uint64_t i64) : type_(ValType::I64) { u.i64_ = i64; }
+
+    explicit LitVal(float f32) : type_(ValType::F32) { u.f32_ = f32; }
+    explicit LitVal(double f64) : type_(ValType::F64) { u.f64_ = f64; }
+
+    explicit LitVal(ValType refType, JSObject* ptr) : type_(refType) {
+        MOZ_ASSERT(refType.isRefOrAnyRef());
+        MOZ_ASSERT(ptr == nullptr, "use Val for non-nullptr ref types to get tracing");
+        u.ptr_ = ptr;
+    }
+
+    explicit LitVal(const I8x16& i8x16, ValType type = ValType::I8x16) : type_(type) {
         MOZ_ASSERT(type_ == ValType::I8x16 || type_ == ValType::B8x16);
         memcpy(u.i8x16_, i8x16, sizeof(u.i8x16_));
     }
-    explicit Val(const I16x8& i16x8, ValType type = ValType::I16x8) : type_(type) {
+    explicit LitVal(const I16x8& i16x8, ValType type = ValType::I16x8) : type_(type) {
         MOZ_ASSERT(type_ == ValType::I16x8 || type_ == ValType::B16x8);
         memcpy(u.i16x8_, i16x8, sizeof(u.i16x8_));
     }
-    explicit Val(const I32x4& i32x4, ValType type = ValType::I32x4) : type_(type) {
+    explicit LitVal(const I32x4& i32x4, ValType type = ValType::I32x4) : type_(type) {
         MOZ_ASSERT(type_ == ValType::I32x4 || type_ == ValType::B32x4);
         memcpy(u.i32x4_, i32x4, sizeof(u.i32x4_));
     }
-    explicit Val(const F32x4& f32x4) : type_(ValType::F32x4) {
+    explicit LitVal(const F32x4& f32x4) : type_(ValType::F32x4) {
         memcpy(u.f32x4_, f32x4, sizeof(u.f32x4_));
     }
 
     ValType type() const { return type_; }
     bool isSimd() const { return IsSimdType(type()); }
     static constexpr size_t sizeofLargestValue() { return sizeof(u); }
 
     uint32_t i32() const { MOZ_ASSERT(type_ == ValType::I32); return u.i32_; }
     uint64_t i64() const { MOZ_ASSERT(type_ == ValType::I64); return u.i64_; }
     const float& f32() const { MOZ_ASSERT(type_ == ValType::F32); return u.f32_; }
     const double& f64() const { MOZ_ASSERT(type_ == ValType::F64); return u.f64_; }
+    JSObject* ptr() const { MOZ_ASSERT(type_.isRefOrAnyRef()); return u.ptr_; }
 
     const I8x16& i8x16() const {
         MOZ_ASSERT(type_ == ValType::I8x16 || type_ == ValType::B8x16);
         return u.i8x16_;
     }
     const I16x8& i16x8() const {
         MOZ_ASSERT(type_ == ValType::I16x8 || type_ == ValType::B16x8);
         return u.i16x8_;
@@ -832,21 +841,50 @@ class Val
     const I32x4& i32x4() const {
         MOZ_ASSERT(type_ == ValType::I32x4 || type_ == ValType::B32x4);
         return u.i32x4_;
     }
     const F32x4& f32x4() const {
         MOZ_ASSERT(type_ == ValType::F32x4);
         return u.f32x4_;
     }
-
-    void writePayload(uint8_t* dst) const;
+    // To be used only by Val.
+    const void* rawSimd() const { return &u.i32x4_; }
 };
 
-typedef Vector<Val, 0, SystemAllocPolicy> ValVector;
+typedef Vector<LitVal, 0, SystemAllocPolicy> LitValVector;
+
+// A Val is a LitVal that can contain pointers to JSObjects, thanks to their
+// trace implementation. Since a Val is able to store a pointer to a JSObject,
+// it needs to be traced during compilation in case the pointee is moved.
+// The classic shorthands for Rooted things are defined after this class, for
+// easier usage.
+
+class MOZ_NON_PARAM Val : public LitVal
+{
+  public:
+    Val() : LitVal() {}
+    explicit Val(const LitVal& val);
+    explicit Val(uint32_t i32)  : LitVal(i32) {}
+    explicit Val(uint64_t i64)  : LitVal(i64) {}
+    explicit Val(float f32)     : LitVal(f32) {}
+    explicit Val(double f64)    : LitVal(f64) {}
+    explicit Val(JSObject* obj) : LitVal(ValType::AnyRef, nullptr) { u.ptr_ = obj; }
+    void writePayload(uint8_t* dst) const;
+    void trace(JSTracer* trc);
+};
+
+typedef Rooted<Val> RootedVal;
+typedef Handle<Val> HandleVal;
+typedef MutableHandle<Val> MutableHandleVal;
+
+typedef GCVector<Val, 0, SystemAllocPolicy> GCVectorVal;
+typedef Rooted<GCVectorVal> RootedValVector;
+typedef Handle<GCVectorVal> HandleValVector;
+typedef MutableHandle<GCVectorVal> MutableHandleValVector;
 
 // The FuncType class represents a WebAssembly function signature which takes a
 // list of value types and returns an expression type. The engine uses two
 // in-memory representations of the argument Vector's memory (when elements do
 // not fit inline): normal malloc allocation (via SystemAllocPolicy) and
 // allocation in a LifoAlloc (via LifoAllocPolicy). The former FuncType objects
 // can have any lifetime since they own the memory. The latter FuncType objects
 // must not outlive the associated LifoAlloc mark/release interval (which is
@@ -958,46 +996,46 @@ class InitExpr
     enum class Kind {
         Constant,
         GetGlobal
     };
 
   private:
     Kind kind_;
     union U {
-        Val val_;
+        LitVal val_;
         struct {
             uint32_t index_;
             ValType type_;
         } global;
         U() : global{} {}
     } u;
 
   public:
     InitExpr() = default;
 
-    explicit InitExpr(Val val) : kind_(Kind::Constant) {
+    explicit InitExpr(LitVal val) : kind_(Kind::Constant) {
         u.val_ = val;
     }
 
     explicit InitExpr(uint32_t globalIndex, ValType type) : kind_(Kind::GetGlobal) {
         u.global.index_ = globalIndex;
         u.global.type_ = type;
     }
 
     Kind kind() const { return kind_; }
 
     bool isVal() const { return kind() == Kind::Constant; }
-    Val val() const { MOZ_ASSERT(isVal()); return u.val_; }
+    LitVal val() const { MOZ_ASSERT(isVal()); return u.val_; }
 
     uint32_t globalIndex() const { MOZ_ASSERT(kind() == Kind::GetGlobal); return u.global.index_; }
 
     ValType type() const {
         switch (kind()) {
-          case Kind::Constant: return u.val_.type();
+          case Kind::Constant:  return u.val_.type();
           case Kind::GetGlobal: return u.global.type_;
         }
         MOZ_CRASH("unexpected initExpr type");
     }
 };
 
 // CacheableChars is used to cacheably store UniqueChars.
 
@@ -1093,17 +1131,17 @@ class GlobalDesc
                 } import;
                 U() : import{} {}
             } val;
             unsigned offset_;
             bool isMutable_;
             bool isWasm_;
             bool isExport_;
         } var;
-        Val cst_;
+        LitVal cst_;
         V() {}
     } u;
     GlobalKind kind_;
 
     // Private, as they have unusual semantics.
 
     bool isExport() const { return !isConstant() && u.var.isExport_; }
     bool isWasm() const { return !isConstant() && u.var.isWasm_; }
@@ -1120,17 +1158,18 @@ class GlobalDesc
             u.var.isWasm_ = kind == Wasm;
             u.var.isExport_ = false;
             u.var.offset_ = UINT32_MAX;
         } else {
             u.cst_ = initial.val();
         }
     }
 
-    explicit GlobalDesc(ValType type, bool isMutable, uint32_t importIndex, ModuleKind kind = ModuleKind::Wasm)
+    explicit GlobalDesc(ValType type, bool isMutable, uint32_t importIndex,
+                        ModuleKind kind = ModuleKind::Wasm)
       : kind_(GlobalKind::Import)
     {
         u.var.val.import.type_ = type;
         u.var.val.import.index_ = importIndex;
         u.var.isMutable_ = isMutable;
         u.var.isWasm_ = kind == Wasm;
         u.var.isExport_ = false;
         u.var.offset_ = UINT32_MAX;
@@ -1153,17 +1192,17 @@ class GlobalDesc
     }
 
     GlobalKind kind() const { return kind_; }
     bool isVariable() const { return kind_ == GlobalKind::Variable; }
     bool isConstant() const { return kind_ == GlobalKind::Constant; }
     bool isImport() const { return kind_ == GlobalKind::Import; }
 
     bool isMutable() const { return !isConstant() && u.var.isMutable_; }
-    Val constantValue() const { MOZ_ASSERT(isConstant()); return u.cst_; }
+    LitVal constantValue() const { MOZ_ASSERT(isConstant()); return u.cst_; }
     const InitExpr& initExpr() const { MOZ_ASSERT(isVariable()); return u.var.val.initial_; }
     uint32_t importIndex() const { MOZ_ASSERT(isImport()); return u.var.val.import.index_; }
 
     // If isIndirect() is true then storage for the value is not in the
     // instance's global area, but in a WasmGlobalObject::Cell hanging off a
     // WasmGlobalObject; the global area contains a pointer to the Cell.
     //
     // We don't want to indirect unless we must, so only mutable, exposed
@@ -1937,16 +1976,17 @@ enum class SymbolicAddress
     Int64ToDouble,
     GrowMemory,
     CurrentMemory,
     WaitI32,
     WaitI64,
     Wake,
     MemCopy,
     MemFill,
+    PostBarrier,
 #if defined(JS_CODEGEN_MIPS32)
     js_jit_gAtomic64Lock,
 #endif
     Limit
 };
 
 bool
 IsRoundingFunction(SymbolicAddress callee, jit::RoundingMode* mode);
@@ -2058,16 +2098,20 @@ struct TlsData
     // Usually equal to cx->stackLimitForJitCode(JS::StackForUntrustedScript),
     // but can be racily set to trigger immediate trap as an opportunity to
     // CheckForInterrupt without an additional branch.
     Atomic<uintptr_t, mozilla::Relaxed> stackLimit;
 
     // Set to 1 when wasm should call CheckForInterrupt.
     Atomic<uint32_t, mozilla::Relaxed> interrupt;
 
+#ifdef ENABLE_WASM_GC
+    uint8_t* addressOfNeedsIncrementalBarrier;
+#endif
+
     // Methods to set, test and clear the above two fields. Both interrupt
     // fields are Relaxed and so no consistency/ordering can be assumed.
     void setInterrupt();
     bool isInterrupted() const;
     void resetInterrupt(JSContext* cx);
 
     // Pointer that should be freed (due to padding before the TlsData).
     void* allocatedBase;
@@ -2310,17 +2354,17 @@ static const unsigned PageSize = 64 * 10
 
 // Bounds checks always compare the base of the memory access with the bounds
 // check limit. If the memory access is unaligned, this means that, even if the
 // bounds check succeeds, a few bytes of the access can extend past the end of
 // memory. To guard against this, extra space is included in the guard region to
 // catch the overflow. MaxMemoryAccessSize is a conservative approximation of
 // the maximum guard space needed to catch all unaligned overflows.
 
-static const unsigned MaxMemoryAccessSize = Val::sizeofLargestValue();
+static const unsigned MaxMemoryAccessSize = LitVal::sizeofLargestValue();
 
 #ifdef WASM_HUGE_MEMORY
 
 // On WASM_HUGE_MEMORY platforms, every asm.js or WebAssembly memory
 // unconditionally allocates a huge region of virtual memory of size
 // wasm::HugeMappedSize. This allows all memory resizing to work without
 // reallocation and provides enough guard space for all offsets to be folded
 // into memory accesses.
@@ -2514,12 +2558,54 @@ class DebugFrame
 
     // DebugFrames are aligned to 8-byte aligned, allowing them to be placed in
     // an AbstractFramePtr.
 
     static const unsigned Alignment = 8;
     static void alignmentStaticAsserts();
 };
 
+# ifdef ENABLE_WASM_GC
+// A packed format for an argument to the Instance::postBarrier function.
+class PostBarrierArg
+{
+  public:
+    enum class Type {
+        Global = 0x0,
+        Last = Global
+    };
+
+  private:
+    uint32_t type_: 1;
+    uint32_t payload_: 31;
+
+    PostBarrierArg(uint32_t payload, Type type)
+      : type_(uint32_t(type)),
+        payload_(payload)
+    {
+        MOZ_ASSERT(payload < (UINT32_MAX >> 1));
+        MOZ_ASSERT(uint32_t(type) <= uint32_t(Type::Last));
+    }
+
+  public:
+    static PostBarrierArg Global(uint32_t globalIndex) {
+        return PostBarrierArg(globalIndex, Type::Global);
+    }
+
+    Type type() const {
+        MOZ_ASSERT(type_ <= uint32_t(Type::Last));
+        return Type(type_);
+    }
+    uint32_t globalIndex() const {
+        MOZ_ASSERT(type() == Type::Global);
+        return payload_;
+    }
+
+    uint32_t rawPayload() const {
+        return (payload_ << 1) | type_;
+    }
+};
+# endif
+
 } // namespace wasm
 } // namespace js
 
 #endif // wasm_types_h
--- a/js/src/wasm/WasmValidate.cpp
+++ b/js/src/wasm/WasmValidate.cpp
@@ -1393,29 +1393,30 @@ DecodeTableLimits(Decoder& d, TableDescV
 static bool
 GlobalIsJSCompatible(Decoder& d, ValType type, bool isMutable)
 {
     switch (type.code()) {
       case ValType::I32:
       case ValType::F32:
       case ValType::F64:
       case ValType::I64:
+      case ValType::AnyRef:
         break;
       default:
         return d.fail("unexpected variable type in global import/export");
     }
 
     return true;
 }
 
 static bool
-DecodeGlobalType(Decoder& d, const TypeDefVector& types, ValType* type, bool* isMutable)
+DecodeGlobalType(Decoder& d, const TypeDefVector& types, HasGcTypes gcTypesEnabled, ValType* type,
+                 bool* isMutable)
 {
-    // No gc types in globals at the moment.
-    if (!DecodeValType(d, ModuleKind::Wasm, types.length(), HasGcTypes::False, type))
+    if (!DecodeValType(d, ModuleKind::Wasm, types.length(), gcTypesEnabled, type))
         return false;
     if (!ValidateRefType(d, types, *type))
         return false;
 
     uint8_t flags;
     if (!d.readFixedU8(&flags))
         return d.fail("expected global flags");
 
@@ -1504,17 +1505,17 @@ DecodeImport(Decoder& d, ModuleEnvironme
       case DefinitionKind::Memory: {
         if (!DecodeMemoryLimits(d, env))
             return false;
         break;
       }
       case DefinitionKind::Global: {
         ValType type;
         bool isMutable;
-        if (!DecodeGlobalType(d, env->types, &type, &isMutable))
+        if (!DecodeGlobalType(d, env->types, env->gcTypesEnabled, &type, &isMutable))
             return false;
         if (!GlobalIsJSCompatible(d, type, isMutable))
             return false;
         if (!env->globals.append(GlobalDesc(type, isMutable, env->globals.length())))
             return false;
         if (env->globals.length() > MaxGlobals)
             return d.fail("too many globals");
         break;
@@ -1632,50 +1633,62 @@ DecodeMemorySection(Decoder& d, ModuleEn
         if (!DecodeMemoryLimits(d, env))
             return false;
     }
 
     return d.finishSection(*range, "memory");
 }
 
 static bool
-DecodeInitializerExpression(Decoder& d, const GlobalDescVector& globals, ValType expected,
-                            InitExpr* init)
+DecodeInitializerExpression(Decoder& d, HasGcTypes gcTypesEnabled, const GlobalDescVector& globals,
+                            ValType expected, InitExpr* init)
 {
     OpBytes op;
     if (!d.readOp(&op))
         return d.fail("failed to read initializer type");
 
     switch (op.b0) {
       case uint16_t(Op::I32Const): {
         int32_t i32;
         if (!d.readVarS32(&i32))
             return d.fail("failed to read initializer i32 expression");
-        *init = InitExpr(Val(uint32_t(i32)));
+        *init = InitExpr(LitVal(uint32_t(i32)));
         break;
       }
       case uint16_t(Op::I64Const): {
         int64_t i64;
         if (!d.readVarS64(&i64))
             return d.fail("failed to read initializer i64 expression");
-        *init = InitExpr(Val(uint64_t(i64)));
+        *init = InitExpr(LitVal(uint64_t(i64)));
         break;
       }
       case uint16_t(Op::F32Const): {
         float f32;
         if (!d.readFixedF32(&f32))
             return d.fail("failed to read initializer f32 expression");
-        *init = InitExpr(Val(f32));
+        *init = InitExpr(LitVal(f32));
         break;
       }
       case uint16_t(Op::F64Const): {
         double f64;
         if (!d.readFixedF64(&f64))
             return d.fail("failed to read initializer f64 expression");
-        *init = InitExpr(Val(f64));
+        *init = InitExpr(LitVal(f64));
+        break;
+      }
+      case uint16_t(Op::RefNull): {
+        if (gcTypesEnabled == HasGcTypes::False)
+            return d.fail("unexpected initializer expression");
+        uint8_t valType;
+        uint32_t unusedRefTypeIndex;
+        if (!d.readValType(&valType, &unusedRefTypeIndex))
+            return false;
+        if (valType != uint8_t(ValType::AnyRef))
+            return d.fail("expected anyref as type for ref.null");
+        *init = InitExpr(LitVal(ValType::AnyRef, nullptr));
         break;
       }
       case uint16_t(Op::GetGlobal): {
         uint32_t i;
         if (!d.readVarU32(&i))
             return d.fail("failed to read get_global index in initializer expression");
         if (i >= globals.length())
             return d.fail("global index out of range in initializer expression");
@@ -1718,21 +1731,21 @@ DecodeGlobalSection(Decoder& d, ModuleEn
         return d.fail("too many globals");
 
     if (!env->globals.reserve(numGlobals.value()))
         return false;
 
     for (uint32_t i = 0; i < numDefs; i++) {
         ValType type;
         bool isMutable;
-        if (!DecodeGlobalType(d, env->types, &type, &isMutable))
+        if (!DecodeGlobalType(d, env->types, env->gcTypesEnabled, &type, &isMutable))
             return false;
 
         InitExpr initializer;
-        if (!DecodeInitializerExpression(d, env->globals, type, &initializer))
+        if (!DecodeInitializerExpression(d, env->gcTypesEnabled, env->globals, type, &initializer))
             return false;
 
         env->globals.infallibleAppend(GlobalDesc(initializer, isMutable));
     }
 
     return d.finishSection(*range, "global");
 }
 
@@ -1903,17 +1916,18 @@ DecodeElemSection(Decoder& d, ModuleEnvi
         if (!d.readVarU32(&tableIndex))
             return d.fail("expected table index");
 
         MOZ_ASSERT(env->tables.length() <= 1);
         if (tableIndex >= env->tables.length())
             return d.fail("table index out of range");
 
         InitExpr offset;
-        if (!DecodeInitializerExpression(d, env->globals, ValType::I32, &offset))
+        if (!DecodeInitializerExpression(d, env->gcTypesEnabled, env->globals, ValType::I32,
+                                         &offset))
             return false;
 
         uint32_t numElems;
         if (!d.readVarU32(&numElems))
             return d.fail("expected segment size");
 
         if (numElems > MaxTableInitialLength)
             return d.fail("too many table elements");
@@ -2073,17 +2087,18 @@ DecodeDataSection(Decoder& d, ModuleEnvi
 
         if (linearMemoryIndex != 0)
             return d.fail("linear memory index must currently be 0");
 
         if (!env->usesMemory())
             return d.fail("data segment requires a memory section");
 
         DataSegment seg;
-        if (!DecodeInitializerExpression(d, env->globals, ValType::I32, &seg.offset))
+        if (!DecodeInitializerExpression(d, env->gcTypesEnabled, env->globals, ValType::I32,
+                                         &seg.offset))
             return false;
 
         if (!d.readVarU32(&seg.length))
             return d.fail("expected segment size");
 
         if (seg.length > MaxMemoryInitialPages * PageSize)
             return d.fail("segment size too big");
 
--- a/layout/painting/FrameLayerBuilder.cpp
+++ b/layout/painting/FrameLayerBuilder.cpp
@@ -5043,21 +5043,24 @@ FrameLayerBuilder::AddPaintedDisplayItem
     }
     if (!tempManager) {
       tempManager = new BasicLayerManager(BasicLayerManager::BLM_INACTIVE);
     }
   }
 
   if (layer->Manager() == mRetainingManager) {
     DisplayItemData *data = aItem.mDisplayItemData;
-    if (data) {
-      if (!data->mUsed) {
-        data->BeginUpdate(layer, aItem.mLayerState, aItem.mItem, aItem.mReused, aItem.mMerged);
+    if (data && !data->mUsed) {
+      data->BeginUpdate(layer, aItem.mLayerState, aItem.mItem, aItem.mReused, aItem.mMerged);
+    } else {
+      if (data && data->mUsed) {
+        // If the DID has already been used (by a previously merged frame,
+        // which is not merged this paint) we must create a new DID for the item.
+        aItem.mItem->SetDisplayItemData(nullptr);
       }
-    } else {
       data = StoreDataForFrame(aItem.mItem, layer, aItem.mLayerState, nullptr);
     }
     data->mInactiveManager = tempManager;
     // We optimized this PaintedLayer into a ColorLayer/ImageLayer. Store the optimized
     // layer here.
     if (aLayer != layer) {
       data->mOptLayer = aLayer;
       data->mItem = nullptr;
--- a/netwerk/base/nsNetUtil.cpp
+++ b/netwerk/base/nsNetUtil.cpp
@@ -1669,16 +1669,21 @@ private:
             }
 
             // If we succeeded, let's try to read again.
             if (NS_SUCCEEDED(rv)) {
                 mWrittenData += writtenData;
                 if (mCount != -1) {
                     MOZ_ASSERT(mCount >= writtenData);
                     mCount -= writtenData;
+
+                    // Is this the end of the reading?
+                    if (mCount == 0) {
+                        return NS_OK;
+                    }
                 }
 
                 continue;
             }
 
             // Async wait...
             if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
                 rv = MaybeCreateTaskQueue();
--- a/netwerk/cache2/CacheFileInputStream.cpp
+++ b/netwerk/cache2/CacheFileInputStream.cpp
@@ -147,16 +147,20 @@ CacheFileInputStream::ReadSegments(nsWri
 
     if (NS_FAILED(mStatus)) {
       return mStatus;
     }
 
     return NS_OK;
   }
 
+  if (aCount == 0) {
+    return NS_OK;
+  }
+
   EnsureCorrectChunk(false);
 
   while (true) {
     if (NS_FAILED(mStatus))
       return mStatus;
 
     if (!mChunk) {
       if (mListeningForChunk == -1) {
--- a/security/manager/ssl/nsNSSComponent.cpp
+++ b/security/manager/ssl/nsNSSComponent.cpp
@@ -856,19 +856,24 @@ nsNSSComponent::TrustLoaded3rdPartyRoots
   CERTCertTrust trust = {
     CERTDB_TRUSTED_CA | CERTDB_VALID_CA | CERTDB_USER,
     0,
     0
   };
   if (mEnterpriseRoots) {
     for (CERTCertListNode* n = CERT_LIST_HEAD(mEnterpriseRoots.get());
          !CERT_LIST_END(n, mEnterpriseRoots.get()); n = CERT_LIST_NEXT(n)) {
-      if (!n || !n->cert) {
+      if (!n) {
         MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
-                ("library failure: CERTCertListNode null or lacks cert"));
+                ("library failure: CERTCertListNode null"));
+        break;
+      }
+      if (!n->cert) {
+        MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
+                ("library failure: CERTCertListNode lacks cert"));
         continue;
       }
       UniqueCERTCertificate cert(CERT_DupCertificate(n->cert));
       if (ChangeCertTrustWithPossibleAuthentication(cert, trust, nullptr)
             != SECSuccess) {
         MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
                 ("couldn't trust enterprise certificate for TLS server auth"));
       }
--- a/taskcluster/ci/spidermonkey/linux.yml
+++ b/taskcluster/ci/spidermonkey/linux.yml
@@ -121,16 +121,17 @@ sm-compacting-linux64/debug:
 
 sm-msan-linux64/opt:
     description: "Spidermonkey Memory Sanitizer"
     index:
         job-name: sm-msan-linux64-opt
     treeherder:
         symbol: SM(msan)
         platform: linux64/opt
+        tier: 3
     run:
         spidermonkey-variant: msan
 
 sm-tsan-linux64/opt:
     description: "Spidermonkey Thread Sanitizer"
     index:
         job-name: sm-tsan-linux64-opt
     treeherder:
--- a/testing/mozharness/scripts/desktop_unittest.py
+++ b/testing/mozharness/scripts/desktop_unittest.py
@@ -905,16 +905,17 @@ class DesktopUnittest(TestingMixin, Merc
                         self.add_per_test_coverage_report(
                             env['GCOV_RESULTS_DIR'] if 'GCOV_RESULTS_DIR' in env else gcov_dir,
                             jsvm_dir,
                             suite,
                             per_test_args[-1]
                         )
                         if 'GCOV_RESULTS_DIR' in env:
                             shutil.rmtree(gcov_dir)
+                            del env['GCOV_RESULTS_DIR']
 
                     # mochitest, reftest, and xpcshell suites do not return
                     # appropriate return codes. Therefore, we must parse the output
                     # to determine what the tbpl_status and worst_log_level must
                     # be. We do this by:
                     # 1) checking to see if our mozharness script ran into any
                     #    errors itself with 'num_errors' <- OutputParser
                     # 2) if num_errors is 0 then we look in the subclassed 'parser'