Merge inbound to central, a=merge
authorWes Kocher <wkocher@mozilla.com>
Mon, 13 Mar 2017 16:37:21 -0700
changeset 347360 6d38ad302429c98115c354d643e81987ecec5d3c
parent 347359 727412152afc7cf3530ce0fd9f2199aa84aa33a0 (current diff)
parent 347291 2b52802899ae6b4d14481481e0ea37664c48a7d5 (diff)
child 347361 474dc1155078c3bcb71362f2c7588d77e0d17977
child 347445 6b1862025a25525ce9ebcd044afa91269d1d4ee5
push id88014
push userkwierso@gmail.com
push dateMon, 13 Mar 2017 23:39:54 +0000
treeherdermozilla-inbound@474dc1155078 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone55.0a1
first release with
nightly linux32
6d38ad302429 / 55.0a1 / 20170314110401 / files
nightly linux64
6d38ad302429 / 55.0a1 / 20170314110401 / files
nightly mac
6d38ad302429 / 55.0a1 / 20170314030215 / files
nightly win32
6d38ad302429 / 55.0a1 / 20170314030215 / files
nightly win64
6d38ad302429 / 55.0a1 / 20170314030215 / 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 central, a=merge MozReview-Commit-ID: ECt4ZkKUuQp
gfx/layers/d3d11/TextureD3D11.cpp
--- a/browser/base/content/nsContextMenu.js
+++ b/browser/base/content/nsContextMenu.js
@@ -1129,20 +1129,22 @@ nsContextMenu.prototype = {
       };
       mm.addMessageListener("ContextMenu:Canvas:ToBlobURL:Result", onMessage);
     });
   },
 
   // Change current window to the URL of the image, video, or audio.
   viewMedia: function(e) {
     let referrerURI = gContextMenuContentData.documentURIObject;
+    let systemPrincipal = Services.scriptSecurityManager.getSystemPrincipal();
     if (this.onCanvas) {
       this._canvasToBlobURL(this.target).then(function(blobURL) {
         openUILink(blobURL, e, { disallowInheritPrincipal: true,
-                                 referrerURI: referrerURI });
+                                 referrerURI: referrerURI,
+                                 originPrincipal: systemPrincipal});
       }, Cu.reportError);
     }
     else {
       urlSecurityCheck(this.mediaURL,
                        this.browser.contentPrincipal,
                        Ci.nsIScriptSecurityManager.DISALLOW_SCRIPT);
       openUILink(this.mediaURL, e, { disallowInheritPrincipal: true,
                                      referrerURI: referrerURI });
--- a/browser/base/content/test/general/browser_selectpopup.js
+++ b/browser/base/content/test/general/browser_selectpopup.js
@@ -589,22 +589,21 @@ function* performLargePopupTests(win) {
 
   scrollPos = selectPopup.scrollBox.scrollTop;
   EventUtils.synthesizeMouseAtPoint(popupRect.left + 20, popupRect.bottom + 25, { type: "mouseup" }, win);
   is(selectPopup.scrollBox.scrollTop, scrollPos, "scroll position at mouseup from option should not change");
 
   EventUtils.synthesizeMouseAtPoint(popupRect.left + 20, popupRect.bottom + 20, { type: "mousemove" }, win);
   is(selectPopup.scrollBox.scrollTop, scrollPos, "scroll position at mousemove after mouseup should not change");
 
-
   yield hideSelectPopup(selectPopup, "escape", win);
 
   let positions = [
     "margin-top: 300px;",
-    "position: fixed; bottom: 100px;",
+    "position: fixed; bottom: 200px;",
     "width: 100%; height: 9999px;"
   ];
 
   let position;
   while (positions.length) {
     yield openSelectPopup(selectPopup, "key", "select", win);
 
     let rect = selectPopup.getBoundingClientRect();
@@ -639,18 +638,63 @@ function* performLargePopupTests(win) {
     let contentPainted = BrowserTestUtils.contentPainted(browser);
     yield ContentTask.spawn(browser, position, function*(contentPosition) {
       let select = content.document.getElementById("one");
       select.setAttribute("style", contentPosition || "");
       select.getBoundingClientRect();
     });
     yield contentPainted;
   }
+
+  if (navigator.platform.indexOf("Mac") == 0) {
+    yield ContentTask.spawn(browser, null, function*() {
+      let doc = content.document;
+      doc.body.style = "padding-top: 400px;"
+
+      let select = doc.getElementById("one");
+      select.options[41].selected = true;
+      select.focus();
+    });
+
+    yield openSelectPopup(selectPopup, "key", "select", win);
+
+    ok(selectPopup.getBoundingClientRect().top > browser.getBoundingClientRect().top,
+       "select popup appears over selected item");
+
+    yield hideSelectPopup(selectPopup, "escape", win);
+  }
 }
 
+// This test checks select elements with a large number of options to ensure that
+// the popup appears within the browser area.
+add_task(function* test_large_popup() {
+  const pageUrl = "data:text/html," + escape(PAGECONTENT_SMALL);
+  let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, pageUrl);
+
+  yield* performLargePopupTests(window);
+
+  yield BrowserTestUtils.removeTab(tab);
+});
+
+// This test checks the same as the previous test but in a new smaller window.
+add_task(function* test_large_popup_in_small_window() {
+  let newwin = yield BrowserTestUtils.openNewBrowserWindow({ width: 400, height: 400 });
+
+  const pageUrl = "data:text/html," + escape(PAGECONTENT_SMALL);
+  let browserLoadedPromise = BrowserTestUtils.browserLoaded(newwin.gBrowser.selectedBrowser);
+  yield BrowserTestUtils.loadURI(newwin.gBrowser.selectedBrowser, pageUrl);
+  yield browserLoadedPromise;
+
+  newwin.gBrowser.selectedBrowser.focus();
+
+  yield* performLargePopupTests(newwin);
+
+  yield BrowserTestUtils.closeWindow(newwin);
+});
+
 function* performSelectSearchTests(win) {
   let browser = win.gBrowser.selectedBrowser;
   yield ContentTask.spawn(browser, null, function*() {
     let doc = content.document;
     let select = doc.getElementById("one");
 
     for (var i = 0; i < 40; i++) {
       select.add(new content.Option("Test" + i));
@@ -713,43 +757,16 @@ add_task(function* test_select_search() 
 
   yield performSelectSearchTests(window);
 
   yield BrowserTestUtils.removeTab(tab);
 
   yield SpecialPowers.popPrefEnv();
 });
 
-// This test checks select elements with a large number of options to ensure that
-// the popup appears within the browser area.
-add_task(function* test_large_popup() {
-  const pageUrl = "data:text/html," + escape(PAGECONTENT_SMALL);
-  let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, pageUrl);
-
-  yield* performLargePopupTests(window);
-
-  yield BrowserTestUtils.removeTab(tab);
-});
-
-// This test checks the same as the previous test but in a new smaller window.
-add_task(function* test_large_popup_in_small_window() {
-  let newwin = yield BrowserTestUtils.openNewBrowserWindow({ width: 400, height: 400 });
-
-  const pageUrl = "data:text/html," + escape(PAGECONTENT_SMALL);
-  let browserLoadedPromise = BrowserTestUtils.browserLoaded(newwin.gBrowser.selectedBrowser);
-  yield BrowserTestUtils.loadURI(newwin.gBrowser.selectedBrowser, pageUrl);
-  yield browserLoadedPromise;
-
-  newwin.gBrowser.selectedBrowser.focus();
-
-  yield* performLargePopupTests(newwin);
-
-  yield BrowserTestUtils.closeWindow(newwin);
-});
-
 // This test checks that a mousemove event is fired correctly at the menu and
 // not at the browser, ensuring that any mouse capture has been cleared.
 add_task(function* test_mousemove_correcttarget() {
   const pageUrl = "data:text/html," + escape(PAGECONTENT_SMALL);
   let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, pageUrl);
 
   let selectPopup = document.getElementById("ContentSelectDropdown").menupopup;
 
--- a/devtools/server/actors/layout.js
+++ b/devtools/server/actors/layout.js
@@ -93,16 +93,20 @@ var LayoutActor = ActorClassWithSpec(lay
    *
    * @param  {Node|NodeActor} rootNode
    *         The root node to start iterating at.
    * @return {Array} An array of GridActor objects.
    */
   getGrids: function (rootNode) {
     let grids = [];
 
+    if (!rootNode) {
+      return grids;
+    }
+
     let treeWalker = this.walker.getDocumentWalker(rootNode);
     while (treeWalker.nextNode()) {
       let currentNode = treeWalker.currentNode;
 
       if (currentNode.getGridFragments && currentNode.getGridFragments().length > 0) {
         let gridActor = new GridActor(this, currentNode);
         grids.push(gridActor);
       }
--- a/dom/base/TimeoutManager.cpp
+++ b/dom/base/TimeoutManager.cpp
@@ -1193,18 +1193,26 @@ TimeoutManager::Resume()
 }
 
 void
 TimeoutManager::Freeze()
 {
   MOZ_LOG(gLog, LogLevel::Debug,
           ("Freeze(TimeoutManager=%p)\n", this));
 
+  DebugOnly<bool> _seenDummyTimeout = false;
+
   TimeStamp now = TimeStamp::Now();
   ForEachUnorderedTimeout([&](Timeout* aTimeout) {
+    if (!aTimeout->mWindow) {
+      NS_ASSERTION(!_seenDummyTimeout, "More than one dummy timeout?!");
+      _seenDummyTimeout = true;
+      return;
+    }
+
     // Save the current remaining time for this timeout.  We will
     // re-apply it when the window is Thaw()'d.  This effectively
     // shifts timers to the right as if time does not pass while
     // the window is frozen.
     TimeDuration delta(0);
     if (aTimeout->When() > now) {
       delta = aTimeout->When() - now;
     }
--- a/dom/media/platforms/wmf/DXVA2Manager.cpp
+++ b/dom/media/platforms/wmf/DXVA2Manager.cpp
@@ -878,17 +878,16 @@ D3D11DXVA2Manager::CreateOutputSample(Re
 
 HRESULT
 D3D11DXVA2Manager::CopyToImage(IMFSample* aVideoSample,
                                const nsIntRect& aRegion,
                                Image** aOutImage)
 {
   NS_ENSURE_TRUE(aVideoSample, E_POINTER);
   NS_ENSURE_TRUE(aOutImage, E_POINTER);
-  NS_ENSURE_TRUE(mSyncObject, E_FAIL);
   MOZ_ASSERT(mTextureClientAllocator);
 
   RefPtr<D3D11ShareHandleImage> image =
     new D3D11ShareHandleImage(gfx::IntSize(mWidth, mHeight), aRegion);
   bool ok = image->AllocateTexture(mTextureClientAllocator, mDevice);
   NS_ENSURE_TRUE(ok, E_FAIL);
 
   RefPtr<TextureClient> client = image->GetTextureClient(ImageBridgeChild::GetSingleton().get());
@@ -898,16 +897,18 @@ D3D11DXVA2Manager::CopyToImage(IMFSample
   HRESULT hr;
   RefPtr<ID3D11Texture2D> texture = image->GetTexture();
 
   texture->QueryInterface((IDXGIKeyedMutex**)getter_AddRefs(mutex));
 
   if (mutex) {
     hr = mutex->AcquireSync(0, 2000);
     NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
+  } else {
+    NS_ENSURE_TRUE(mSyncObject, E_FAIL);
   }
 
   if (client && client->GetFormat() == SurfaceFormat::NV12) {
     // Our video frame is stored in a non-sharable ID3D11Texture2D. We need
     // to create a copy of that frame as a sharable resource, save its share
     // handle, and put that handle into the rendering pipeline.
 
     RefPtr<IMFMediaBuffer> buffer;
--- a/dom/media/webrtc/MediaEngineWebRTC.h
+++ b/dom/media/webrtc/MediaEngineWebRTC.h
@@ -568,16 +568,18 @@ private:
   int mChannel;
   MOZ_INIT_OUTSIDE_CTOR TrackID mTrackID;
   bool mStarted;
 
   nsString mDeviceName;
   nsCString mDeviceUUID;
 
   int32_t mSampleFrequency;
+  uint64_t mTotalFrames;
+  uint64_t mLastLogFrames;
   int32_t mPlayoutDelay;
 
   NullTransport *mNullTransport;
 
   nsTArray<int16_t> mInputBuffer;
   // mSkipProcessing is true if none of the processing passes are enabled,
   // because of prefs or constraints. This allows simply copying the audio into
   // the MSG, skipping resampling and the whole webrtc.org code.
--- a/dom/media/webrtc/MediaEngineWebRTCAudio.cpp
+++ b/dom/media/webrtc/MediaEngineWebRTCAudio.cpp
@@ -35,16 +35,21 @@ namespace mozilla {
 #ifdef LOG
 #undef LOG
 #endif
 
 extern LogModule* GetMediaManagerLog();
 #define LOG(msg) MOZ_LOG(GetMediaManagerLog(), mozilla::LogLevel::Debug, msg)
 #define LOG_FRAMES(msg) MOZ_LOG(GetMediaManagerLog(), mozilla::LogLevel::Verbose, msg)
 
+LogModule* AudioLogModule() {
+  static mozilla::LazyLogModule log("AudioLatency");
+  return static_cast<LogModule*>(log);
+}
+
 /**
  * Webrtc microphone source source.
  */
 NS_IMPL_ISUPPORTS0(MediaEngineWebRTCMicrophoneSource)
 NS_IMPL_ISUPPORTS0(MediaEngineWebRTCAudioCaptureSource)
 
 // XXX temp until MSG supports registration
 StaticRefPtr<AudioOutputObserver> gFarendObserver;
@@ -193,16 +198,18 @@ MediaEngineWebRTCMicrophoneSource::Media
   , mVoiceEngine(aVoiceEnginePtr)
   , mAudioInput(aAudioInput)
   , mMonitor("WebRTCMic.Monitor")
   , mCapIndex(aIndex)
   , mChannel(-1)
   , mTrackID(TRACK_NONE)
   , mStarted(false)
   , mSampleFrequency(MediaEngine::DEFAULT_SAMPLE_RATE)
+  , mTotalFrames(0)
+  , mLastLogFrames(0)
   , mPlayoutDelay(0)
   , mNullTransport(nullptr)
   , mSkipProcessing(false)
 {
   MOZ_ASSERT(aVoiceEnginePtr);
   MOZ_ASSERT(aAudioInput);
   mDeviceName.Assign(NS_ConvertUTF8toUTF16(name));
   mDeviceUUID.Assign(uuid);
@@ -573,16 +580,26 @@ void
 MediaEngineWebRTCMicrophoneSource::InsertInGraph(const T* aBuffer,
                                                  size_t aFrames,
                                                  uint32_t aChannels)
 {
   if (mState != kStarted) {
     return;
   }
 
+  if (MOZ_LOG_TEST(AudioLogModule(), LogLevel::Debug)) {
+    mTotalFrames += aFrames;
+    if (mTotalFrames > mLastLogFrames + mSampleFrequency) { // ~ 1 second
+      MOZ_LOG(AudioLogModule(), LogLevel::Debug,
+              ("%p: Inserting %" PRIuSIZE " samples into graph, total frames = %" PRIu64,
+               (void*)this, aFrames, mTotalFrames));
+      mLastLogFrames = mTotalFrames;
+    }
+  }
+
   size_t len = mSources.Length();
   for (size_t i = 0; i < len; i++) {
     if (!mSources[i]) {
       continue;
     }
     RefPtr<SharedBuffer> buffer =
       SharedBuffer::Create(aFrames * aChannels * sizeof(T));
     PodCopy(static_cast<T*>(buffer->Data()),
--- a/js/src/jit-test/lib/wasm-binary.js
+++ b/js/src/jit-test/lib/wasm-binary.js
@@ -80,16 +80,18 @@ const I32TruncSF32Code = 0xa8;
 const I32TruncUF32Code = 0xa9;
 const I32TruncSF64Code = 0xaa;
 const I32TruncUF64Code = 0xab;
 const I64TruncSF32Code = 0xae;
 const I64TruncUF32Code = 0xaf;
 const I64TruncSF64Code = 0xb0;
 const I64TruncUF64Code = 0xb1;
 
+const FirstInvalidOpcode = 0xc0;
+
 // DefinitionKind
 const FunctionCode     = 0x00;
 const TableCode        = 0x01;
 const MemoryCode       = 0x02;
 const GlobalCode       = 0x03;
 
 // ResizableFlags
 const HasMaximumFlag   = 0x1;
--- a/js/src/jit-test/tests/wasm/binary.js
+++ b/js/src/jit-test/tests/wasm/binary.js
@@ -249,65 +249,66 @@ function nameSection(elems) {
     return { name: userDefinedId, body };
 }
 
 function customSection(name, ...body) {
     return { name: userDefinedId, body: [...string(name), ...body] };
 }
 
 const v2vSig = {args:[], ret:VoidCode};
+const v2vSigSection = sigSection([v2vSig]);
 const i2vSig = {args:[I32Code], ret:VoidCode};
 const v2vBody = funcBody({locals:[], body:[]});
 
 assertErrorMessage(() => wasmEval(moduleWithSections([ {name: typeId, body: U32MAX_LEB } ])), CompileError, /too many signatures/);
 assertErrorMessage(() => wasmEval(moduleWithSections([ {name: typeId, body: [1, 0], } ])), CompileError, /expected function form/);
 assertErrorMessage(() => wasmEval(moduleWithSections([ {name: typeId, body: [1, FuncCode, ...U32MAX_LEB], } ])), CompileError, /too many arguments in signature/);
 
 assertThrowsInstanceOf(() => wasmEval(moduleWithSections([{name: typeId, body: [1]}])), CompileError);
 assertThrowsInstanceOf(() => wasmEval(moduleWithSections([{name: typeId, body: [1, 1, 0]}])), CompileError);
 
 wasmEval(moduleWithSections([sigSection([])]));
-wasmEval(moduleWithSections([sigSection([v2vSig])]));
+wasmEval(moduleWithSections([v2vSigSection]));
 wasmEval(moduleWithSections([sigSection([i2vSig])]));
 wasmEval(moduleWithSections([sigSection([v2vSig, i2vSig])]));
 
 assertErrorMessage(() => wasmEval(moduleWithSections([sigSection([{args:[], ret:100}])])), CompileError, /bad type/);
 assertErrorMessage(() => wasmEval(moduleWithSections([sigSection([{args:[100], ret:VoidCode}])])), CompileError, /bad type/);
 
 assertThrowsInstanceOf(() => wasmEval(moduleWithSections([sigSection([]), declSection([0])])), CompileError, /signature index out of range/);
-assertThrowsInstanceOf(() => wasmEval(moduleWithSections([sigSection([v2vSig]), declSection([1])])), CompileError, /signature index out of range/);
-assertErrorMessage(() => wasmEval(moduleWithSections([sigSection([v2vSig]), declSection([0])])), CompileError, /expected function bodies/);
-wasmEval(moduleWithSections([sigSection([v2vSig]), declSection([0]), bodySection([v2vBody])]));
+assertThrowsInstanceOf(() => wasmEval(moduleWithSections([v2vSigSection, declSection([1])])), CompileError, /signature index out of range/);
+assertErrorMessage(() => wasmEval(moduleWithSections([v2vSigSection, declSection([0])])), CompileError, /expected function bodies/);
+wasmEval(moduleWithSections([v2vSigSection, declSection([0]), bodySection([v2vBody])]));
 
-assertErrorMessage(() => wasmEval(moduleWithSections([sigSection([v2vSig]), declSection([0]), bodySection([v2vBody.concat(v2vBody)])])), CompileError, /byte size mismatch in code section/);
+assertErrorMessage(() => wasmEval(moduleWithSections([v2vSigSection, declSection([0]), bodySection([v2vBody.concat(v2vBody)])])), CompileError, /byte size mismatch in code section/);
 
-assertThrowsInstanceOf(() => wasmEval(moduleWithSections([sigSection([v2vSig]), {name: importId, body:[]}])), CompileError);
+assertThrowsInstanceOf(() => wasmEval(moduleWithSections([v2vSigSection, {name: importId, body:[]}])), CompileError);
 assertErrorMessage(() => wasmEval(moduleWithSections([importSection([{sigIndex:0, module:"a", func:"b"}])])), CompileError, /signature index out of range/);
-assertErrorMessage(() => wasmEval(moduleWithSections([sigSection([v2vSig]), importSection([{sigIndex:1, module:"a", func:"b"}])])), CompileError, /signature index out of range/);
-wasmEval(moduleWithSections([sigSection([v2vSig]), importSection([])]));
-wasmEval(moduleWithSections([sigSection([v2vSig]), importSection([{sigIndex:0, module:"a", func:""}])]), {a:{"":()=>{}}});
+assertErrorMessage(() => wasmEval(moduleWithSections([v2vSigSection, importSection([{sigIndex:1, module:"a", func:"b"}])])), CompileError, /signature index out of range/);
+wasmEval(moduleWithSections([v2vSigSection, importSection([])]));
+wasmEval(moduleWithSections([v2vSigSection, importSection([{sigIndex:0, module:"a", func:""}])]), {a:{"":()=>{}}});
 
 wasmEval(moduleWithSections([
-    sigSection([v2vSig]),
+    v2vSigSection,
     importSection([{sigIndex:0, module:"a", func:""}]),
     declSection([0]),
     bodySection([v2vBody])
 ]), {a:{"":()=>{}}});
 
 assertErrorMessage(() => wasmEval(moduleWithSections([ dataSection([{offset:1, elems:[]}]) ])), CompileError, /data segment requires a memory section/);
 
 wasmEval(moduleWithSections([tableSection(0)]));
 wasmEval(moduleWithSections([elemSection([])]));
 wasmEval(moduleWithSections([tableSection(0), elemSection([])]));
 wasmEval(moduleWithSections([tableSection(1), elemSection([{offset:1, elems:[]}])]));
 assertErrorMessage(() => wasmEval(moduleWithSections([tableSection(1), elemSection([{offset:0, elems:[0]}])])), CompileError, /table element out of range/);
-wasmEval(moduleWithSections([sigSection([v2vSig]), declSection([0]), tableSection(1), elemSection([{offset:0, elems:[0]}]), bodySection([v2vBody])]));
-wasmEval(moduleWithSections([sigSection([v2vSig]), declSection([0]), tableSection(2), elemSection([{offset:0, elems:[0,0]}]), bodySection([v2vBody])]));
-assertErrorMessage(() => wasmEval(moduleWithSections([sigSection([v2vSig]), declSection([0]), tableSection(2), elemSection([{offset:0, elems:[0,1]}]), bodySection([v2vBody])])), CompileError, /table element out of range/);
-wasmEval(moduleWithSections([sigSection([v2vSig]), declSection([0,0,0]), tableSection(4), elemSection([{offset:0, elems:[0,1,0,2]}]), bodySection([v2vBody, v2vBody, v2vBody])]));
+wasmEval(moduleWithSections([v2vSigSection, declSection([0]), tableSection(1), elemSection([{offset:0, elems:[0]}]), bodySection([v2vBody])]));
+wasmEval(moduleWithSections([v2vSigSection, declSection([0]), tableSection(2), elemSection([{offset:0, elems:[0,0]}]), bodySection([v2vBody])]));
+assertErrorMessage(() => wasmEval(moduleWithSections([v2vSigSection, declSection([0]), tableSection(2), elemSection([{offset:0, elems:[0,1]}]), bodySection([v2vBody])])), CompileError, /table element out of range/);
+wasmEval(moduleWithSections([v2vSigSection, declSection([0,0,0]), tableSection(4), elemSection([{offset:0, elems:[0,1,0,2]}]), bodySection([v2vBody, v2vBody, v2vBody])]));
 wasmEval(moduleWithSections([sigSection([v2vSig,i2vSig]), declSection([0,0,1]), tableSection(3), elemSection([{offset:0,elems:[0,1,2]}]), bodySection([v2vBody, v2vBody, v2vBody])]));
 
 function tableSection0() {
     var body = [];
     body.push(...varU32(0));           // number of tables
     return { name: tableId, body };
 }
 
@@ -344,59 +345,58 @@ function invalidMemorySection2() {
     return { name: memoryId, body };
 }
 
 wasmEval(moduleWithSections([memorySection0()]));
 assertErrorMessage(() => wasmEval(moduleWithSections([invalidMemorySection2()])), CompileError, /number of memories must be at most one/);
 
 // Test early 'end'
 const bodyMismatch = /function body length mismatch/;
-assertErrorMessage(() => wasmEval(moduleWithSections([sigSection([v2vSig]), declSection([0]), bodySection([funcBody({locals:[], body:[EndCode]})])])), CompileError, bodyMismatch);
-assertErrorMessage(() => wasmEval(moduleWithSections([sigSection([v2vSig]), declSection([0]), bodySection([funcBody({locals:[], body:[UnreachableCode,EndCode]})])])), CompileError, bodyMismatch);
-assertErrorMessage(() => wasmEval(moduleWithSections([sigSection([v2vSig]), declSection([0]), bodySection([funcBody({locals:[], body:[EndCode,UnreachableCode]})])])), CompileError, bodyMismatch);
+assertErrorMessage(() => wasmEval(moduleWithSections([v2vSigSection, declSection([0]), bodySection([funcBody({locals:[], body:[EndCode]})])])), CompileError, bodyMismatch);
+assertErrorMessage(() => wasmEval(moduleWithSections([v2vSigSection, declSection([0]), bodySection([funcBody({locals:[], body:[UnreachableCode,EndCode]})])])), CompileError, bodyMismatch);
+assertErrorMessage(() => wasmEval(moduleWithSections([v2vSigSection, declSection([0]), bodySection([funcBody({locals:[], body:[EndCode,UnreachableCode]})])])), CompileError, bodyMismatch);
 
 // Deep nesting shouldn't crash or even throw.
 var manyBlocks = [];
 for (var i = 0; i < 20000; i++)
     manyBlocks.push(BlockCode, VoidCode, EndCode);
-wasmEval(moduleWithSections([sigSection([v2vSig]), declSection([0]), bodySection([funcBody({locals:[], body:manyBlocks})])]));
+wasmEval(moduleWithSections([v2vSigSection, declSection([0]), bodySection([funcBody({locals:[], body:manyBlocks})])]));
 
 // Ignore errors in name section.
 var tooBigNameSection = {
     name: userDefinedId,
     body: [...string(nameName), ...varU32(Math.pow(2, 31))] // declare 2**31 functions.
 };
 wasmEval(moduleWithSections([tooBigNameSection]));
 
 // Skip custom sections before any expected section
 var customDefSec = customSection("wee", 42, 13);
-var sigSec = sigSection([v2vSig]);
 var declSec = declSection([0]);
 var bodySec = bodySection([v2vBody]);
 var nameSec = nameSection([{name:'hi'}]);
-wasmEval(moduleWithSections([customDefSec, sigSec, declSec, bodySec]));
-wasmEval(moduleWithSections([sigSec, customDefSec, declSec, bodySec]));
-wasmEval(moduleWithSections([sigSec, declSec, customDefSec, bodySec]));
-wasmEval(moduleWithSections([sigSec, declSec, bodySec, customDefSec]));
-wasmEval(moduleWithSections([customDefSec, customDefSec, sigSec, declSec, bodySec]));
-wasmEval(moduleWithSections([customDefSec, customDefSec, sigSec, customDefSec, declSec, customDefSec, bodySec]));
+wasmEval(moduleWithSections([customDefSec, v2vSigSection, declSec, bodySec]));
+wasmEval(moduleWithSections([v2vSigSection, customDefSec, declSec, bodySec]));
+wasmEval(moduleWithSections([v2vSigSection, declSec, customDefSec, bodySec]));
+wasmEval(moduleWithSections([v2vSigSection, declSec, bodySec, customDefSec]));
+wasmEval(moduleWithSections([customDefSec, customDefSec, v2vSigSection, declSec, bodySec]));
+wasmEval(moduleWithSections([customDefSec, customDefSec, v2vSigSection, customDefSec, declSec, customDefSec, bodySec]));
 
 // custom sections reflection:
 function checkCustomSection(buf, val) {
     assertEq(buf instanceof ArrayBuffer, true);
     assertEq(buf.byteLength, 1);
     assertEq(new Uint8Array(buf)[0], val);
 }
 var custom1 = customSection("one", 1);
 var custom2 = customSection("one", 2);
 var custom3 = customSection("two", 3);
 var custom4 = customSection("three", 4);
 var custom5 = customSection("three", 5);
 var custom6 = customSection("three", 6);
-var m = new Module(moduleWithSections([custom1, sigSec, custom2, declSec, custom3, bodySec, custom4, nameSec, custom5, custom6]));
+var m = new Module(moduleWithSections([custom1, v2vSigSection, custom2, declSec, custom3, bodySec, custom4, nameSec, custom5, custom6]));
 var arr = Module.customSections(m, "one");
 assertEq(arr.length, 2);
 checkCustomSection(arr[0], 1);
 checkCustomSection(arr[1], 2);
 var arr = Module.customSections(m, "two");
 assertEq(arr.length, 1);
 checkCustomSection(arr[0], 3);
 var arr = Module.customSections(m, "three");
@@ -407,16 +407,23 @@ checkCustomSection(arr[2], 6);
 var arr = Module.customSections(m, "name");
 assertEq(arr.length, 1);
 assertEq(arr[0].byteLength, nameSec.body.length - 5 /* 4name */);
 
 // Diagnose nonstandard block signature types.
 for (var bad of [0xff, 0, 1, 0x3f])
     assertErrorMessage(() => wasmEval(moduleWithSections([sigSection([v2vSig]), declSection([0]), bodySection([funcBody({locals:[], body:[BlockCode, bad, EndCode]})])])), CompileError, /invalid inline block type/);
 
+// Ensure all asm.js opcodes rejected
+for (var i = FirstInvalidOpcode; i <= 0xff; i++) {
+    var binary = moduleWithSections([v2vSigSection, declSection([0]), bodySection([funcBody({locals:[], body:[i]})])]);
+    assertErrorMessage(() => wasmEval(binary), CompileError, /unrecognized opcode/);
+    assertEq(WebAssembly.validate(binary), false);
+}
+
 // Checking stack trace.
 function runStackTraceTest(namesContent, expectedName) {
     var sections = [
         sigSection([v2vSig]),
         importSection([{sigIndex:0, module:"env", func:"callback"}]),
         declSection([0]),
         exportSection([{funcIndex:1, name: "run"}]),
         bodySection([funcBody({locals: [], body: [CallCode, varU32(0)]})]),
--- a/js/src/jit/arm/Assembler-arm.cpp
+++ b/js/src/jit/arm/Assembler-arm.cpp
@@ -666,21 +666,22 @@ Assembler::asmMergeWith(Assembler& other
     if (other.oom())
         return false;
     if (!AssemblerShared::asmMergeWith(size(), other))
         return false;
     return m_buffer.appendBuffer(other.m_buffer);
 }
 
 void
-Assembler::executableCopy(uint8_t* buffer)
+Assembler::executableCopy(uint8_t* buffer, bool flushICache)
 {
     MOZ_ASSERT(isFinished);
     m_buffer.executableCopy(buffer);
-    AutoFlushICache::setRange(uintptr_t(buffer), m_buffer.size());
+    if (flushICache)
+        AutoFlushICache::setRange(uintptr_t(buffer), m_buffer.size());
 }
 
 uint32_t
 Assembler::actualIndex(uint32_t idx_) const
 {
     ARMBuffer::PoolEntry pe(idx_);
     return m_buffer.poolEntryOffset(pe);
 }
--- a/js/src/jit/arm/Assembler-arm.h
+++ b/js/src/jit/arm/Assembler-arm.h
@@ -1407,17 +1407,16 @@ class Assembler : public AssemblerShared
         return StackPointer;
     }
 
   private:
     bool isFinished;
   public:
     void finish();
     bool asmMergeWith(Assembler& other);
-    void executableCopy(void* buffer);
     void copyJumpRelocationTable(uint8_t* dest);
     void copyDataRelocationTable(uint8_t* dest);
     void copyPreBarrierTable(uint8_t* dest);
 
     // Size of the instruction stream, in bytes, after pools are flushed.
     size_t size() const;
     // Size of the jump relocation table, in bytes.
     size_t jumpRelocationTableBytes() const;
@@ -1768,17 +1767,17 @@ class Assembler : public AssemblerShared
     void comment(const char* msg) {
 #ifdef JS_DISASM_ARM
         spew("; %s", msg);
 #endif
     }
 
     // Copy the assembly code to the given buffer, and perform any pending
     // relocations relying on the target address.
-    void executableCopy(uint8_t* buffer);
+    void executableCopy(uint8_t* buffer, bool flushICache = true);
 
     // Actual assembly emitting functions.
 
     // Since I can't think of a reasonable default for the mode, I'm going to
     // leave it as a required argument.
     void startDataTransferM(LoadStore ls, Register rm,
                             DTMMode mode, DTMWriteBack update = NoWriteBack,
                             Condition c = Always)
--- a/js/src/jit/arm64/Assembler-arm64.cpp
+++ b/js/src/jit/arm64/Assembler-arm64.cpp
@@ -122,17 +122,17 @@ Assembler::emitExtendedJumpTable()
 
     if (oom())
         return BufferOffset();
 
     return tableOffset;
 }
 
 void
-Assembler::executableCopy(uint8_t* buffer)
+Assembler::executableCopy(uint8_t* buffer, bool flushICache)
 {
     // Copy the code and all constant pools into the output buffer.
     armbuffer_.executableCopy(buffer);
 
     // Patch any relative jumps that target code outside the buffer.
     // The extended jump table may be used for distant jumps.
     for (size_t i = 0; i < pendingJumps_.length(); i++) {
         RelativePatch& rp = pendingJumps_[i];
@@ -156,17 +156,19 @@ Assembler::executableCopy(uint8_t* buffe
                 branch->SetImmPCOffsetTarget(entry->getLdr());
                 entry->data = target;
             }
         } else {
             // Currently a two-instruction call, it should be possible to optimize this
             // into a single instruction call + nop in some instances, but this will work.
         }
     }
-    AutoFlushICache::setRange(uintptr_t(buffer), armbuffer_.size());
+
+    if (flushICache)
+        AutoFlushICache::setRange(uintptr_t(buffer), armbuffer_.size());
 }
 
 BufferOffset
 Assembler::immPool(ARMRegister dest, uint8_t* value, vixl::LoadLiteralOp op, ARMBuffer::PoolEntry* pe)
 {
     uint32_t inst = op | Rt(dest);
     const size_t numInst = 1;
     const unsigned sizeOfPoolEntryInBytes = 4;
--- a/js/src/jit/arm64/Assembler-arm64.h
+++ b/js/src/jit/arm64/Assembler-arm64.h
@@ -191,17 +191,17 @@ class Assembler : public vixl::Assembler
     bool asmMergeWith(const Assembler& other) {
         MOZ_CRASH("NYI");
     }
     void trace(JSTracer* trc);
 
     // Emit the jump table, returning the BufferOffset to the first entry in the table.
     BufferOffset emitExtendedJumpTable();
     BufferOffset ExtendedJumpTable_;
-    void executableCopy(uint8_t* buffer);
+    void executableCopy(uint8_t* buffer, bool flushICache = true);
 
     BufferOffset immPool(ARMRegister dest, uint8_t* value, vixl::LoadLiteralOp op,
                          ARMBuffer::PoolEntry* pe = nullptr);
     BufferOffset immPool64(ARMRegister dest, uint64_t value, ARMBuffer::PoolEntry* pe = nullptr);
     BufferOffset immPool64Branch(RepatchLabel* label, ARMBuffer::PoolEntry* pe, vixl::Condition c);
     BufferOffset fImmPool(ARMFPRegister dest, uint8_t* value, vixl::LoadLiteralOp op);
     BufferOffset fImmPool64(ARMFPRegister dest, double value);
     BufferOffset fImmPool32(ARMFPRegister dest, float value);
--- a/js/src/jit/mips-shared/Assembler-mips-shared.h
+++ b/js/src/jit/mips-shared/Assembler-mips-shared.h
@@ -923,17 +923,17 @@ class AssemblerMIPSShared : public Assem
         return StackPointer;
     }
 
   protected:
     bool isFinished;
   public:
     void finish();
     bool asmMergeWith(const AssemblerMIPSShared& other);
-    void executableCopy(void* buffer);
+    void executableCopy(void* buffer, bool flushICache = true);
     void copyJumpRelocationTable(uint8_t* dest);
     void copyDataRelocationTable(uint8_t* dest);
     void copyPreBarrierTable(uint8_t* dest);
 
     // Size of the instruction stream, in bytes.
     size_t size() const;
     // Size of the jump relocation table, in bytes.
     size_t jumpRelocationTableBytes() const;
--- a/js/src/jit/mips32/Assembler-mips32.cpp
+++ b/js/src/jit/mips32/Assembler-mips32.cpp
@@ -153,30 +153,31 @@ jit::PatchBackedge(CodeLocationJump& jum
             Instruction* lui = &branch[4];
             AssemblerMIPSShared::UpdateLuiOriValue(lui, lui->next(), targetAddr);
             branch->setBOffImm16(BOffImm16(4 * sizeof(uint32_t)));
         }
     }
 }
 
 void
-Assembler::executableCopy(uint8_t* buffer)
+Assembler::executableCopy(uint8_t* buffer, bool flushICache)
 {
     MOZ_ASSERT(isFinished);
     m_buffer.executableCopy(buffer);
 
     // Patch all long jumps during code copy.
     for (size_t i = 0; i < longJumps_.length(); i++) {
         Instruction* inst1 = (Instruction*) ((uint32_t)buffer + longJumps_[i]);
 
         uint32_t value = Assembler::ExtractLuiOriValue(inst1, inst1->next());
         AssemblerMIPSShared::UpdateLuiOriValue(inst1, inst1->next(), (uint32_t)buffer + value);
     }
 
-    AutoFlushICache::setRange(uintptr_t(buffer), m_buffer.size());
+    if (flushICache)
+        AutoFlushICache::setRange(uintptr_t(buffer), m_buffer.size());
 }
 
 uintptr_t
 Assembler::GetPointer(uint8_t* instPtr)
 {
     Instruction* inst = (Instruction*)instPtr;
     return Assembler::ExtractLuiOriValue(inst, inst->next());
 }
--- a/js/src/jit/mips32/Assembler-mips32.h
+++ b/js/src/jit/mips32/Assembler-mips32.h
@@ -151,17 +151,17 @@ class Assembler : public AssemblerMIPSSh
 
     static void TraceJumpRelocations(JSTracer* trc, JitCode* code, CompactBufferReader& reader);
     static void TraceDataRelocations(JSTracer* trc, JitCode* code, CompactBufferReader& reader);
 
     void bind(InstImm* inst, uintptr_t branch, uintptr_t target);
 
     // Copy the assembly code to the given buffer, and perform any pending
     // relocations relying on the target address.
-    void executableCopy(uint8_t* buffer);
+    void executableCopy(uint8_t* buffer, bool flushICache = true);
 
     static uint32_t PatchWrite_NearCallSize();
 
     static uint32_t ExtractLuiOriValue(Instruction* inst0, Instruction* inst1);
     static void WriteLuiOriInstructions(Instruction* inst, Instruction* inst1,
                                         Register reg, uint32_t value);
 
     static void PatchWrite_NearCall(CodeLocationLabel start, CodeLocationLabel toCall);
--- a/js/src/jit/mips64/Assembler-mips64.cpp
+++ b/js/src/jit/mips64/Assembler-mips64.cpp
@@ -121,30 +121,31 @@ jit::PatchBackedge(CodeLocationJump& jum
             Assembler::UpdateLoad64Value(inst, targetAddr);
             // Jump to first ori of interrupt loop.
             branch->setBOffImm16(BOffImm16(6 * sizeof(uint32_t)));
         }
     }
 }
 
 void
-Assembler::executableCopy(uint8_t* buffer)
+Assembler::executableCopy(uint8_t* buffer, bool flushICache = true)
 {
     MOZ_ASSERT(isFinished);
     m_buffer.executableCopy(buffer);
 
     // Patch all long jumps during code copy.
     for (size_t i = 0; i < longJumps_.length(); i++) {
         Instruction* inst = (Instruction*) ((uintptr_t)buffer + longJumps_[i]);
 
         uint64_t value = Assembler::ExtractLoad64Value(inst);
         Assembler::UpdateLoad64Value(inst, (uint64_t)buffer + value);
     }
 
-    AutoFlushICache::setRange(uintptr_t(buffer), m_buffer.size());
+    if (flushICache)
+        AutoFlushICache::setRange(uintptr_t(buffer), m_buffer.size());
 }
 
 uintptr_t
 Assembler::GetPointer(uint8_t* instPtr)
 {
     Instruction* inst = (Instruction*)instPtr;
     return Assembler::ExtractLoad64Value(inst);
 }
--- a/js/src/jit/mips64/Assembler-mips64.h
+++ b/js/src/jit/mips64/Assembler-mips64.h
@@ -150,17 +150,17 @@ class Assembler : public AssemblerMIPSSh
 
     static void TraceJumpRelocations(JSTracer* trc, JitCode* code, CompactBufferReader& reader);
     static void TraceDataRelocations(JSTracer* trc, JitCode* code, CompactBufferReader& reader);
 
     void bind(InstImm* inst, uintptr_t branch, uintptr_t target);
 
     // Copy the assembly code to the given buffer, and perform any pending
     // relocations relying on the target address.
-    void executableCopy(uint8_t* buffer);
+    void executableCopy(uint8_t* buffer, bool flushICache = true);
 
     static uint32_t PatchWrite_NearCallSize();
 
     static uint64_t ExtractLoad64Value(Instruction* inst0);
     static void UpdateLoad64Value(Instruction* inst0, uint64_t value);
     static void WriteLoad64Instructions(Instruction* inst0, Register reg, uint64_t value);
 
 
--- a/js/src/jit/none/MacroAssembler-none.h
+++ b/js/src/jit/none/MacroAssembler-none.h
@@ -192,17 +192,17 @@ class MacroAssemblerNone : public Assemb
     static void TraceDataRelocations(JSTracer*, JitCode*, CompactBufferReader&) { MOZ_CRASH(); }
 
     static bool SupportsFloatingPoint() { return false; }
     static bool SupportsSimd() { return false; }
     static bool SupportsUnalignedAccesses() { return false; }
 
     static bool HasRoundInstruction(RoundingMode) { return false; }
 
-    void executableCopy(void*) { MOZ_CRASH(); }
+    void executableCopy(void*, bool) { MOZ_CRASH(); }
     void copyJumpRelocationTable(uint8_t*) { MOZ_CRASH(); }
     void copyDataRelocationTable(uint8_t*) { MOZ_CRASH(); }
     void copyPreBarrierTable(uint8_t*) { MOZ_CRASH(); }
     void processCodeLabels(uint8_t*) { MOZ_CRASH(); }
 
     void flushBuffer() { MOZ_CRASH(); }
 
     template <typename T> void bind(T) { MOZ_CRASH(); }
--- a/js/src/jit/x64/Assembler-x64.cpp
+++ b/js/src/jit/x64/Assembler-x64.cpp
@@ -209,17 +209,17 @@ Assembler::finish()
         MOZ_ASSERT_IF(!masm.oom(), masm.size() - oldSize == 8);
         masm.immediate64(0);
         MOZ_ASSERT_IF(!masm.oom(), masm.size() - oldSize == SizeOfExtendedJump);
         MOZ_ASSERT_IF(!masm.oom(), masm.size() - oldSize == SizeOfJumpTableEntry);
     }
 }
 
 void
-Assembler::executableCopy(uint8_t* buffer)
+Assembler::executableCopy(uint8_t* buffer, bool flushICache)
 {
     AssemblerX86Shared::executableCopy(buffer);
 
     for (size_t i = 0; i < jumps_.length(); i++) {
         RelativePatch& rp = jumps_[i];
         uint8_t* src = buffer + rp.offset;
         if (!rp.target) {
             // The patch target is nullptr for jumps that have been linked to
--- a/js/src/jit/x64/Assembler-x64.h
+++ b/js/src/jit/x64/Assembler-x64.h
@@ -311,17 +311,17 @@ class Assembler : public AssemblerX86Sha
     static void TraceJumpRelocations(JSTracer* trc, JitCode* code, CompactBufferReader& reader);
 
     // The buffer is about to be linked, make sure any constant pools or excess
     // bookkeeping has been flushed to the instruction stream.
     void finish();
 
     // Copy the assembly code to the given buffer, and perform any pending
     // relocations relying on the target address.
-    void executableCopy(uint8_t* buffer);
+    void executableCopy(uint8_t* buffer, bool flushICache = true);
 
     // Actual assembly emitting functions.
 
     void push(const ImmGCPtr ptr) {
         movq(ptr, ScratchReg);
         push(ScratchReg);
     }
     void push(const ImmWord ptr) {
--- a/js/src/jit/x86/Assembler-x86.cpp
+++ b/js/src/jit/x86/Assembler-x86.cpp
@@ -50,17 +50,17 @@ ABIArgGenerator::next(MIRType type)
         break;
       default:
         MOZ_CRASH("Unexpected argument type");
     }
     return current_;
 }
 
 void
-Assembler::executableCopy(uint8_t* buffer)
+Assembler::executableCopy(uint8_t* buffer, bool flushICache)
 {
     AssemblerX86Shared::executableCopy(buffer);
 
     for (size_t i = 0; i < jumps_.length(); i++) {
         RelativePatch& rp = jumps_[i];
         X86Encoding::SetRel32(buffer + rp.offset, rp.target);
     }
 }
--- a/js/src/jit/x86/Assembler-x86.h
+++ b/js/src/jit/x86/Assembler-x86.h
@@ -227,17 +227,17 @@ class Assembler : public AssemblerX86Sha
     using AssemblerX86Shared::call;
     using AssemblerX86Shared::push;
     using AssemblerX86Shared::pop;
 
     static void TraceJumpRelocations(JSTracer* trc, JitCode* code, CompactBufferReader& reader);
 
     // Copy the assembly code to the given buffer, and perform any pending
     // relocations relying on the target address.
-    void executableCopy(uint8_t* buffer);
+    void executableCopy(uint8_t* buffer, bool flushICache = true);
 
     // Actual assembly emitting functions.
 
     void push(ImmGCPtr ptr) {
         masm.push_i32(int32_t(ptr.value));
         writeDataRelocation(ptr);
     }
     void push(const ImmWord imm) {
--- a/js/src/wasm/WasmBaselineCompile.cpp
+++ b/js/src/wasm/WasmBaselineCompile.cpp
@@ -509,23 +509,22 @@ class BaseCompiler
         uint32_t framePushed;           // From masm
         uint32_t stackSize;             // Value stack height
         BCESet bceSafeOnEntry;          // Bounds check info flowing into the item
         BCESet bceSafeOnExit;           // Bounds check info flowing out of the item
         bool deadOnArrival;             // deadCode_ was set on entry to the region
         bool deadThenBranch;            // deadCode_ was set on exit from "then"
     };
 
-    struct BaseCompilePolicy : OpIterPolicy
+    struct BaseCompilePolicy
     {
-        static const bool Output = true;
-
         // The baseline compiler tracks values on a stack of its own -- it
         // needs to scan that stack for spilling -- and thus has no need
         // for the values maintained by the iterator.
+        typedef Nothing Value;
 
         // The baseline compiler uses the iterator's control stack, attaching
         // its own control information.
         typedef Control ControlItem;
     };
 
     typedef OpIter<BaseCompilePolicy> BaseOpIter;
 
@@ -536,18 +535,18 @@ class BaseCompiler
     // The baseline compiler will use OOL code more sparingly than
     // Baldr since our code is not high performance and frills like
     // code density and branch prediction friendliness will be less
     // important.
 
     class OutOfLineCode : public TempObject
     {
       private:
-        Label entry_;
-        Label rejoin_;
+        NonAssertingLabel entry_;
+        NonAssertingLabel rejoin_;
         uint32_t framePushed_;
 
       public:
         OutOfLineCode() : framePushed_(UINT32_MAX) {}
 
         Label* entry() { return &entry_; }
         Label* rejoin() { return &rejoin_; }
 
@@ -601,18 +600,18 @@ class BaseCompiler
     bool                        deadCode_;       // Flag indicating we should decode & discard the opcode
     bool                        debugEnabled_;
     BCESet                      bceSafe_;        // Locals that have been bounds checked and not updated since
     ValTypeVector               SigI64I64_;
     ValTypeVector               SigD_;
     ValTypeVector               SigF_;
     MIRTypeVector               SigPI_;
     MIRTypeVector               SigP_;
-    Label                       returnLabel_;
-    Label                       stackOverflowLabel_;
+    NonAssertingLabel           returnLabel_;
+    NonAssertingLabel           stackOverflowLabel_;
     CodeOffset                  stackAddOffset_;
 
     LatentOp                    latentOp_;       // Latent operation for branch (seen next)
     ValType                     latentType_;     // Operand type, if latentOp_ is true
     Assembler::Condition        latentIntCmp_;   // Comparison operator, if latentOp_ == Compare, int types
     Assembler::DoubleCondition  latentDoubleCmp_;// Comparison operator, if latentOp_ == Compare, float types
 
     FuncOffsets                 offsets_;
@@ -6861,16 +6860,21 @@ BaseCompiler::emitCurrentMemory()
     pushReturned(baselineCall, ExprType::I32);
 
     return true;
 }
 
 bool
 BaseCompiler::emitBody()
 {
+    if (!iter_.readFunctionStart(func_.sig().ret()))
+        return false;
+
+    initControl(controlItem());
+
     uint32_t overhead = 0;
 
     for (;;) {
 
         Nothing unused_a, unused_b;
 
 #ifdef DEBUG
         performRegisterLeakCheck();
@@ -6894,19 +6898,19 @@ BaseCompiler::emitBody()
 
 #define emitCalloutConversionOOM(doEmit, symbol, inType, outType) \
         iter_.readConversion(inType, outType, &unused_a) && \
             (deadCode_ || doEmit(symbol, inType, outType))
 
 #define emitIntDivCallout(doEmit, symbol, type) \
         iter_.readBinary(type, &unused_a, &unused_b) && (deadCode_ || (doEmit(symbol, type), true))
 
-#define CHECK(E)      if (!(E)) goto done
+#define CHECK(E)      if (!(E)) return false
 #define NEXT()        continue
-#define CHECK_NEXT(E) if (!(E)) goto done; continue
+#define CHECK_NEXT(E) if (!(E)) return false; continue
 
         // TODO / EVALUATE (bug 1316845): Not obvious that this attempt at
         // reducing overhead is really paying off relative to making the check
         // every iteration.
 
         if (overhead == 0) {
             // Check every 50 expressions -- a happy medium between
             // memory usage and checking overhead.
@@ -6918,49 +6922,54 @@ BaseCompiler::emitBody()
 
             // The pushiest opcode is LOOP, which pushes two values
             // per instance.
             CHECK(stk_.reserve(stk_.length() + overhead * 2));
         }
 
         overhead--;
 
-        if (done())
-            return true;
-
         uint16_t op;
         CHECK(iter_.readOp(&op));
 
         // When debugEnabled_, every operator has breakpoint site but Op::End.
         if (debugEnabled_ && op != (uint16_t)Op::End) {
             // TODO sync only registers that can be clobbered by the exit
             // prologue/epilogue or disable these registers for use in
             // baseline compiler when debugEnabled_ is set.
             sync();
 
             insertBreakablePoint(CallSiteDesc::Breakpoint);
         }
 
         switch (op) {
+          case uint16_t(Op::End):
+            if (!emitEnd())
+                return false;
+
+            if (iter_.controlStackEmpty()) {
+                if (!deadCode_)
+                    doReturn(func_.sig().ret(), PopStack(false));
+                return iter_.readFunctionEnd(iter_.end());
+            }
+            NEXT();
+
           // Control opcodes
           case uint16_t(Op::Nop):
-            CHECK(iter_.readNop());
-            NEXT();
+            CHECK_NEXT(iter_.readNop());
           case uint16_t(Op::Drop):
             CHECK_NEXT(emitDrop());
           case uint16_t(Op::Block):
             CHECK_NEXT(emitBlock());
           case uint16_t(Op::Loop):
             CHECK_NEXT(emitLoop());
           case uint16_t(Op::If):
             CHECK_NEXT(emitIf());
           case uint16_t(Op::Else):
             CHECK_NEXT(emitElse());
-          case uint16_t(Op::End):
-            CHECK_NEXT(emitEnd());
           case uint16_t(Op::Br):
             CHECK_NEXT(emitBr());
           case uint16_t(Op::BrIf):
             CHECK_NEXT(emitBrIf());
           case uint16_t(Op::BrTable):
             CHECK_NEXT(emitBrTable());
           case uint16_t(Op::Return):
             CHECK_NEXT(emitReturn());
@@ -7387,165 +7396,50 @@ BaseCompiler::emitBody()
             CHECK_NEXT(emitComparison(emitCompareF64, ValType::F64, Assembler::DoubleLessThan));
           case uint16_t(Op::F64Le):
             CHECK_NEXT(emitComparison(emitCompareF64, ValType::F64, Assembler::DoubleLessThanOrEqual));
           case uint16_t(Op::F64Gt):
             CHECK_NEXT(emitComparison(emitCompareF64, ValType::F64, Assembler::DoubleGreaterThan));
           case uint16_t(Op::F64Ge):
             CHECK_NEXT(emitComparison(emitCompareF64, ValType::F64, Assembler::DoubleGreaterThanOrEqual));
 
-          // asm.js
-          case uint16_t(Op::TeeGlobal):
-          case uint16_t(Op::I32Min):
-          case uint16_t(Op::I32Max):
-          case uint16_t(Op::I32Neg):
-          case uint16_t(Op::I32BitNot):
-          case uint16_t(Op::I32Abs):
-          case uint16_t(Op::F32TeeStoreF64):
-          case uint16_t(Op::F64TeeStoreF32):
-          case uint16_t(Op::I32TeeStore8):
-          case uint16_t(Op::I32TeeStore16):
-          case uint16_t(Op::I32TeeStore):
-          case uint16_t(Op::I64TeeStore8):
-          case uint16_t(Op::I64TeeStore16):
-          case uint16_t(Op::I64TeeStore32):
-          case uint16_t(Op::I64TeeStore):
-          case uint16_t(Op::F32TeeStore):
-          case uint16_t(Op::F64TeeStore):
-          case uint16_t(Op::F64Mod):
-          case uint16_t(Op::F64Sin):
-          case uint16_t(Op::F64Cos):
-          case uint16_t(Op::F64Tan):
-          case uint16_t(Op::F64Asin):
-          case uint16_t(Op::F64Acos):
-          case uint16_t(Op::F64Atan):
-          case uint16_t(Op::F64Exp):
-          case uint16_t(Op::F64Log):
-          case uint16_t(Op::F64Pow):
-          case uint16_t(Op::F64Atan2):
-          case uint16_t(Op::OldCallIndirect):
-            MOZ_CRASH("Unimplemented Asm.JS");
-
-          // SIMD
-#define CASE(TYPE, OP, SIGN) \
-          case uint16_t(Op::TYPE##OP): \
-            MOZ_CRASH("Unimplemented SIMD");
-#define I8x16CASE(OP) CASE(I8x16, OP, SimdSign::Signed)
-#define I16x8CASE(OP) CASE(I16x8, OP, SimdSign::Signed)
-#define I32x4CASE(OP) CASE(I32x4, OP, SimdSign::Signed)
-#define F32x4CASE(OP) CASE(F32x4, OP, SimdSign::NotApplicable)
-#define B8x16CASE(OP) CASE(B8x16, OP, SimdSign::NotApplicable)
-#define B16x8CASE(OP) CASE(B16x8, OP, SimdSign::NotApplicable)
-#define B32x4CASE(OP) CASE(B32x4, OP, SimdSign::NotApplicable)
-#define ENUMERATE(TYPE, FORALL, DO) \
-          case uint16_t(Op::TYPE##Constructor): \
-            FORALL(DO)
-
-          ENUMERATE(I8x16, FORALL_INT8X16_ASMJS_OP, I8x16CASE)
-          ENUMERATE(I16x8, FORALL_INT16X8_ASMJS_OP, I16x8CASE)
-          ENUMERATE(I32x4, FORALL_INT32X4_ASMJS_OP, I32x4CASE)
-          ENUMERATE(F32x4, FORALL_FLOAT32X4_ASMJS_OP, F32x4CASE)
-          ENUMERATE(B8x16, FORALL_BOOL_SIMD_OP, B8x16CASE)
-          ENUMERATE(B16x8, FORALL_BOOL_SIMD_OP, B16x8CASE)
-          ENUMERATE(B32x4, FORALL_BOOL_SIMD_OP, B32x4CASE)
-
-#undef CASE
-#undef I8x16CASE
-#undef I16x8CASE
-#undef I32x4CASE
-#undef F32x4CASE
-#undef B8x16CASE
-#undef B16x8CASE
-#undef B32x4CASE
-#undef ENUMERATE
-
-          case uint16_t(Op::I8x16Const):
-          case uint16_t(Op::I16x8Const):
-          case uint16_t(Op::I32x4Const):
-          case uint16_t(Op::F32x4Const):
-          case uint16_t(Op::B8x16Const):
-          case uint16_t(Op::B16x8Const):
-          case uint16_t(Op::B32x4Const):
-          case uint16_t(Op::I32x4shiftRightByScalarU):
-          case uint16_t(Op::I8x16addSaturateU):
-          case uint16_t(Op::I8x16subSaturateU):
-          case uint16_t(Op::I8x16shiftRightByScalarU):
-          case uint16_t(Op::I8x16lessThanU):
-          case uint16_t(Op::I8x16lessThanOrEqualU):
-          case uint16_t(Op::I8x16greaterThanU):
-          case uint16_t(Op::I8x16greaterThanOrEqualU):
-          case uint16_t(Op::I8x16extractLaneU):
-          case uint16_t(Op::I16x8addSaturateU):
-          case uint16_t(Op::I16x8subSaturateU):
-          case uint16_t(Op::I16x8shiftRightByScalarU):
-          case uint16_t(Op::I16x8lessThanU):
-          case uint16_t(Op::I16x8lessThanOrEqualU):
-          case uint16_t(Op::I16x8greaterThanU):
-          case uint16_t(Op::I16x8greaterThanOrEqualU):
-          case uint16_t(Op::I16x8extractLaneU):
-          case uint16_t(Op::I32x4lessThanU):
-          case uint16_t(Op::I32x4lessThanOrEqualU):
-          case uint16_t(Op::I32x4greaterThanU):
-          case uint16_t(Op::I32x4greaterThanOrEqualU):
-          case uint16_t(Op::I32x4fromFloat32x4U):
-            MOZ_CRASH("Unimplemented SIMD");
-
-          // Atomics
-          case uint16_t(Op::I32AtomicsLoad):
-          case uint16_t(Op::I32AtomicsStore):
-          case uint16_t(Op::I32AtomicsBinOp):
-          case uint16_t(Op::I32AtomicsCompareExchange):
-          case uint16_t(Op::I32AtomicsExchange):
-            MOZ_CRASH("Unimplemented Atomics");
-
           // Memory Related
           case uint16_t(Op::GrowMemory):
             CHECK_NEXT(emitGrowMemory());
           case uint16_t(Op::CurrentMemory):
             CHECK_NEXT(emitCurrentMemory());
+
+          default:
+            return iter_.unrecognizedOpcode(op);
         }
 
-        MOZ_CRASH("unexpected wasm opcode");
-
 #undef CHECK
 #undef NEXT
 #undef CHECK_NEXT
 #undef emitBinary
 #undef emitUnary
 #undef emitComparison
 #undef emitConversion
 #undef emitConversionOOM
 #undef emitCalloutConversionOOM
-    }
-
-done:
-    return false;
+
+        MOZ_CRASH("unreachable");
+    }
+
+    MOZ_CRASH("unreachable");
 }
 
 bool
 BaseCompiler::emitFunction()
 {
-    const Sig& sig = func_.sig();
-
-    if (!iter_.readFunctionStart(sig.ret()))
-        return false;
-
     beginFunction();
 
-    initControl(controlItem());
-
     if (!emitBody())
         return false;
 
-    if (!deadCode_)
-        doReturn(sig.ret(), PopStack(false));
-
-    if (!iter_.readFunctionEnd())
-        return false;
-
     if (!endFunction())
         return false;
 
     return true;
 }
 
 BaseCompiler::BaseCompiler(const ModuleEnvironment& env,
                            Decoder& decoder,
@@ -7712,25 +7606,19 @@ js::wasm::BaselineCanCompile()
 
 bool
 js::wasm::BaselineCompileFunction(CompileTask* task, FuncCompileUnit* unit, UniqueChars *error)
 {
     MOZ_ASSERT(task->mode() == CompileMode::Baseline);
     MOZ_ASSERT(task->env().kind == ModuleKind::Wasm);
 
     const FuncBytes& func = unit->func();
-    uint32_t bodySize = func.bytes().length();
 
     Decoder d(func.bytes().begin(), func.bytes().end(), func.lineOrBytecode(), error);
 
-    if (!ValidateFunctionBody(task->env(), func.index(), bodySize, d))
-        return false;
-
-    d.rollbackPosition(d.begin());
-
     // Build the local types vector.
 
     ValTypeVector locals;
     if (!locals.appendAll(func.sig().args()))
         return false;
     if (!DecodeLocalEntries(d, task->env().kind, &locals))
         return false;
 
--- a/js/src/wasm/WasmBinaryIterator.h
+++ b/js/src/wasm/WasmBinaryIterator.h
@@ -287,155 +287,97 @@ class TypeAndValue<Nothing>
     TypeAndValue(ValType type, Nothing value) : type_(ToStackType(type)) {}
 
     StackType type() const { return type_; }
     StackType& typeRef() { return type_; }
     Nothing value() const { return Nothing(); }
     void setValue(Nothing value) {}
 };
 
-// A policy class for configuring OpIter. Clients can use this as a
-// base class, and override the behavior as needed.
-struct OpIterPolicy
-{
-    // Should the iterator perform validation, such as type checking and
-    // validity checking?
-    static const bool Validate = false;
-
-    // Should the iterator produce output values?
-    static const bool Output = false;
-
-    // These members allow clients to add additional information to the value
-    // and control stacks, respectively. Using Nothing means that no additional
-    // field is added.
-    typedef Nothing Value;
-    typedef Nothing ControlItem;
-};
-
 // An iterator over the bytes of a function body. It performs validation
-// (if Policy::Validate is true) and unpacks the data into a usable form.
+// and unpacks the data into a usable form.
 //
 // The MOZ_STACK_CLASS attribute here is because of the use of DebugOnly.
 // There's otherwise nothing inherent in this class which would require
 // it to be used on the stack.
 template <typename Policy>
 class MOZ_STACK_CLASS OpIter : private Policy
 {
-    static const bool Validate = Policy::Validate;
-    static const bool Output = Policy::Output;
     typedef typename Policy::Value Value;
     typedef typename Policy::ControlItem ControlItem;
 
     Decoder& d_;
     const ModuleEnvironment& env_;
 
     Vector<TypeAndValue<Value>, 8, SystemAllocPolicy> valueStack_;
     Vector<ControlStackEntry<ControlItem>, 8, SystemAllocPolicy> controlStack_;
 
     DebugOnly<Op> op_;
     size_t offsetOfLastReadOp_;
 
     MOZ_MUST_USE bool readFixedU8(uint8_t* out) {
-        if (Validate)
-            return d_.readFixedU8(out);
-        *out = d_.uncheckedReadFixedU8();
-        return true;
+        return d_.readFixedU8(out);
     }
     MOZ_MUST_USE bool readFixedU32(uint32_t* out) {
-        if (Validate)
-            return d_.readFixedU32(out);
-        *out = d_.uncheckedReadFixedU32();
-        return true;
+        return d_.readFixedU32(out);
     }
     MOZ_MUST_USE bool readVarS32(int32_t* out) {
-        if (Validate)
-            return d_.readVarS32(out);
-        *out = d_.uncheckedReadVarS32();
-        return true;
+        return d_.readVarS32(out);
     }
     MOZ_MUST_USE bool readVarU32(uint32_t* out) {
-        if (Validate)
-            return d_.readVarU32(out);
-        *out = d_.uncheckedReadVarU32();
-        return true;
+        return d_.readVarU32(out);
     }
     MOZ_MUST_USE bool readVarS64(int64_t* out) {
-        if (Validate)
-            return d_.readVarS64(out);
-        *out = d_.uncheckedReadVarS64();
-        return true;
+        return d_.readVarS64(out);
     }
     MOZ_MUST_USE bool readVarU64(uint64_t* out) {
-        if (Validate)
-            return d_.readVarU64(out);
-        *out = d_.uncheckedReadVarU64();
-        return true;
+        return d_.readVarU64(out);
     }
     MOZ_MUST_USE bool readFixedF32(float* out) {
-        if (Validate)
-            return d_.readFixedF32(out);
-        d_.uncheckedReadFixedF32(out);
-        return true;
+        return d_.readFixedF32(out);
     }
     MOZ_MUST_USE bool readFixedF64(double* out) {
-        if (Validate)
-            return d_.readFixedF64(out);
-        d_.uncheckedReadFixedF64(out);
-        return true;
+        return d_.readFixedF64(out);
     }
     MOZ_MUST_USE bool readFixedI8x16(I8x16* out) {
-        if (Validate)
-            return d_.readFixedI8x16(out);
-        d_.uncheckedReadFixedI8x16(out);
-        return true;
+        return d_.readFixedI8x16(out);
     }
     MOZ_MUST_USE bool readFixedI16x8(I16x8* out) {
-        if (Validate)
-            return d_.readFixedI16x8(out);
-        d_.uncheckedReadFixedI16x8(out);
-        return true;
+        return d_.readFixedI16x8(out);
     }
     MOZ_MUST_USE bool readFixedI32x4(I32x4* out) {
-        if (Validate)
-            return d_.readFixedI32x4(out);
-        d_.uncheckedReadFixedI32x4(out);
-        return true;
+        return d_.readFixedI32x4(out);
     }
     MOZ_MUST_USE bool readFixedF32x4(F32x4* out) {
-        if (Validate)
-            return d_.readFixedF32x4(out);
-        d_.uncheckedReadFixedF32x4(out);
-        return true;
+        return d_.readFixedF32x4(out);
     }
 
     MOZ_MUST_USE bool readAtomicViewType(Scalar::Type* viewType) {
         uint8_t x;
         if (!readFixedU8(&x))
             return fail("unable to read atomic view");
-        if (Validate && x >= Scalar::MaxTypedArrayViewType)
+        if (x >= Scalar::MaxTypedArrayViewType)
             return fail("invalid atomic view type");
         *viewType = Scalar::Type(x);
         return true;
     }
 
     MOZ_MUST_USE bool readAtomicBinOpOp(jit::AtomicOp* op) {
         uint8_t x;
         if (!readFixedU8(&x))
             return fail("unable to read atomic opcode");
-        if (Validate) {
-            switch (x) {
-              case jit::AtomicFetchAddOp:
-              case jit::AtomicFetchSubOp:
-              case jit::AtomicFetchAndOp:
-              case jit::AtomicFetchOrOp:
-              case jit::AtomicFetchXorOp:
-                break;
-              default:
-                return fail("unrecognized atomic binop");
-            }
+        switch (x) {
+          case jit::AtomicFetchAddOp:
+          case jit::AtomicFetchSubOp:
+          case jit::AtomicFetchAndOp:
+          case jit::AtomicFetchOrOp:
+          case jit::AtomicFetchXorOp:
+            break;
+          default:
+            return fail("unrecognized atomic binop");
         }
         *op = jit::AtomicOp(x);
         return true;
     }
 
     MOZ_MUST_USE bool readLinearMemoryAddress(uint32_t byteSize, LinearMemoryAddress<Value>* addr);
     MOZ_MUST_USE bool readBlockType(ExprType* expr);
     MOZ_MUST_USE bool popCallArgs(const ValTypeVector& expectedTypes, Vector<Value, 8, SystemAllocPolicy>* values);
@@ -503,16 +445,21 @@ class MOZ_STACK_CLASS OpIter : private P
         return TrapOffset(errorOffset());
     }
 
     // Test whether the iterator has reached the end of the buffer.
     bool done() const {
         return d_.done();
     }
 
+    // Return a pointer to the end of the buffer being decoded by this iterator.
+    const uint8_t* end() const {
+        return d_.end();
+    }
+
     // Report a general failure.
     MOZ_MUST_USE bool fail(const char* msg) MOZ_COLD;
 
     // Report an unrecognized opcode.
     MOZ_MUST_USE bool unrecognizedOpcode(uint32_t expr) MOZ_COLD;
 
     // Return whether the innermost block has a polymorphic base of its stack.
     // Ideally this accessor would be removed; consider using something else.
@@ -520,17 +467,17 @@ class MOZ_STACK_CLASS OpIter : private P
         return !controlStack_.empty() && controlStack_.back().polymorphicBase();
     }
 
     // ------------------------------------------------------------------------
     // Decoding and validation interface.
 
     MOZ_MUST_USE bool readOp(uint16_t* op);
     MOZ_MUST_USE bool readFunctionStart(ExprType ret);
-    MOZ_MUST_USE bool readFunctionEnd();
+    MOZ_MUST_USE bool readFunctionEnd(const uint8_t* bodyEnd);
     MOZ_MUST_USE bool readReturn(Value* value);
     MOZ_MUST_USE bool readBlock();
     MOZ_MUST_USE bool readLoop();
     MOZ_MUST_USE bool readIf(Value* condition);
     MOZ_MUST_USE bool readElse(ExprType* thenType, Value* thenValue);
     MOZ_MUST_USE bool readEnd(LabelKind* kind, ExprType* type, Value* value);
     void popEnd();
     MOZ_MUST_USE bool readBr(uint32_t* relativeDepth, ExprType* type, Value* value);
@@ -678,37 +625,32 @@ OpIter<Policy>::popAnyType(StackType* ty
     ControlStackEntry<ControlItem>& block = controlStack_.back();
 
     MOZ_ASSERT(valueStack_.length() >= block.valueStackStart());
     if (MOZ_UNLIKELY(valueStack_.length() == block.valueStackStart())) {
         // If the base of this block's stack is polymorphic, then we can pop a
         // dummy value of any type; it won't be used since we're in unreachable code.
         if (block.polymorphicBase()) {
             *type = StackType::Any;
-            if (Output)
-                *value = Value();
+            *value = Value();
 
             // Maintain the invariant that, after a pop, there is always memory
             // reserved to push a value infallibly.
             return valueStack_.reserve(valueStack_.length() + 1);
         }
 
         if (valueStack_.empty())
             return fail("popping value from empty stack");
         return fail("popping value from outside block");
     }
 
     TypeAndValue<Value>& tv = valueStack_.back();
-
     *type = tv.type();
-    if (Output)
-        *value = tv.value();
-
+    *value = tv.value();
     valueStack_.popBack();
-
     return true;
 }
 
 template <typename Policy>
 inline bool
 OpIter<Policy>::typeMismatch(StackType actual, StackType expected)
 {
     UniqueChars error(JS_smprintf("type mismatch: expression has type %s but expected %s",
@@ -729,57 +671,48 @@ OpIter<Policy>::popWithType(StackType ex
     ControlStackEntry<ControlItem>& block = controlStack_.back();
 
     MOZ_ASSERT(valueStack_.length() >= block.valueStackStart());
     if (MOZ_UNLIKELY(valueStack_.length() == block.valueStackStart())) {
         // If the base of this block's stack is polymorphic, then we can pop a
         // dummy value of any expected type; it won't be used since we're in
         // unreachable code.
         if (block.polymorphicBase()) {
-            if (Output)
-                *value = Value();
+            *value = Value();
 
             // Maintain the invariant that, after a pop, there is always memory
             // reserved to push a value infallibly.
             return valueStack_.reserve(valueStack_.length() + 1);
         }
 
         if (valueStack_.empty())
             return fail("popping value from empty stack");
         return fail("popping value from outside block");
     }
 
     TypeAndValue<Value> tv = valueStack_.popCopy();
 
-    if (Validate) {
-        StackType _;
-        if (MOZ_UNLIKELY(!Unify(tv.type(), expectedType, &_)))
-            return typeMismatch(tv.type(), expectedType);
-    } else {
-        DebugOnly<StackType> result;
-        MOZ_ASSERT(Unify(tv.type(), expectedType, &result));
-    }
+    StackType _;
+    if (MOZ_UNLIKELY(!Unify(tv.type(), expectedType, &_)))
+        return typeMismatch(tv.type(), expectedType);
 
-    if (Output)
-        *value = tv.value();
-
+    *value = tv.value();
     return true;
 }
 
 // This function pops as many types from the stack as determined by the given
 // signature. Currently, all signatures are limited to 0 or 1 types, with
 // ExprType::Void meaning 0 and all other ValTypes meaning 1, but this will be
 // generalized in the future.
 template <typename Policy>
 inline bool
 OpIter<Policy>::popWithType(ExprType expectedType, Value* value)
 {
     if (IsVoid(expectedType)) {
-        if (Output)
-            *value = Value();
+        *value = Value();
         return true;
     }
 
     return popWithType(NonVoidToValType(expectedType), value);
 }
 
 // This function is just an optimization of popWithType + push.
 template <typename Policy>
@@ -793,44 +726,41 @@ OpIter<Policy>::topWithType(ValType expe
         // If the base of this block's stack is polymorphic, then we can just
         // pull out a dummy value of the expected type; it won't be used since
         // we're in unreachable code. We must however push this value onto the
         // stack since it is now fixed to a specific type by this type
         // constraint.
         if (block.polymorphicBase()) {
             if (!valueStack_.emplaceBack(expectedType, Value()))
                 return false;
-            if (Output)
-                *value = Value();
+
+            *value = Value();
             return true;
         }
 
         if (valueStack_.empty())
             return fail("reading value from empty stack");
         return fail("reading value from outside block");
     }
 
     TypeAndValue<Value>& tv = valueStack_.back();
 
     if (MOZ_UNLIKELY(!Unify(tv.type(), ToStackType(expectedType), &tv.typeRef())))
         return typeMismatch(tv.type(), ToStackType(expectedType));
 
-    if (Output)
-        *value = tv.value();
-
+    *value = tv.value();
     return true;
 }
 
 template <typename Policy>
 inline bool
 OpIter<Policy>::topWithType(ExprType expectedType, Value* value)
 {
     if (IsVoid(expectedType)) {
-        if (Output)
-            *value = Value();
+        *value = Value();
         return true;
     }
 
     return topWithType(NonVoidToValType(expectedType), value);
 }
 
 template <typename Policy>
 inline bool
@@ -842,104 +772,92 @@ OpIter<Policy>::pushControl(LabelKind ki
 template <typename Policy>
 inline bool
 OpIter<Policy>::checkStackAtEndOfBlock(ExprType* type, Value* value)
 {
     ControlStackEntry<ControlItem>& block = controlStack_.back();
 
     MOZ_ASSERT(valueStack_.length() >= block.valueStackStart());
     size_t pushed = valueStack_.length() - block.valueStackStart();
-    if (Validate && pushed > (IsVoid(block.resultType()) ? 0u : 1u))
+    if (pushed > (IsVoid(block.resultType()) ? 0u : 1u))
         return fail("unused values not explicitly dropped by end of block");
 
     if (!topWithType(block.resultType(), value))
         return false;
 
-    if (Output)
-        *type = block.resultType();
-
+    *type = block.resultType();
     return true;
 }
 
 template <typename Policy>
 inline bool
 OpIter<Policy>::getControl(uint32_t relativeDepth, ControlStackEntry<ControlItem>** controlEntry)
 {
-    if (Validate && relativeDepth >= controlStack_.length())
+    if (relativeDepth >= controlStack_.length())
         return fail("branch depth exceeds current nesting level");
 
     *controlEntry = &controlStack_[controlStack_.length() - 1 - relativeDepth];
     return true;
 }
 
 template <typename Policy>
 inline bool
 OpIter<Policy>::readBlockType(ExprType* type)
 {
     uint8_t unchecked;
     if (!d_.readBlockType(&unchecked))
         return fail("unable to read block signature");
 
-    if (Validate) {
-        switch (unchecked) {
-          case uint8_t(ExprType::Void):
-          case uint8_t(ExprType::I32):
-          case uint8_t(ExprType::I64):
-          case uint8_t(ExprType::F32):
-          case uint8_t(ExprType::F64):
-          case uint8_t(ExprType::I8x16):
-          case uint8_t(ExprType::I16x8):
-          case uint8_t(ExprType::I32x4):
-          case uint8_t(ExprType::F32x4):
-          case uint8_t(ExprType::B8x16):
-          case uint8_t(ExprType::B16x8):
-          case uint8_t(ExprType::B32x4):
-            break;
-          default:
-            return fail("invalid inline block type");
-        }
+    switch (unchecked) {
+      case uint8_t(ExprType::Void):
+      case uint8_t(ExprType::I32):
+      case uint8_t(ExprType::I64):
+      case uint8_t(ExprType::F32):
+      case uint8_t(ExprType::F64):
+      case uint8_t(ExprType::I8x16):
+      case uint8_t(ExprType::I16x8):
+      case uint8_t(ExprType::I32x4):
+      case uint8_t(ExprType::F32x4):
+      case uint8_t(ExprType::B8x16):
+      case uint8_t(ExprType::B16x8):
+      case uint8_t(ExprType::B32x4):
+        break;
+      default:
+        return fail("invalid inline block type");
     }
 
     *type = ExprType(unchecked);
     return true;
 }
 
 template <typename Policy>
 inline bool
 OpIter<Policy>::readOp(uint16_t* op)
 {
     MOZ_ASSERT(!controlStack_.empty());
 
     offsetOfLastReadOp_ = d_.currentOffset();
 
-    if (Validate) {
-        if (MOZ_UNLIKELY(!d_.readOp(op)))
-            return fail("unable to read opcode");
-    } else {
-        *op = uint16_t(d_.uncheckedReadOp());
-    }
+    if (MOZ_UNLIKELY(!d_.readOp(op)))
+        return fail("unable to read opcode");
 
     op_ = Op(*op);  // debug-only
 
     return true;
 }
 
 template <typename Policy>
 inline uint16_t
 OpIter<Policy>::peekOp()
 {
     const uint8_t* pos = d_.currentPosition();
     uint16_t op;
 
-    if (Validate) {
-        if (MOZ_UNLIKELY(!d_.readOp(&op)))
-            op = uint16_t(Op::Limit);
-    } else {
-        op = uint16_t(d_.uncheckedReadOp());
-    }
+    if (MOZ_UNLIKELY(!d_.readOp(&op)))
+        op = uint16_t(Op::Limit);
 
     d_.rollbackPosition(pos);
 
     return op;
 }
 
 template <typename Policy>
 inline bool
@@ -949,24 +867,23 @@ OpIter<Policy>::readFunctionStart(ExprTy
     MOZ_ASSERT(controlStack_.empty());
     MOZ_ASSERT(Op(op_) == Op::Limit);
 
     return pushControl(LabelKind::Block, ret);
 }
 
 template <typename Policy>
 inline bool
-OpIter<Policy>::readFunctionEnd()
+OpIter<Policy>::readFunctionEnd(const uint8_t* bodyEnd)
 {
-    if (Validate) {
-        if (!controlStack_.empty())
-            return fail("unbalanced function body control flow");
-    } else {
-        MOZ_ASSERT(controlStack_.empty());
-    }
+    if (d_.currentPosition() != bodyEnd)
+        return fail("function body length mismatch");
+
+    if (!controlStack_.empty())
+        return fail("unbalanced function body control flow");
 
     op_ = Op::Limit;
     valueStack_.clear();
     return true;
 }
 
 template <typename Policy>
 inline bool
@@ -1034,17 +951,17 @@ OpIter<Policy>::readElse(ExprType* type,
 
     // Finish checking the then-block.
 
     if (!checkStackAtEndOfBlock(type, value))
         return false;
 
     ControlStackEntry<ControlItem>& block = controlStack_.back();
 
-    if (Validate && block.kind() != LabelKind::Then)
+    if (block.kind() != LabelKind::Then)
         return fail("else can only be used within an if");
 
     // Switch to the else-block.
 
     if (!IsVoid(block.resultType()))
         valueStack_.popBack();
 
     MOZ_ASSERT(valueStack_.length() == block.valueStackStart());
@@ -1061,22 +978,20 @@ OpIter<Policy>::readEnd(LabelKind* kind,
 
     if (!checkStackAtEndOfBlock(type, value))
         return false;
 
     ControlStackEntry<ControlItem>& block = controlStack_.back();
 
     // If an `if` block ends with `end` instead of `else`, then we must
     // additionally validate that the then-block doesn't push anything.
-    if (Validate && block.kind() == LabelKind::Then && !IsVoid(block.resultType()))
+    if (block.kind() == LabelKind::Then && !IsVoid(block.resultType()))
         return fail("if without else with a result value");
 
-    if (Output)
-        *kind = block.kind();
-
+    *kind = block.kind();
     return true;
 }
 
 template <typename Policy>
 inline void
 OpIter<Policy>::popEnd()
 {
     MOZ_ASSERT(Classify(op_) == OpKind::End);
@@ -1093,62 +1008,43 @@ OpIter<Policy>::checkBranchValue(uint32_
         return false;
 
     *type = block->branchTargetType();
     return topWithType(*type, value);
 }
 
 template <typename Policy>
 inline bool
-OpIter<Policy>::readBr(uint32_t* relativeDepthOut, ExprType* typeOut, Value* value)
+OpIter<Policy>::readBr(uint32_t* relativeDepth, ExprType* type, Value* value)
 {
     MOZ_ASSERT(Classify(op_) == OpKind::Br);
 
-    uint32_t relativeDepth;
-    if (!readVarU32(&relativeDepth))
+    if (!readVarU32(relativeDepth))
         return fail("unable to read br depth");
 
-    if (Output)
-        *relativeDepthOut = relativeDepth;
-
-    ExprType type;
-    if (!checkBranchValue(relativeDepth, &type, value))
+    if (!checkBranchValue(*relativeDepth, type, value))
         return false;
 
-    if (Output)
-        *typeOut = type;
-
     afterUnconditionalBranch();
     return true;
 }
 
 template <typename Policy>
 inline bool
-OpIter<Policy>::readBrIf(uint32_t* relativeDepthOut, ExprType* typeOut, Value* value, Value* condition)
+OpIter<Policy>::readBrIf(uint32_t* relativeDepth, ExprType* type, Value* value, Value* condition)
 {
     MOZ_ASSERT(Classify(op_) == OpKind::BrIf);
 
-    uint32_t relativeDepth;
-    if (!readVarU32(&relativeDepth))
+    if (!readVarU32(relativeDepth))
         return fail("unable to read br_if depth");
 
-    if (Output)
-        *relativeDepthOut = relativeDepth;
-
     if (!popWithType(ValType::I32, condition))
         return false;
 
-    ExprType type;
-    if (!checkBranchValue(relativeDepth, &type, value))
-        return false;
-
-    if (Output)
-        *typeOut = type;
-
-    return true;
+    return checkBranchValue(*relativeDepth, type, value);
 }
 
 template <typename Policy>
 inline bool
 OpIter<Policy>::checkBrTableEntry(uint32_t* relativeDepth, ExprType* branchValueType,
                                   Value* branchValue)
 {
     if (!readVarU32(relativeDepth))
@@ -1171,53 +1067,44 @@ OpIter<Policy>::checkBrTableEntry(uint32
             return fail("br_table targets must all have the same value type");
     }
 
     return true;
 }
 
 template <typename Policy>
 inline bool
-OpIter<Policy>::readBrTable(Uint32Vector* depthsOut, uint32_t* defaultDepthOut,
+OpIter<Policy>::readBrTable(Uint32Vector* depths, uint32_t* defaultDepth,
                             ExprType* branchValueType, Value* branchValue, Value* index)
 {
     MOZ_ASSERT(Classify(op_) == OpKind::BrTable);
 
     uint32_t tableLength;
     if (!readVarU32(&tableLength))
         return fail("unable to read br_table table length");
 
     if (!popWithType(ValType::I32, index))
         return false;
 
-    Uint32Vector depths;
-    if (!depths.resize(tableLength))
+    if (!depths->resize(tableLength))
         return false;
 
-    ExprType type = ExprType::Limit;
+    *branchValueType = ExprType::Limit;
 
     for (uint32_t i = 0; i < tableLength; i++) {
-        if (!checkBrTableEntry(&depths[i], &type, branchValue))
+        if (!checkBrTableEntry(&(*depths)[i], branchValueType, branchValue))
             return false;
     }
 
-    uint32_t defaultDepth;
-    if (!checkBrTableEntry(&defaultDepth, &type, branchValue))
+    if (!checkBrTableEntry(defaultDepth, branchValueType, branchValue))
         return false;
 
-    MOZ_ASSERT(type != ExprType::Limit);
+    MOZ_ASSERT(*branchValueType != ExprType::Limit);
 
     afterUnconditionalBranch();
-
-    if (Output) {
-        *depthsOut = Move(depths);
-        *defaultDepthOut = defaultDepth;
-        *branchValueType = type;
-    }
-
     return true;
 }
 
 template <typename Policy>
 inline bool
 OpIter<Policy>::readUnreachable()
 {
     MOZ_ASSERT(Classify(op_) == OpKind::Unreachable);
@@ -1304,30 +1191,26 @@ OpIter<Policy>::readLinearMemoryAddress(
 {
     if (!env_.usesMemory())
         return fail("can't touch memory without memory");
 
     uint8_t alignLog2;
     if (!readFixedU8(&alignLog2))
         return fail("unable to read load alignment");
 
-    uint32_t unusedOffset;
-    if (!readVarU32(Output ? &addr->offset : &unusedOffset))
+    if (!readVarU32(&addr->offset))
         return fail("unable to read load offset");
 
-    if (Validate && (alignLog2 >= 32 || (uint32_t(1) << alignLog2) > byteSize))
+    if (alignLog2 >= 32 || (uint32_t(1) << alignLog2) > byteSize)
         return fail("greater than natural alignment");
 
-    Value unused;
-    if (!popWithType(ValType::I32, Output ? &addr->base : &unused))
+    if (!popWithType(ValType::I32, &addr->base))
         return false;
 
-    if (Output)
-        addr->align = uint32_t(1) << alignLog2;
-
+    addr->align = uint32_t(1) << alignLog2;
     return true;
 }
 
 template <typename Policy>
 inline bool
 OpIter<Policy>::readLoad(ValType resultType, uint32_t byteSize, LinearMemoryAddress<Value>* addr)
 {
     MOZ_ASSERT(Classify(op_) == OpKind::Load);
@@ -1364,18 +1247,17 @@ OpIter<Policy>::readTeeStore(ValType res
     MOZ_ASSERT(Classify(op_) == OpKind::TeeStore);
 
     if (!popWithType(resultType, value))
         return false;
 
     if (!readLinearMemoryAddress(byteSize, addr))
         return false;
 
-    infalliblePush(TypeAndValue<Value>(resultType, Output ? *value : Value()));
-
+    infalliblePush(TypeAndValue<Value>(resultType, *value));
     return true;
 }
 
 template <typename Policy>
 inline bool
 OpIter<Policy>::readNop()
 {
     MOZ_ASSERT(Classify(op_) == OpKind::Nop);
@@ -1391,17 +1273,17 @@ OpIter<Policy>::readCurrentMemory()
 
     if (!env_.usesMemory())
         return fail("can't touch memory without memory");
 
     uint32_t flags;
     if (!readVarU32(&flags))
         return false;
 
-    if (Validate && flags != uint32_t(MemoryTableFlags::Default))
+    if (flags != uint32_t(MemoryTableFlags::Default))
         return fail("unexpected flags");
 
     return push(ValType::I32);
 }
 
 template <typename Policy>
 inline bool
 OpIter<Policy>::readGrowMemory(Value* input)
@@ -1410,17 +1292,17 @@ OpIter<Policy>::readGrowMemory(Value* in
 
     if (!env_.usesMemory())
         return fail("can't touch memory without memory");
 
     uint32_t flags;
     if (!readVarU32(&flags))
         return false;
 
-    if (Validate && flags != uint32_t(MemoryTableFlags::Default))
+    if (flags != uint32_t(MemoryTableFlags::Default))
         return fail("unexpected flags");
 
     if (!popWithType(ValType::I32, input))
         return false;
 
     infalliblePush(ValType::I32);
 
     return true;
@@ -1438,423 +1320,298 @@ OpIter<Policy>::readSelect(StackType* ty
     StackType falseType;
     if (!popAnyType(&falseType, falseValue))
         return false;
 
     StackType trueType;
     if (!popAnyType(&trueType, trueValue))
         return false;
 
-    StackType resultType;
-    if (!Unify(falseType, trueType, &resultType))
+    if (!Unify(falseType, trueType, type))
         return fail("select operand types must match");
 
-    infalliblePush(resultType);
-
-    if (Output)
-        *type = resultType;
-
+    infalliblePush(*type);
     return true;
 }
 
 template <typename Policy>
 inline bool
 OpIter<Policy>::readGetLocal(const ValTypeVector& locals, uint32_t* id)
 {
     MOZ_ASSERT(Classify(op_) == OpKind::GetLocal);
 
-    uint32_t validateId;
-    if (!readVarU32(&validateId))
+    if (!readVarU32(id))
         return false;
 
-    if (Validate && validateId >= locals.length())
+    if (*id >= locals.length())
         return fail("get_local index out of range");
 
-    if (!push(locals[validateId]))
-        return false;
-
-    if (Output)
-        *id = validateId;
-
-    return true;
+    return push(locals[*id]);
 }
 
 template <typename Policy>
 inline bool
 OpIter<Policy>::readSetLocal(const ValTypeVector& locals, uint32_t* id, Value* value)
 {
     MOZ_ASSERT(Classify(op_) == OpKind::SetLocal);
 
-    uint32_t validateId;
-    if (!readVarU32(&validateId))
+    if (!readVarU32(id))
         return false;
 
-    if (Validate && validateId >= locals.length())
+    if (*id >= locals.length())
         return fail("set_local index out of range");
 
-    if (!popWithType(locals[validateId], value))
-        return false;
-
-    if (Output)
-        *id = validateId;
-
-    return true;
+    return popWithType(locals[*id], value);
 }
 
 template <typename Policy>
 inline bool
 OpIter<Policy>::readTeeLocal(const ValTypeVector& locals, uint32_t* id, Value* value)
 {
     MOZ_ASSERT(Classify(op_) == OpKind::TeeLocal);
 
-    uint32_t validateId;
-    if (!readVarU32(&validateId))
+    if (!readVarU32(id))
         return false;
 
-    if (Validate && validateId >= locals.length())
+    if (*id >= locals.length())
         return fail("set_local index out of range");
 
-    if (!topWithType(locals[validateId], value))
-        return false;
-
-    if (Output)
-        *id = validateId;
-
-    return true;
+    return topWithType(locals[*id], value);
 }
 
 template <typename Policy>
 inline bool
 OpIter<Policy>::readGetGlobal(uint32_t* id)
 {
     MOZ_ASSERT(Classify(op_) == OpKind::GetGlobal);
 
-    uint32_t validateId;
-    if (!readVarU32(&validateId))
+    if (!readVarU32(id))
         return false;
 
-    if (Validate && validateId >= env_.globals.length())
+    if (*id >= env_.globals.length())
         return fail("get_global index out of range");
 
-    if (!push(env_.globals[validateId].type()))
-        return false;
-
-    if (Output)
-        *id = validateId;
-
-    return true;
+    return push(env_.globals[*id].type());
 }
 
 template <typename Policy>
 inline bool
 OpIter<Policy>::readSetGlobal(uint32_t* id, Value* value)
 {
     MOZ_ASSERT(Classify(op_) == OpKind::SetGlobal);
 
-    uint32_t validateId;
-    if (!readVarU32(&validateId))
+    if (!readVarU32(id))
         return false;
 
-    if (Validate && validateId >= env_.globals.length())
+    if (*id >= env_.globals.length())
         return fail("set_global index out of range");
 
-    if (Validate && !env_.globals[validateId].isMutable())
+    if (!env_.globals[*id].isMutable())
         return fail("can't write an immutable global");
 
-    if (!popWithType(env_.globals[validateId].type(), value))
-        return false;
-
-    if (Output)
-        *id = validateId;
-
-    return true;
+    return popWithType(env_.globals[*id].type(), value);
 }
 
 template <typename Policy>
 inline bool
 OpIter<Policy>::readTeeGlobal(uint32_t* id, Value* value)
 {
     MOZ_ASSERT(Classify(op_) == OpKind::TeeGlobal);
 
-    uint32_t validateId;
-    if (!readVarU32(&validateId))
+    if (!readVarU32(id))
         return false;
 
-    if (Validate && validateId >= env_.globals.length())
+    if (*id >= env_.globals.length())
         return fail("set_global index out of range");
 
-    if (Validate && !env_.globals[validateId].isMutable())
+    if (!env_.globals[*id].isMutable())
         return fail("can't write an immutable global");
 
-    if (!topWithType(env_.globals[validateId].type(), value))
-        return false;
-
-    if (Output)
-        *id = validateId;
-
-    return true;
+    return topWithType(env_.globals[*id].type(), value);
 }
 
 template <typename Policy>
 inline bool
 OpIter<Policy>::readI32Const(int32_t* i32)
 {
     MOZ_ASSERT(Classify(op_) == OpKind::I32);
 
-    int32_t unused;
-    if (!readVarS32(Output ? i32 : &unused))
-        return false;
-
-    return push(ValType::I32);
+    return readVarS32(i32) &&
+           push(ValType::I32);
 }
 
 template <typename Policy>
 inline bool
 OpIter<Policy>::readI64Const(int64_t* i64)
 {
     MOZ_ASSERT(Classify(op_) == OpKind::I64);
 
-    int64_t unused;
-    if (!readVarS64(Output ? i64 : &unused))
-        return false;
-
-    if (!push(ValType::I64))
-        return false;
-
-    return true;
+    return readVarS64(i64) &&
+           push(ValType::I64);
 }
 
 template <typename Policy>
 inline bool
 OpIter<Policy>::readF32Const(float* f32)
 {
     MOZ_ASSERT(Classify(op_) == OpKind::F32);
 
-    float unused;
-    if (!readFixedF32(Output ? f32 : &unused))
-        return false;
-
-    if (!push(ValType::F32))
-        return false;
-
-    return true;
+    return readFixedF32(f32) &&
+           push(ValType::F32);
 }
 
 template <typename Policy>
 inline bool
 OpIter<Policy>::readF64Const(double* f64)
 {
     MOZ_ASSERT(Classify(op_) == OpKind::F64);
 
-    double unused;
-    if (!readFixedF64(Output ? f64 : &unused))
-       return false;
-
-    if (!push(ValType::F64))
-        return false;
-
-    return true;
+    return readFixedF64(f64) &&
+           push(ValType::F64);
 }
 
 template <typename Policy>
 inline bool
 OpIter<Policy>::readI8x16Const(I8x16* i8x16)
 {
     MOZ_ASSERT(Classify(op_) == OpKind::I8x16);
 
-    I8x16 unused;
-    if (!readFixedI8x16(Output ? i8x16 : &unused))
-        return false;
-
-    if (!push(ValType::I8x16))
-        return false;
-
-    return true;
+    return readFixedI8x16(i8x16) &&
+           push(ValType::I8x16);
 }
 
 template <typename Policy>
 inline bool
 OpIter<Policy>::readI16x8Const(I16x8* i16x8)
 {
     MOZ_ASSERT(Classify(op_) == OpKind::I16x8);
 
-    I16x8 unused;
-    if (!readFixedI16x8(Output ? i16x8 : &unused))
-        return false;
-
-    if (!push(ValType::I16x8))
-        return false;
-
-    return true;
+    return readFixedI16x8(i16x8) &&
+           push(ValType::I16x8);
 }
 
 template <typename Policy>
 inline bool
 OpIter<Policy>::readI32x4Const(I32x4* i32x4)
 {
     MOZ_ASSERT(Classify(op_) == OpKind::I32x4);
 
-    I32x4 unused;
-    if (!readFixedI32x4(Output ? i32x4 : &unused))
-        return false;
-
-    if (!push(ValType::I32x4))
-        return false;
-
-    return true;
+    return readFixedI32x4(i32x4) &&
+           push(ValType::I32x4);
 }
 
 template <typename Policy>
 inline bool
 OpIter<Policy>::readF32x4Const(F32x4* f32x4)
 {
     MOZ_ASSERT(Classify(op_) == OpKind::F32x4);
 
-    F32x4 unused;
-    if (!readFixedF32x4(Output ? f32x4 : &unused))
-        return false;
-
-    if (!push(ValType::F32x4))
-        return false;
-
-    return true;
+    return readFixedF32x4(f32x4) &&
+           push(ValType::F32x4);
 }
 
 template <typename Policy>
 inline bool
 OpIter<Policy>::readB8x16Const(I8x16* i8x16)
 {
     MOZ_ASSERT(Classify(op_) == OpKind::B8x16);
 
-    I8x16 unused;
-    if (!readFixedI8x16(Output ? i8x16 : &unused))
-        return false;
-
-    if (!push(ValType::B8x16))
-        return false;
-
-    return true;
+    return readFixedI8x16(i8x16) &&
+           push(ValType::B8x16);
 }
 
 template <typename Policy>
 inline bool
 OpIter<Policy>::readB16x8Const(I16x8* i16x8)
 {
     MOZ_ASSERT(Classify(op_) == OpKind::B16x8);
 
-    I16x8 unused;
-    if (!readFixedI16x8(Output ? i16x8 : &unused))
-        return false;
-
-    if (!push(ValType::B16x8))
-        return false;
-
-    return true;
+    return readFixedI16x8(i16x8) &&
+           push(ValType::B16x8);
 }
 
 template <typename Policy>
 inline bool
 OpIter<Policy>::readB32x4Const(I32x4* i32x4)
 {
     MOZ_ASSERT(Classify(op_) == OpKind::B32x4);
 
-    I32x4 unused;
-    if (!readFixedI32x4(Output ? i32x4 : &unused))
-        return false;
-
-    if (!push(ValType::B32x4))
-        return false;
-
-    return true;
+    return readFixedI32x4(i32x4) &&
+           push(ValType::B32x4);
 }
 
 template <typename Policy>
 inline bool
 OpIter<Policy>::popCallArgs(const ValTypeVector& expectedTypes, ValueVector* values)
 {
     // Iterate through the argument types backward so that pops occur in the
     // right order.
 
-    if (Output && !values->resize(expectedTypes.length()))
+    if (!values->resize(expectedTypes.length()))
         return false;
 
     for (int32_t i = expectedTypes.length() - 1; i >= 0; i--) {
-        Value* argValue = Output ? &(*values)[i] : nullptr;
-        if (!popWithType(expectedTypes[i], argValue))
+        if (!popWithType(expectedTypes[i], &(*values)[i]))
             return false;
     }
 
     return true;
 }
 
 template <typename Policy>
 inline bool
-OpIter<Policy>::readCall(uint32_t* funcIndexOut, ValueVector* argValues)
+OpIter<Policy>::readCall(uint32_t* funcIndex, ValueVector* argValues)
 {
     MOZ_ASSERT(Classify(op_) == OpKind::Call);
 
-    uint32_t funcIndex;
-    if (!readVarU32(&funcIndex))
+    if (!readVarU32(funcIndex))
         return fail("unable to read call function index");
 
-    if (funcIndex >= env_.funcSigs.length())
+    if (*funcIndex >= env_.funcSigs.length())
         return fail("callee index out of range");
 
-    const Sig& sig = *env_.funcSigs[funcIndex];
+    const Sig& sig = *env_.funcSigs[*funcIndex];
 
     if (!popCallArgs(sig.args(), argValues))
         return false;
 
-    if (!push(sig.ret()))
-        return false;
-
-    if (Output)
-        *funcIndexOut = funcIndex;
-
-    return true;
+    return push(sig.ret());
 }
 
 template <typename Policy>
 inline bool
-OpIter<Policy>::readCallIndirect(uint32_t* sigIndexOut, Value* callee, ValueVector* argValues)
+OpIter<Policy>::readCallIndirect(uint32_t* sigIndex, Value* callee, ValueVector* argValues)
 {
     MOZ_ASSERT(Classify(op_) == OpKind::CallIndirect);
 
     if (!env_.tables.length())
         return fail("can't call_indirect without a table");
 
-    uint32_t sigIndex;
-    if (!readVarU32(&sigIndex))
+    if (!readVarU32(sigIndex))
         return fail("unable to read call_indirect signature index");
 
-    if (sigIndex >= env_.numSigs())
+    if (*sigIndex >= env_.numSigs())
         return fail("signature index out of range");
 
     uint32_t flags;
     if (!readVarU32(&flags))
         return false;
 
-    if (Validate && flags != uint32_t(MemoryTableFlags::Default))
+    if (flags != uint32_t(MemoryTableFlags::Default))
         return fail("unexpected flags");
 
     if (!popWithType(ValType::I32, callee))
         return false;
 
-    const Sig& sig = env_.sigs[sigIndex];
+    const Sig& sig = env_.sigs[*sigIndex];
 
     if (!popCallArgs(sig.args(), argValues))
         return false;
 
-    if (!push(sig.ret()))
-        return false;
-
-    if (Output)
-        *sigIndexOut = sigIndex;
-
-    return true;
+    return push(sig.ret());
 }
 
 template <typename Policy>
 inline bool
 OpIter<Policy>::readOldCallIndirect(uint32_t* sigIndex, Value* callee, ValueVector* argValues)
 {
     MOZ_ASSERT(Classify(op_) == OpKind::OldCallIndirect);
 
@@ -1879,139 +1636,114 @@ OpIter<Policy>::readOldCallIndirect(uint
 }
 
 template <typename Policy>
 inline bool
 OpIter<Policy>::readAtomicLoad(LinearMemoryAddress<Value>* addr, Scalar::Type* viewType)
 {
     MOZ_ASSERT(Classify(op_) == OpKind::AtomicLoad);
 
-    Scalar::Type validateViewType;
-    if (!readAtomicViewType(&validateViewType))
+    if (!readAtomicViewType(viewType))
         return false;
 
-    uint32_t byteSize = Scalar::byteSize(validateViewType);
+    uint32_t byteSize = Scalar::byteSize(*viewType);
     if (!readLinearMemoryAddress(byteSize, addr))
         return false;
 
     infalliblePush(ValType::I32);
-
-    if (Output)
-        *viewType = validateViewType;
-
     return true;
 }
 
 template <typename Policy>
 inline bool
 OpIter<Policy>::readAtomicStore(LinearMemoryAddress<Value>* addr, Scalar::Type* viewType,
                                 Value* value)
 {
     MOZ_ASSERT(Classify(op_) == OpKind::AtomicStore);
 
-    Scalar::Type validateViewType;
-    if (!readAtomicViewType(&validateViewType))
+    if (!readAtomicViewType(viewType))
         return false;
 
-    uint32_t byteSize = Scalar::byteSize(validateViewType);
+    uint32_t byteSize = Scalar::byteSize(*viewType);
     if (!readLinearMemoryAddress(byteSize, addr))
         return false;
 
     if (!popWithType(ValType::I32, value))
         return false;
 
     infalliblePush(ValType::I32);
-
-    if (Output)
-        *viewType = validateViewType;
-
     return true;
 }
 
 template <typename Policy>
 inline bool
 OpIter<Policy>::readAtomicBinOp(LinearMemoryAddress<Value>* addr, Scalar::Type* viewType,
                                 jit::AtomicOp* op, Value* value)
 {
     MOZ_ASSERT(Classify(op_) == OpKind::AtomicBinOp);
 
-    Scalar::Type validateViewType;
-    if (!readAtomicViewType(&validateViewType))
+    if (!readAtomicViewType(viewType))
         return false;
 
     if (!readAtomicBinOpOp(op))
         return false;
 
-    uint32_t byteSize = Scalar::byteSize(validateViewType);
+    uint32_t byteSize = Scalar::byteSize(*viewType);
     if (!readLinearMemoryAddress(byteSize, addr))
         return false;
 
     if (!popWithType(ValType::I32, value))
         return false;
 
     infalliblePush(ValType::I32);
-
-    if (Output)
-        *viewType = validateViewType;
-
     return true;
 }
 
 template <typename Policy>
 inline bool
 OpIter<Policy>::readAtomicCompareExchange(LinearMemoryAddress<Value>* addr, Scalar::Type* viewType,
                                           Value* oldValue, Value* newValue)
 {
     MOZ_ASSERT(Classify(op_) == OpKind::AtomicCompareExchange);
 
-    Scalar::Type validateViewType;
-    if (!readAtomicViewType(&validateViewType))
+    if (!readAtomicViewType(viewType))
         return false;
 
-    uint32_t byteSize = Scalar::byteSize(validateViewType);
+    uint32_t byteSize = Scalar::byteSize(*viewType);
     if (!readLinearMemoryAddress(byteSize, addr))
         return false;
 
     if (!popWithType(ValType::I32, newValue))
         return false;
 
     if (!popWithType(ValType::I32, oldValue))
         return false;
 
     infalliblePush(ValType::I32);
-
-    if (Output)
-        *viewType = validateViewType;
-
     return true;
 }
 
 template <typename Policy>
 inline bool
 OpIter<Policy>::readAtomicExchange(LinearMemoryAddress<Value>* addr, Scalar::Type* viewType,
                                    Value* value)
 {
     MOZ_ASSERT(Classify(op_) == OpKind::AtomicExchange);
 
-    Scalar::Type validateViewType;
-    if (!readAtomicViewType(&validateViewType))
+    if (!readAtomicViewType(viewType))
         return false;
 
-    uint32_t byteSize = Scalar::byteSize(validateViewType);
+    uint32_t byteSize = Scalar::byteSize(*viewType);
     if (!readLinearMemoryAddress(byteSize, addr))
         return false;
 
     if (!popWithType(ValType::I32, value))
         return false;
 
     infalliblePush(ValType::I32);
-
-    if (Output)
-        *viewType = validateViewType;
-
     return true;
 }
 
 template <typename Policy>
 inline bool
 OpIter<Policy>::readSimdComparison(ValType simdType, Value* lhs, Value* rhs)
 {
     MOZ_ASSERT(Classify(op_) == OpKind::SimdComparison);
@@ -2063,54 +1795,50 @@ inline bool
 OpIter<Policy>::readExtractLane(ValType simdType, uint8_t* lane, Value* vector)
 {
     MOZ_ASSERT(Classify(op_) == OpKind::ExtractLane);
 
     uint32_t laneBits;
     if (!readVarU32(&laneBits))
         return false;
 
-    if (Validate && laneBits >= NumSimdElements(simdType))
+    if (laneBits >= NumSimdElements(simdType))
         return fail("simd lane out of bounds for simd type");
 
+    *lane = uint8_t(laneBits);
+
     if (!popWithType(simdType, vector))
         return false;
 
     infalliblePush(SimdElementType(simdType));
-
-    if (Output)
-        *lane = uint8_t(laneBits);
-
     return true;
 }
 
 template <typename Policy>
 inline bool
 OpIter<Policy>::readReplaceLane(ValType simdType, uint8_t* lane, Value* vector, Value* scalar)
 {
     MOZ_ASSERT(Classify(op_) == OpKind::ReplaceLane);
 
     uint32_t laneBits;
     if (!readVarU32(&laneBits))
         return false;
 
-    if (Validate && laneBits >= NumSimdElements(simdType))
+    if (laneBits >= NumSimdElements(simdType))
         return fail("simd lane out of bounds for simd type");
 
+    *lane = uint8_t(laneBits);
+
     if (!popWithType(SimdElementType(simdType), scalar))
         return false;
 
     if (!popWithType(simdType, vector))
         return false;
 
     infalliblePush(simdType);
-
-    if (Output)
-        *lane = uint8_t(laneBits);
-
     return true;
 }
 
 template <typename Policy>
 inline bool
 OpIter<Policy>::readSplat(ValType simdType, Value* scalar)
 {
     MOZ_ASSERT(Classify(op_) == OpKind::Splat);
@@ -2127,20 +1855,19 @@ template <typename Policy>
 inline bool
 OpIter<Policy>::readSwizzle(ValType simdType, uint8_t (* lanes)[16], Value* vector)
 {
     MOZ_ASSERT(Classify(op_) == OpKind::Swizzle);
 
     uint32_t numSimdLanes = NumSimdElements(simdType);
     MOZ_ASSERT(numSimdLanes <= mozilla::ArrayLength(*lanes));
     for (uint32_t i = 0; i < numSimdLanes; ++i) {
-        uint8_t validateLane;
-        if (!readFixedU8(Output ? &(*lanes)[i] : &validateLane))
+        if (!readFixedU8(&(*lanes)[i]))
             return fail("unable to read swizzle lane");
-        if (Validate && (Output ? (*lanes)[i] : validateLane) >= numSimdLanes)
+        if ((*lanes)[i] >= numSimdLanes)
             return fail("swizzle index out of bounds");
     }
 
     if (!popWithType(simdType, vector))
         return false;
 
     infalliblePush(simdType);
 
@@ -2151,20 +1878,19 @@ template <typename Policy>
 inline bool
 OpIter<Policy>::readShuffle(ValType simdType, uint8_t (* lanes)[16], Value* lhs, Value* rhs)
 {
     MOZ_ASSERT(Classify(op_) == OpKind::Shuffle);
 
     uint32_t numSimdLanes = NumSimdElements(simdType);
     MOZ_ASSERT(numSimdLanes <= mozilla::ArrayLength(*lanes));
     for (uint32_t i = 0; i < numSimdLanes; ++i) {
-        uint8_t validateLane;
-        if (!readFixedU8(Output ? &(*lanes)[i] : &validateLane))
+        if (!readFixedU8(&(*lanes)[i]))
             return fail("unable to read shuffle lane");
-        if (Validate && (Output ? (*lanes)[i] : validateLane) >= numSimdLanes * 2)
+        if ((*lanes)[i] >= numSimdLanes * 2)
             return fail("shuffle index out of bounds");
     }
 
     if (!popWithType(simdType, rhs))
         return false;
 
     if (!popWithType(simdType, lhs))
         return false;
@@ -2195,22 +1921,21 @@ OpIter<Policy>::readSimdSelect(ValType s
 
 template <typename Policy>
 inline bool
 OpIter<Policy>::readSimdCtor(ValType elementType, uint32_t numElements, ValType simdType,
                              ValueVector* argValues)
 {
     MOZ_ASSERT(Classify(op_) == OpKind::SimdCtor);
 
-    if (Output && !argValues->resize(numElements))
+    if (!argValues->resize(numElements))
         return false;
 
     for (int32_t i = numElements - 1; i >= 0; i--) {
-        Value* argValue = Output ? &(*argValues)[i] : nullptr;
-        if (!popWithType(elementType, argValue))
+        if (!popWithType(elementType, &(*argValues)[i]))
             return false;
     }
 
     infalliblePush(simdType);
 
     return true;
 }
 
--- a/js/src/wasm/WasmBinaryToAST.cpp
+++ b/js/src/wasm/WasmBinaryToAST.cpp
@@ -61,24 +61,20 @@ struct AstDecodeStackItem
        terminationKind(AstDecodeTerminationKind::Unknown),
        type(ExprType::Limit)
     {}
 };
 
 // We don't define a Value type because OpIter doesn't push void values, which
 // we actually need here because we're building an AST, so we maintain our own
 // stack.
-struct AstDecodePolicy : OpIterPolicy
+struct AstDecodePolicy
 {
-    // Enable validation because we can be called from wasmBinaryToText on bytes
-    // which are not necessarily valid, and we shouldn't run the decoder in
-    // non-validating mode on invalid code.
-    static const bool Validate = true;
-
-    static const bool Output = true;
+    typedef Nothing Value;
+    typedef Nothing ControlItem;
 };
 
 typedef OpIter<AstDecodePolicy> AstDecodeOpIter;
 
 class AstDecodeContext
 {
   public:
     typedef AstVector<AstDecodeStackItem> AstDecodeStack;
@@ -1450,17 +1446,17 @@ AstDecodeFunctionBody(AstDecodeContext &
 
     AstExprVector body(c.lifo);
     for (auto i = c.exprs().begin() + c.depths().back(), e = c.exprs().end(); i != e; ++i) {
         if (!body.append(i->expr))
             return false;
     }
     c.exprs().shrinkTo(c.depths().popCopy());
 
-    if (!c.iter().readFunctionEnd())
+    if (!c.iter().readFunctionEnd(bodyEnd))
         return false;
 
     c.endFunction();
 
     if (c.d.currentPosition() != bodyEnd)
         return c.d.fail("function body length mismatch");
 
     size_t sigIndex = c.env().funcIndexToSigIndex(funcIndex);
--- a/js/src/wasm/WasmGenerator.cpp
+++ b/js/src/wasm/WasmGenerator.cpp
@@ -1127,22 +1127,20 @@ ModuleGenerator::finish(const ShareableB
     uint32_t padding = ComputeByteAlignment(bytesNeeded, gc::SystemPageSize());
 
     // Use initLengthUninitialized so there is no round-up allocation nor time
     // wasted zeroing memory.
     Bytes code;
     if (!code.initLengthUninitialized(bytesNeeded + padding))
         return nullptr;
 
-    // Delay flushing of the icache until CodeSegment::create since there is
-    // more patching to do before this code becomes executable.
-    {
-        AutoFlushICache afc("ModuleGenerator::finish", /* inhibit = */ true);
-        masm_.executableCopy(code.begin());
-    }
+    // We're not copying into executable memory, so don't flush the icache.
+    // Note: we may be executing on an arbitrary thread without TlsContext set
+    // so we can't use AutoFlushICache to inhibit.
+    masm_.executableCopy(code.begin(), /* flushICache = */ false);
 
     // Zero the padding, since we used resizeUninitialized above.
     memset(code.begin() + bytesNeeded, 0, padding);
 
     // Convert the CallSiteAndTargetVector (needed during generation) to a
     // CallSiteVector (what is stored in the Module).
     if (!metadata_->callSites.appendAll(masm_.callSites()))
         return nullptr;
--- a/js/src/wasm/WasmIonCompile.cpp
+++ b/js/src/wasm/WasmIonCompile.cpp
@@ -37,43 +37,42 @@ using mozilla::IsPowerOfTwo;
 using mozilla::Maybe;
 using mozilla::Nothing;
 using mozilla::Some;
 
 namespace {
 
 typedef Vector<MBasicBlock*, 8, SystemAllocPolicy> BlockVector;
 
-struct IonCompilePolicy : OpIterPolicy
+struct IonCompilePolicy
 {
-    // Producing output is what we're all about here.
-    static const bool Output = true;
-
     // We store SSA definitions in the value stack.
     typedef MDefinition* Value;
 
     // We store loop headers and then/else blocks in the control flow stack.
     typedef MBasicBlock* ControlItem;
 };
 
 typedef OpIter<IonCompilePolicy> IonOpIter;
 
 class FunctionCompiler;
 
 // TlsUsage describes how the TLS register is used during a function call.
 
-enum class TlsUsage {
+enum class TlsUsage
+{
     Unused,     // No particular action is taken with respect to the TLS register.
     Need,       // The TLS register must be reloaded just before the call.
     CallerSaved // Same, plus space must be allocated to save/restore the TLS
                 // register.
 };
 
 static bool
-NeedsTls(TlsUsage usage) {
+NeedsTls(TlsUsage usage)
+{
     return usage == TlsUsage::Need || usage == TlsUsage::CallerSaved;
 }
 
 // CallCompileState describes a call that is being compiled. Due to expression
 // nesting, multiple calls can be in the middle of compilation at the same time
 // and these are tracked in a stack by FunctionCompiler.
 
 class CallCompileState
@@ -1658,16 +1657,137 @@ MDefinition* FunctionCompiler::unary<MAb
     auto* ins = MAbs::NewWasm(alloc(), op, type);
     curBlock_->add(ins);
     return ins;
 }
 
 } // end anonymous namespace
 
 static bool
+EmitI32Const(FunctionCompiler& f)
+{
+    int32_t i32;
+    if (!f.iter().readI32Const(&i32))
+        return false;
+
+    f.iter().setResult(f.constant(Int32Value(i32), MIRType::Int32));
+    return true;
+}
+
+static bool
+EmitI64Const(FunctionCompiler& f)
+{
+    int64_t i64;
+    if (!f.iter().readI64Const(&i64))
+        return false;
+
+    f.iter().setResult(f.constant(i64));
+    return true;
+}
+
+static bool
+EmitF32Const(FunctionCompiler& f)
+{
+    float f32;
+    if (!f.iter().readF32Const(&f32))
+        return false;
+
+    f.iter().setResult(f.constant(f32));
+    return true;
+}
+
+static bool
+EmitF64Const(FunctionCompiler& f)
+{
+    double f64;
+    if (!f.iter().readF64Const(&f64))
+        return false;
+
+    f.iter().setResult(f.constant(f64));
+    return true;
+}
+
+static bool
+EmitI8x16Const(FunctionCompiler& f)
+{
+    I8x16 i8x16;
+    if (!f.iter().readI8x16Const(&i8x16))
+        return false;
+
+    f.iter().setResult(f.constant(SimdConstant::CreateX16(i8x16), MIRType::Int8x16));
+    return true;
+}
+
+static bool
+EmitI16x8Const(FunctionCompiler& f)
+{
+    I16x8 i16x8;
+    if (!f.iter().readI16x8Const(&i16x8))
+        return false;
+
+    f.iter().setResult(f.constant(SimdConstant::CreateX8(i16x8), MIRType::Int16x8));
+    return true;
+}
+
+static bool
+EmitI32x4Const(FunctionCompiler& f)
+{
+    I32x4 i32x4;
+    if (!f.iter().readI32x4Const(&i32x4))
+        return false;
+
+    f.iter().setResult(f.constant(SimdConstant::CreateX4(i32x4), MIRType::Int32x4));
+    return true;
+}
+
+static bool
+EmitF32x4Const(FunctionCompiler& f)
+{
+    F32x4 f32x4;
+    if (!f.iter().readF32x4Const(&f32x4))
+        return false;
+
+    f.iter().setResult(f.constant(SimdConstant::CreateX4(f32x4), MIRType::Float32x4));
+    return true;
+}
+
+static bool
+EmitB8x16Const(FunctionCompiler& f)
+{
+    I8x16 i8x16;
+    if (!f.iter().readB8x16Const(&i8x16))
+        return false;
+
+    f.iter().setResult(f.constant(SimdConstant::CreateX16(i8x16), MIRType::Bool8x16));
+    return true;
+}
+
+static bool
+EmitB16x8Const(FunctionCompiler& f)
+{
+    I16x8 i16x8;
+    if (!f.iter().readB16x8Const(&i16x8))
+        return false;
+
+    f.iter().setResult(f.constant(SimdConstant::CreateX8(i16x8), MIRType::Bool16x8));
+    return true;
+}
+
+static bool
+EmitB32x4Const(FunctionCompiler& f)
+{
+    I32x4 i32x4;
+    if (!f.iter().readB32x4Const(&i32x4))
+        return false;
+
+    f.iter().setResult(f.constant(SimdConstant::CreateX4(i32x4), MIRType::Bool32x4));
+    return true;
+}
+
+static bool
 EmitBlock(FunctionCompiler& f)
 {
     return f.iter().readBlock() &&
            f.startBlock();
 }
 
 static bool
 EmitLoop(FunctionCompiler& f)
@@ -1846,16 +1966,26 @@ EmitReturn(FunctionCompiler& f)
         f.returnVoid();
         return true;
     }
 
     f.returnExpr(value);
     return true;
 }
 
+static bool
+EmitUnreachable(FunctionCompiler& f)
+{
+    if (!f.iter().readUnreachable())
+        return false;
+
+    f.unreachableTrap();
+    return true;
+}
+
 typedef IonOpIter::ValueVector DefVector;
 
 static bool
 EmitCallArgs(FunctionCompiler& f, const Sig& sig, const DefVector& args, TlsUsage tls,
              CallCompileState* call)
 {
     MOZ_ASSERT(NeedsTls(tls));
 
@@ -3066,635 +3196,591 @@ EmitCurrentMemory(FunctionCompiler& f)
     if (!f.builtinInstanceMethodCall(SymbolicAddress::CurrentMemory, args, ValType::I32, &ret))
         return false;
 
     f.iter().setResult(ret);
     return true;
 }
 
 static bool
-EmitExpr(FunctionCompiler& f)
+EmitBodyExprs(FunctionCompiler& f)
 {
-    if (!f.mirGen().ensureBallast())
+    if (!f.iter().readFunctionStart(f.sig().ret()))
         return false;
 
-    uint16_t u16;
-    MOZ_ALWAYS_TRUE(f.iter().readOp(&u16));
-    Op op = Op(u16);
-
-    switch (op) {
-      // Control opcodes
-      case Op::Nop:
-        return f.iter().readNop();
-      case Op::Drop:
-        return f.iter().readDrop();
-      case Op::Block:
-        return EmitBlock(f);
-      case Op::Loop:
-        return EmitLoop(f);
-      case Op::If:
-        return EmitIf(f);
-      case Op::Else:
-        return EmitElse(f);
-      case Op::End:
-        return EmitEnd(f);
-      case Op::Br:
-        return EmitBr(f);
-      case Op::BrIf:
-        return EmitBrIf(f);
-      case Op::BrTable:
-        return EmitBrTable(f);
-      case Op::Return:
-        return EmitReturn(f);
-      case Op::Unreachable:
-        if (!f.iter().readUnreachable())
-            return false;
-        f.unreachableTrap();
-        return true;
-
-      // Calls
-      case Op::Call:
-        return EmitCall(f);
-      case Op::CallIndirect:
-        return EmitCallIndirect(f, /* oldStyle = */ false);
-      case Op::OldCallIndirect:
-        return EmitCallIndirect(f, /* oldStyle = */ true);
-
-      // Locals and globals
-      case Op::GetLocal:
-        return EmitGetLocal(f);
-      case Op::SetLocal:
-        return EmitSetLocal(f);
-      case Op::TeeLocal:
-        return EmitTeeLocal(f);
-      case Op::GetGlobal:
-        return EmitGetGlobal(f);
-      case Op::SetGlobal:
-        return EmitSetGlobal(f);
-      case Op::TeeGlobal:
-        return EmitTeeGlobal(f);
-
-      // Select
-      case Op::Select:
-        return EmitSelect(f);
-
-      // I32
-      case Op::I32Const: {
-        int32_t i32;
-        if (!f.iter().readI32Const(&i32))
+#define CHECK(c)                                                              \
+    if (!(c))                                                                 \
+        return false;                                                         \
+    break
+
+#define CHECK_ASMJS(c)                                                        \
+    if (!f.env().isAsmJS())                                                   \
+        return f.iter().unrecognizedOpcode(op);                               \
+    if (!(c))                                                                 \
+        return false;                                                         \
+    break
+
+    while (true) {
+        if (!f.mirGen().ensureBallast())
             return false;
 
-        f.iter().setResult(f.constant(Int32Value(i32), MIRType::Int32));
-        return true;
-      }
-      case Op::I32Add:
-        return EmitAdd(f, ValType::I32, MIRType::Int32);
-      case Op::I32Sub:
-        return EmitSub(f, ValType::I32, MIRType::Int32);
-      case Op::I32Mul:
-        return EmitMul(f, ValType::I32, MIRType::Int32);
-      case Op::I32DivS:
-      case Op::I32DivU:
-        return EmitDiv(f, ValType::I32, MIRType::Int32, op == Op::I32DivU);
-      case Op::I32RemS:
-      case Op::I32RemU:
-        return EmitRem(f, ValType::I32, MIRType::Int32, op == Op::I32RemU);
-      case Op::I32Min:
-      case Op::I32Max:
-        return EmitMinMax(f, ValType::I32, MIRType::Int32, op == Op::I32Max);
-      case Op::I32Eqz:
-        return EmitConversion<MNot>(f, ValType::I32, ValType::I32);
-      case Op::I32TruncSF32:
-      case Op::I32TruncUF32:
-        return EmitTruncate(f, ValType::F32, ValType::I32, op == Op::I32TruncUF32);
-      case Op::I32TruncSF64:
-      case Op::I32TruncUF64:
-        return EmitTruncate(f, ValType::F64, ValType::I32, op == Op::I32TruncUF64);
-      case Op::I32WrapI64:
-        return EmitConversion<MWrapInt64ToInt32>(f, ValType::I64, ValType::I32);
-      case Op::I32ReinterpretF32:
-        return EmitReinterpret(f, ValType::I32, ValType::F32, MIRType::Int32);
-      case Op::I32Clz:
-        return EmitUnaryWithType<MClz>(f, ValType::I32, MIRType::Int32);
-      case Op::I32Ctz:
-        return EmitUnaryWithType<MCtz>(f, ValType::I32, MIRType::Int32);
-      case Op::I32Popcnt:
-        return EmitUnaryWithType<MPopcnt>(f, ValType::I32, MIRType::Int32);
-      case Op::I32Abs:
-        return EmitUnaryWithType<MAbs>(f, ValType::I32, MIRType::Int32);
-      case Op::I32Neg:
-        return EmitUnaryWithType<MAsmJSNeg>(f, ValType::I32, MIRType::Int32);
-      case Op::I32Or:
-        return EmitBitwise<MBitOr>(f, ValType::I32, MIRType::Int32);
-      case Op::I32And:
-        return EmitBitwise<MBitAnd>(f, ValType::I32, MIRType::Int32);
-      case Op::I32Xor:
-        return EmitBitwise<MBitXor>(f, ValType::I32, MIRType::Int32);
-      case Op::I32Shl:
-        return EmitBitwise<MLsh>(f, ValType::I32, MIRType::Int32);
-      case Op::I32ShrS:
-        return EmitBitwise<MRsh>(f, ValType::I32, MIRType::Int32);
-      case Op::I32ShrU:
-        return EmitBitwise<MUrsh>(f, ValType::I32, MIRType::Int32);
-      case Op::I32BitNot:
-        return EmitBitNot(f, ValType::I32);
-      case Op::I32Load8S:
-        return EmitLoad(f, ValType::I32, Scalar::Int8);
-      case Op::I32Load8U:
-        return EmitLoad(f, ValType::I32, Scalar::Uint8);
-      case Op::I32Load16S:
-        return EmitLoad(f, ValType::I32, Scalar::Int16);
-      case Op::I32Load16U:
-        return EmitLoad(f, ValType::I32, Scalar::Uint16);
-      case Op::I32Load:
-        return EmitLoad(f, ValType::I32, Scalar::Int32);
-      case Op::I32Store8:
-        return EmitStore(f, ValType::I32, Scalar::Int8);
-      case Op::I32TeeStore8:
-        return EmitTeeStore(f, ValType::I32, Scalar::Int8);
-      case Op::I32Store16:
-        return EmitStore(f, ValType::I32, Scalar::Int16);
-      case Op::I32TeeStore16:
-        return EmitTeeStore(f, ValType::I32, Scalar::Int16);
-      case Op::I32Store:
-        return EmitStore(f, ValType::I32, Scalar::Int32);
-      case Op::I32TeeStore:
-        return EmitTeeStore(f, ValType::I32, Scalar::Int32);
-      case Op::I32Rotr:
-      case Op::I32Rotl:
-        return EmitRotate(f, ValType::I32, op == Op::I32Rotl);
-
-      // I64
-      case Op::I64Const: {
-        int64_t i64;
-        if (!f.iter().readI64Const(&i64))
+        uint16_t op;
+        if (!f.iter().readOp(&op))
             return false;
 
-        f.iter().setResult(f.constant(i64));
-        return true;
-      }
-      case Op::I64Add:
-        return EmitAdd(f, ValType::I64, MIRType::Int64);
-      case Op::I64Sub:
-        return EmitSub(f, ValType::I64, MIRType::Int64);
-      case Op::I64Mul:
-        return EmitMul(f, ValType::I64, MIRType::Int64);
-      case Op::I64DivS:
-      case Op::I64DivU:
-        return EmitDiv(f, ValType::I64, MIRType::Int64, op == Op::I64DivU);
-      case Op::I64RemS:
-      case Op::I64RemU:
-        return EmitRem(f, ValType::I64, MIRType::Int64, op == Op::I64RemU);
-      case Op::I64TruncSF32:
-      case Op::I64TruncUF32:
-        return EmitTruncate(f, ValType::F32, ValType::I64, op == Op::I64TruncUF32);
-      case Op::I64TruncSF64:
-      case Op::I64TruncUF64:
-        return EmitTruncate(f, ValType::F64, ValType::I64, op == Op::I64TruncUF64);
-      case Op::I64ExtendSI32:
-      case Op::I64ExtendUI32:
-        return EmitExtendI32(f, op == Op::I64ExtendUI32);
-      case Op::I64ReinterpretF64:
-        return EmitReinterpret(f, ValType::I64, ValType::F64, MIRType::Int64);
-      case Op::I64Or:
-        return EmitBitwise<MBitOr>(f, ValType::I64, MIRType::Int64);
-      case Op::I64And:
-        return EmitBitwise<MBitAnd>(f, ValType::I64, MIRType::Int64);
-      case Op::I64Xor:
-        return EmitBitwise<MBitXor>(f, ValType::I64, MIRType::Int64);
-      case Op::I64Shl:
-        return EmitBitwise<MLsh>(f, ValType::I64, MIRType::Int64);
-      case Op::I64ShrS:
-        return EmitBitwise<MRsh>(f, ValType::I64, MIRType::Int64);
-      case Op::I64ShrU:
-        return EmitBitwise<MUrsh>(f, ValType::I64, MIRType::Int64);
-      case Op::I64Rotr:
-      case Op::I64Rotl:
-        return EmitRotate(f, ValType::I64, op == Op::I64Rotl);
-      case Op::I64Eqz:
-        return EmitConversion<MNot>(f, ValType::I64, ValType::I32);
-      case Op::I64Clz:
-        return EmitUnaryWithType<MClz>(f, ValType::I64, MIRType::Int64);
-      case Op::I64Ctz:
-        return EmitUnaryWithType<MCtz>(f, ValType::I64, MIRType::Int64);
-      case Op::I64Popcnt:
-        return EmitUnaryWithType<MPopcnt>(f, ValType::I64, MIRType::Int64);
-      case Op::I64Load8S:
-        return EmitLoad(f, ValType::I64, Scalar::Int8);
-      case Op::I64Load8U:
-        return EmitLoad(f, ValType::I64, Scalar::Uint8);
-      case Op::I64Load16S:
-        return EmitLoad(f, ValType::I64, Scalar::Int16);
-      case Op::I64Load16U:
-        return EmitLoad(f, ValType::I64, Scalar::Uint16);
-      case Op::I64Load32S:
-        return EmitLoad(f, ValType::I64, Scalar::Int32);
-      case Op::I64Load32U:
-        return EmitLoad(f, ValType::I64, Scalar::Uint32);
-      case Op::I64Load:
-        return EmitLoad(f, ValType::I64, Scalar::Int64);
-      case Op::I64Store8:
-        return EmitStore(f, ValType::I64, Scalar::Int8);
-      case Op::I64TeeStore8:
-        return EmitTeeStore(f, ValType::I64, Scalar::Int8);
-      case Op::I64Store16:
-        return EmitStore(f, ValType::I64, Scalar::Int16);
-      case Op::I64TeeStore16:
-        return EmitTeeStore(f, ValType::I64, Scalar::Int16);
-      case Op::I64Store32:
-        return EmitStore(f, ValType::I64, Scalar::Int32);
-      case Op::I64TeeStore32:
-        return EmitTeeStore(f, ValType::I64, Scalar::Int32);
-      case Op::I64Store:
-        return EmitStore(f, ValType::I64, Scalar::Int64);
-      case Op::I64TeeStore:
-        return EmitTeeStore(f, ValType::I64, Scalar::Int64);
-
-      // F32
-      case Op::F32Const: {
-        float f32;
-        if (!f.iter().readF32Const(&f32))
-            return false;
-
-        f.iter().setResult(f.constant(f32));
-        return true;
-      }
-      case Op::F32Add:
-        return EmitAdd(f, ValType::F32, MIRType::Float32);
-      case Op::F32Sub:
-        return EmitSub(f, ValType::F32, MIRType::Float32);
-      case Op::F32Mul:
-        return EmitMul(f, ValType::F32, MIRType::Float32);
-      case Op::F32Div:
-        return EmitDiv(f, ValType::F32, MIRType::Float32, /* isUnsigned = */ false);
-      case Op::F32Min:
-      case Op::F32Max:
-        return EmitMinMax(f, ValType::F32, MIRType::Float32, op == Op::F32Max);
-      case Op::F32CopySign:
-        return EmitCopySign(f, ValType::F32);
-      case Op::F32Neg:
-        return EmitUnaryWithType<MAsmJSNeg>(f, ValType::F32, MIRType::Float32);
-      case Op::F32Abs:
-        return EmitUnaryWithType<MAbs>(f, ValType::F32, MIRType::Float32);
-      case Op::F32Sqrt:
-        return EmitUnaryWithType<MSqrt>(f, ValType::F32, MIRType::Float32);
-      case Op::F32Ceil:
-        return EmitUnaryMathBuiltinCall(f, SymbolicAddress::CeilF, ValType::F32);
-      case Op::F32Floor:
-        return EmitUnaryMathBuiltinCall(f, SymbolicAddress::FloorF, ValType::F32);
-      case Op::F32Trunc:
-        return EmitUnaryMathBuiltinCall(f, SymbolicAddress::TruncF, ValType::F32);
-      case Op::F32Nearest:
-        return EmitUnaryMathBuiltinCall(f, SymbolicAddress::NearbyIntF, ValType::F32);
-      case Op::F32DemoteF64:
-        return EmitConversion<MToFloat32>(f, ValType::F64, ValType::F32);
-      case Op::F32ConvertSI32:
-        return EmitConversion<MToFloat32>(f, ValType::I32, ValType::F32);
-      case Op::F32ConvertUI32:
-        return EmitConversion<MWasmUnsignedToFloat32>(f, ValType::I32, ValType::F32);
-      case Op::F32ConvertSI64:
-      case Op::F32ConvertUI64:
-        return EmitConvertI64ToFloatingPoint(f, ValType::F32, MIRType::Float32,
-                                             op == Op::F32ConvertUI64);
-      case Op::F32ReinterpretI32:
-        return EmitReinterpret(f, ValType::F32, ValType::I32, MIRType::Float32);
-
-      case Op::F32Load:
-        return EmitLoad(f, ValType::F32, Scalar::Float32);
-      case Op::F32Store:
-        return EmitStore(f, ValType::F32, Scalar::Float32);
-      case Op::F32TeeStore:
-        return EmitTeeStore(f, ValType::F32, Scalar::Float32);
-      case Op::F32TeeStoreF64:
-        return EmitTeeStoreWithCoercion(f, ValType::F32, Scalar::Float64);
-
-      // F64
-      case Op::F64Const: {
-        double f64;
-        if (!f.iter().readF64Const(&f64))
-            return false;
-
-        f.iter().setResult(f.constant(f64));
-        return true;
-      }
-      case Op::F64Add:
-        return EmitAdd(f, ValType::F64, MIRType::Double);
-      case Op::F64Sub:
-        return EmitSub(f, ValType::F64, MIRType::Double);
-      case Op::F64Mul:
-        return EmitMul(f, ValType::F64, MIRType::Double);
-      case Op::F64Div:
-        return EmitDiv(f, ValType::F64, MIRType::Double, /* isUnsigned = */ false);
-      case Op::F64Mod:
-        return EmitRem(f, ValType::F64, MIRType::Double, /* isUnsigned = */ false);
-      case Op::F64Min:
-      case Op::F64Max:
-        return EmitMinMax(f, ValType::F64, MIRType::Double, op == Op::F64Max);
-      case Op::F64CopySign:
-        return EmitCopySign(f, ValType::F64);
-      case Op::F64Neg:
-        return EmitUnaryWithType<MAsmJSNeg>(f, ValType::F64, MIRType::Double);
-      case Op::F64Abs:
-        return EmitUnaryWithType<MAbs>(f, ValType::F64, MIRType::Double);
-      case Op::F64Sqrt:
-        return EmitUnaryWithType<MSqrt>(f, ValType::F64, MIRType::Double);
-      case Op::F64Ceil:
-        return EmitUnaryMathBuiltinCall(f, SymbolicAddress::CeilD, ValType::F64);
-      case Op::F64Floor:
-        return EmitUnaryMathBuiltinCall(f, SymbolicAddress::FloorD, ValType::F64);
-      case Op::F64Trunc:
-        return EmitUnaryMathBuiltinCall(f, SymbolicAddress::TruncD, ValType::F64);
-      case Op::F64Nearest:
-        return EmitUnaryMathBuiltinCall(f, SymbolicAddress::NearbyIntD, ValType::F64);
-      case Op::F64Sin:
-        return EmitUnaryMathBuiltinCall(f, SymbolicAddress::SinD, ValType::F64);
-      case Op::F64Cos:
-        return EmitUnaryMathBuiltinCall(f, SymbolicAddress::CosD, ValType::F64);
-      case Op::F64Tan:
-        return EmitUnaryMathBuiltinCall(f, SymbolicAddress::TanD, ValType::F64);
-      case Op::F64Asin:
-        return EmitUnaryMathBuiltinCall(f, SymbolicAddress::ASinD, ValType::F64);
-      case Op::F64Acos:
-        return EmitUnaryMathBuiltinCall(f, SymbolicAddress::ACosD, ValType::F64);
-      case Op::F64Atan:
-        return EmitUnaryMathBuiltinCall(f, SymbolicAddress::ATanD, ValType::F64);
-      case Op::F64Exp:
-        return EmitUnaryMathBuiltinCall(f, SymbolicAddress::ExpD, ValType::F64);
-      case Op::F64Log:
-        return EmitUnaryMathBuiltinCall(f, SymbolicAddress::LogD, ValType::F64);
-      case Op::F64Pow:
-        return EmitBinaryMathBuiltinCall(f, SymbolicAddress::PowD, ValType::F64);
-      case Op::F64Atan2:
-        return EmitBinaryMathBuiltinCall(f, SymbolicAddress::ATan2D, ValType::F64);
-      case Op::F64PromoteF32:
-        return EmitConversion<MToDouble>(f, ValType::F32, ValType::F64);
-      case Op::F64ConvertSI32:
-        return EmitConversion<MToDouble>(f, ValType::I32, ValType::F64);
-      case Op::F64ConvertUI32:
-        return EmitConversion<MWasmUnsignedToDouble>(f, ValType::I32, ValType::F64);
-      case Op::F64ConvertSI64:
-      case Op::F64ConvertUI64:
-        return EmitConvertI64ToFloatingPoint(f, ValType::F64, MIRType::Double,
-                                             op == Op::F64ConvertUI64);
-      case Op::F64Load:
-        return EmitLoad(f, ValType::F64, Scalar::Float64);
-      case Op::F64Store:
-        return EmitStore(f, ValType::F64, Scalar::Float64);
-      case Op::F64TeeStore:
-        return EmitTeeStore(f, ValType::F64, Scalar::Float64);
-      case Op::F64TeeStoreF32:
-        return EmitTeeStoreWithCoercion(f, ValType::F64, Scalar::Float32);
-      case Op::F64ReinterpretI64:
-        return EmitReinterpret(f, ValType::F64, ValType::I64, MIRType::Double);
-
-      // Comparisons
-      case Op::I32Eq:
-        return EmitComparison(f, ValType::I32, JSOP_EQ, MCompare::Compare_Int32);
-      case Op::I32Ne:
-        return EmitComparison(f, ValType::I32, JSOP_NE, MCompare::Compare_Int32);
-      case Op::I32LtS:
-        return EmitComparison(f, ValType::I32, JSOP_LT, MCompare::Compare_Int32);
-      case Op::I32LeS:
-        return EmitComparison(f, ValType::I32, JSOP_LE, MCompare::Compare_Int32);
-      case Op::I32GtS:
-        return EmitComparison(f, ValType::I32, JSOP_GT, MCompare::Compare_Int32);
-      case Op::I32GeS:
-        return EmitComparison(f, ValType::I32, JSOP_GE, MCompare::Compare_Int32);
-      case Op::I32LtU:
-        return EmitComparison(f, ValType::I32, JSOP_LT, MCompare::Compare_UInt32);
-      case Op::I32LeU:
-        return EmitComparison(f, ValType::I32, JSOP_LE, MCompare::Compare_UInt32);
-      case Op::I32GtU:
-        return EmitComparison(f, ValType::I32, JSOP_GT, MCompare::Compare_UInt32);
-      case Op::I32GeU:
-        return EmitComparison(f, ValType::I32, JSOP_GE, MCompare::Compare_UInt32);
-      case Op::I64Eq:
-        return EmitComparison(f, ValType::I64, JSOP_EQ, MCompare::Compare_Int64);
-      case Op::I64Ne:
-        return EmitComparison(f, ValType::I64, JSOP_NE, MCompare::Compare_Int64);
-      case Op::I64LtS:
-        return EmitComparison(f, ValType::I64, JSOP_LT, MCompare::Compare_Int64);
-      case Op::I64LeS:
-        return EmitComparison(f, ValType::I64, JSOP_LE, MCompare::Compare_Int64);
-      case Op::I64GtS:
-        return EmitComparison(f, ValType::I64, JSOP_GT, MCompare::Compare_Int64);
-      case Op::I64GeS:
-        return EmitComparison(f, ValType::I64, JSOP_GE, MCompare::Compare_Int64);
-      case Op::I64LtU:
-        return EmitComparison(f, ValType::I64, JSOP_LT, MCompare::Compare_UInt64);
-      case Op::I64LeU:
-        return EmitComparison(f, ValType::I64, JSOP_LE, MCompare::Compare_UInt64);
-      case Op::I64GtU:
-        return EmitComparison(f, ValType::I64, JSOP_GT, MCompare::Compare_UInt64);
-      case Op::I64GeU:
-        return EmitComparison(f, ValType::I64, JSOP_GE, MCompare::Compare_UInt64);
-      case Op::F32Eq:
-        return EmitComparison(f, ValType::F32, JSOP_EQ, MCompare::Compare_Float32);
-      case Op::F32Ne:
-        return EmitComparison(f, ValType::F32, JSOP_NE, MCompare::Compare_Float32);
-      case Op::F32Lt:
-        return EmitComparison(f, ValType::F32, JSOP_LT, MCompare::Compare_Float32);
-      case Op::F32Le:
-        return EmitComparison(f, ValType::F32, JSOP_LE, MCompare::Compare_Float32);
-      case Op::F32Gt:
-        return EmitComparison(f, ValType::F32, JSOP_GT, MCompare::Compare_Float32);
-      case Op::F32Ge:
-        return EmitComparison(f, ValType::F32, JSOP_GE, MCompare::Compare_Float32);
-      case Op::F64Eq:
-        return EmitComparison(f, ValType::F64, JSOP_EQ, MCompare::Compare_Double);
-      case Op::F64Ne:
-        return EmitComparison(f, ValType::F64, JSOP_NE, MCompare::Compare_Double);
-      case Op::F64Lt:
-        return EmitComparison(f, ValType::F64, JSOP_LT, MCompare::Compare_Double);
-      case Op::F64Le:
-        return EmitComparison(f, ValType::F64, JSOP_LE, MCompare::Compare_Double);
-      case Op::F64Gt:
-        return EmitComparison(f, ValType::F64, JSOP_GT, MCompare::Compare_Double);
-      case Op::F64Ge:
-        return EmitComparison(f, ValType::F64, JSOP_GE, MCompare::Compare_Double);
-
-      // SIMD
+        switch (op) {
+          case uint16_t(Op::End):
+            if (!EmitEnd(f))
+                return false;
+
+            if (f.iter().controlStackEmpty()) {
+                if (f.inDeadCode() || IsVoid(f.sig().ret()))
+                    f.returnVoid();
+                else
+                    f.returnExpr(f.iter().getResult());
+                return f.iter().readFunctionEnd(f.iter().end());
+            }
+            break;
+
+          // Control opcodes
+          case uint16_t(Op::Unreachable):
+            CHECK(EmitUnreachable(f));
+          case uint16_t(Op::Nop):
+            CHECK(f.iter().readNop());
+          case uint16_t(Op::Block):
+            CHECK(EmitBlock(f));
+          case uint16_t(Op::Loop):
+            CHECK(EmitLoop(f));
+          case uint16_t(Op::If):
+            CHECK(EmitIf(f));
+          case uint16_t(Op::Else):
+            CHECK(EmitElse(f));
+          case uint16_t(Op::Br):
+            CHECK(EmitBr(f));
+          case uint16_t(Op::BrIf):
+            CHECK(EmitBrIf(f));
+          case uint16_t(Op::BrTable):
+            CHECK(EmitBrTable(f));
+          case uint16_t(Op::Return):
+            CHECK(EmitReturn(f));
+
+          // Calls
+          case uint16_t(Op::Call):
+            CHECK(EmitCall(f));
+          case uint16_t(Op::CallIndirect):
+            CHECK(EmitCallIndirect(f, /* oldStyle = */ false));
+
+          // Parametric operators
+          case uint16_t(Op::Drop):
+            CHECK(f.iter().readDrop());
+          case uint16_t(Op::Select):
+            CHECK(EmitSelect(f));
+
+          // Locals and globals
+          case uint16_t(Op::GetLocal):
+            CHECK(EmitGetLocal(f));
+          case uint16_t(Op::SetLocal):
+            CHECK(EmitSetLocal(f));
+          case uint16_t(Op::TeeLocal):
+            CHECK(EmitTeeLocal(f));
+          case uint16_t(Op::GetGlobal):
+            CHECK(EmitGetGlobal(f));
+          case uint16_t(Op::SetGlobal):
+            CHECK(EmitSetGlobal(f));
+
+          // Memory-related operators
+          case uint16_t(Op::I32Load):
+            CHECK(EmitLoad(f, ValType::I32, Scalar::Int32));
+          case uint16_t(Op::I64Load):
+            CHECK(EmitLoad(f, ValType::I64, Scalar::Int64));
+          case uint16_t(Op::F32Load):
+            CHECK(EmitLoad(f, ValType::F32, Scalar::Float32));
+          case uint16_t(Op::F64Load):
+            CHECK(EmitLoad(f, ValType::F64, Scalar::Float64));
+          case uint16_t(Op::I32Load8S):
+            CHECK(EmitLoad(f, ValType::I32, Scalar::Int8));
+          case uint16_t(Op::I32Load8U):
+            CHECK(EmitLoad(f, ValType::I32, Scalar::Uint8));
+          case uint16_t(Op::I32Load16S):
+            CHECK(EmitLoad(f, ValType::I32, Scalar::Int16));
+          case uint16_t(Op::I32Load16U):
+            CHECK(EmitLoad(f, ValType::I32, Scalar::Uint16));
+          case uint16_t(Op::I64Load8S):
+            CHECK(EmitLoad(f, ValType::I64, Scalar::Int8));
+          case uint16_t(Op::I64Load8U):
+            CHECK(EmitLoad(f, ValType::I64, Scalar::Uint8));
+          case uint16_t(Op::I64Load16S):
+            CHECK(EmitLoad(f, ValType::I64, Scalar::Int16));
+          case uint16_t(Op::I64Load16U):
+            CHECK(EmitLoad(f, ValType::I64, Scalar::Uint16));
+          case uint16_t(Op::I64Load32S):
+            CHECK(EmitLoad(f, ValType::I64, Scalar::Int32));
+          case uint16_t(Op::I64Load32U):
+            CHECK(EmitLoad(f, ValType::I64, Scalar::Uint32));
+          case uint16_t(Op::I32Store):
+            CHECK(EmitStore(f, ValType::I32, Scalar::Int32));
+          case uint16_t(Op::I64Store):
+            CHECK(EmitStore(f, ValType::I64, Scalar::Int64));
+          case uint16_t(Op::F32Store):
+            CHECK(EmitStore(f, ValType::F32, Scalar::Float32));
+          case uint16_t(Op::F64Store):
+            CHECK(EmitStore(f, ValType::F64, Scalar::Float64));
+          case uint16_t(Op::I32Store8):
+            CHECK(EmitStore(f, ValType::I32, Scalar::Int8));
+          case uint16_t(Op::I32Store16):
+            CHECK(EmitStore(f, ValType::I32, Scalar::Int16));
+          case uint16_t(Op::I64Store8):
+            CHECK(EmitStore(f, ValType::I64, Scalar::Int8));
+          case uint16_t(Op::I64Store16):
+            CHECK(EmitStore(f, ValType::I64, Scalar::Int16));
+          case uint16_t(Op::I64Store32):
+            CHECK(EmitStore(f, ValType::I64, Scalar::Int32));
+          case uint16_t(Op::CurrentMemory):
+            CHECK(EmitCurrentMemory(f));
+          case uint16_t(Op::GrowMemory):
+            CHECK(EmitGrowMemory(f));
+
+          // Constants
+          case uint16_t(Op::I32Const):
+            CHECK(EmitI32Const(f));
+          case uint16_t(Op::I64Const):
+            CHECK(EmitI64Const(f));
+          case uint16_t(Op::F32Const):
+            CHECK(EmitF32Const(f));
+          case uint16_t(Op::F64Const):
+            CHECK(EmitF64Const(f));
+
+          // Comparison operators
+          case uint16_t(Op::I32Eqz):
+            CHECK(EmitConversion<MNot>(f, ValType::I32, ValType::I32));
+          case uint16_t(Op::I32Eq):
+            CHECK(EmitComparison(f, ValType::I32, JSOP_EQ, MCompare::Compare_Int32));
+          case uint16_t(Op::I32Ne):
+            CHECK(EmitComparison(f, ValType::I32, JSOP_NE, MCompare::Compare_Int32));
+          case uint16_t(Op::I32LtS):
+            CHECK(EmitComparison(f, ValType::I32, JSOP_LT, MCompare::Compare_Int32));
+          case uint16_t(Op::I32LtU):
+            CHECK(EmitComparison(f, ValType::I32, JSOP_LT, MCompare::Compare_UInt32));
+          case uint16_t(Op::I32GtS):
+            CHECK(EmitComparison(f, ValType::I32, JSOP_GT, MCompare::Compare_Int32));
+          case uint16_t(Op::I32GtU):
+            CHECK(EmitComparison(f, ValType::I32, JSOP_GT, MCompare::Compare_UInt32));
+          case uint16_t(Op::I32LeS):
+            CHECK(EmitComparison(f, ValType::I32, JSOP_LE, MCompare::Compare_Int32));
+          case uint16_t(Op::I32LeU):
+            CHECK(EmitComparison(f, ValType::I32, JSOP_LE, MCompare::Compare_UInt32));
+          case uint16_t(Op::I32GeS):
+            CHECK(EmitComparison(f, ValType::I32, JSOP_GE, MCompare::Compare_Int32));
+          case uint16_t(Op::I32GeU):
+            CHECK(EmitComparison(f, ValType::I32, JSOP_GE, MCompare::Compare_UInt32));
+          case uint16_t(Op::I64Eqz):
+            CHECK(EmitConversion<MNot>(f, ValType::I64, ValType::I32));
+          case uint16_t(Op::I64Eq):
+            CHECK(EmitComparison(f, ValType::I64, JSOP_EQ, MCompare::Compare_Int64));
+          case uint16_t(Op::I64Ne):
+            CHECK(EmitComparison(f, ValType::I64, JSOP_NE, MCompare::Compare_Int64));
+          case uint16_t(Op::I64LtS):
+            CHECK(EmitComparison(f, ValType::I64, JSOP_LT, MCompare::Compare_Int64));
+          case uint16_t(Op::I64LtU):
+            CHECK(EmitComparison(f, ValType::I64, JSOP_LT, MCompare::Compare_UInt64));
+          case uint16_t(Op::I64GtS):
+            CHECK(EmitComparison(f, ValType::I64, JSOP_GT, MCompare::Compare_Int64));
+          case uint16_t(Op::I64GtU):
+            CHECK(EmitComparison(f, ValType::I64, JSOP_GT, MCompare::Compare_UInt64));
+          case uint16_t(Op::I64LeS):
+            CHECK(EmitComparison(f, ValType::I64, JSOP_LE, MCompare::Compare_Int64));
+          case uint16_t(Op::I64LeU):
+            CHECK(EmitComparison(f, ValType::I64, JSOP_LE, MCompare::Compare_UInt64));
+          case uint16_t(Op::I64GeS):
+            CHECK(EmitComparison(f, ValType::I64, JSOP_GE, MCompare::Compare_Int64));
+          case uint16_t(Op::I64GeU):
+            CHECK(EmitComparison(f, ValType::I64, JSOP_GE, MCompare::Compare_UInt64));
+          case uint16_t(Op::F32Eq):
+            CHECK(EmitComparison(f, ValType::F32, JSOP_EQ, MCompare::Compare_Float32));
+          case uint16_t(Op::F32Ne):
+            CHECK(EmitComparison(f, ValType::F32, JSOP_NE, MCompare::Compare_Float32));
+          case uint16_t(Op::F32Lt):
+            CHECK(EmitComparison(f, ValType::F32, JSOP_LT, MCompare::Compare_Float32));
+          case uint16_t(Op::F32Gt):
+            CHECK(EmitComparison(f, ValType::F32, JSOP_GT, MCompare::Compare_Float32));
+          case uint16_t(Op::F32Le):
+            CHECK(EmitComparison(f, ValType::F32, JSOP_LE, MCompare::Compare_Float32));
+          case uint16_t(Op::F32Ge):
+            CHECK(EmitComparison(f, ValType::F32, JSOP_GE, MCompare::Compare_Float32));
+          case uint16_t(Op::F64Eq):
+            CHECK(EmitComparison(f, ValType::F64, JSOP_EQ, MCompare::Compare_Double));
+          case uint16_t(Op::F64Ne):
+            CHECK(EmitComparison(f, ValType::F64, JSOP_NE, MCompare::Compare_Double));
+          case uint16_t(Op::F64Lt):
+            CHECK(EmitComparison(f, ValType::F64, JSOP_LT, MCompare::Compare_Double));
+          case uint16_t(Op::F64Gt):
+            CHECK(EmitComparison(f, ValType::F64, JSOP_GT, MCompare::Compare_Double));
+          case uint16_t(Op::F64Le):
+            CHECK(EmitComparison(f, ValType::F64, JSOP_LE, MCompare::Compare_Double));
+          case uint16_t(Op::F64Ge):
+            CHECK(EmitComparison(f, ValType::F64, JSOP_GE, MCompare::Compare_Double));
+
+          // Numeric operators
+          case uint16_t(Op::I32Clz):
+            CHECK(EmitUnaryWithType<MClz>(f, ValType::I32, MIRType::Int32));
+          case uint16_t(Op::I32Ctz):
+            CHECK(EmitUnaryWithType<MCtz>(f, ValType::I32, MIRType::Int32));
+          case uint16_t(Op::I32Popcnt):
+            CHECK(EmitUnaryWithType<MPopcnt>(f, ValType::I32, MIRType::Int32));
+          case uint16_t(Op::I32Add):
+            CHECK(EmitAdd(f, ValType::I32, MIRType::Int32));
+          case uint16_t(Op::I32Sub):
+            CHECK(EmitSub(f, ValType::I32, MIRType::Int32));
+          case uint16_t(Op::I32Mul):
+            CHECK(EmitMul(f, ValType::I32, MIRType::Int32));
+          case uint16_t(Op::I32DivS):
+          case uint16_t(Op::I32DivU):
+            CHECK(EmitDiv(f, ValType::I32, MIRType::Int32, Op(op) == Op::I32DivU));
+          case uint16_t(Op::I32RemS):
+          case uint16_t(Op::I32RemU):
+            CHECK(EmitRem(f, ValType::I32, MIRType::Int32, Op(op) == Op::I32RemU));
+          case uint16_t(Op::I32And):
+            CHECK(EmitBitwise<MBitAnd>(f, ValType::I32, MIRType::Int32));
+          case uint16_t(Op::I32Or):
+            CHECK(EmitBitwise<MBitOr>(f, ValType::I32, MIRType::Int32));
+          case uint16_t(Op::I32Xor):
+            CHECK(EmitBitwise<MBitXor>(f, ValType::I32, MIRType::Int32));
+          case uint16_t(Op::I32Shl):
+            CHECK(EmitBitwise<MLsh>(f, ValType::I32, MIRType::Int32));
+          case uint16_t(Op::I32ShrS):
+            CHECK(EmitBitwise<MRsh>(f, ValType::I32, MIRType::Int32));
+          case uint16_t(Op::I32ShrU):
+            CHECK(EmitBitwise<MUrsh>(f, ValType::I32, MIRType::Int32));
+          case uint16_t(Op::I32Rotl):
+          case uint16_t(Op::I32Rotr):
+            CHECK(EmitRotate(f, ValType::I32, Op(op) == Op::I32Rotl));
+          case uint16_t(Op::I64Clz):
+            CHECK(EmitUnaryWithType<MClz>(f, ValType::I64, MIRType::Int64));
+          case uint16_t(Op::I64Ctz):
+            CHECK(EmitUnaryWithType<MCtz>(f, ValType::I64, MIRType::Int64));
+          case uint16_t(Op::I64Popcnt):
+            CHECK(EmitUnaryWithType<MPopcnt>(f, ValType::I64, MIRType::Int64));
+          case uint16_t(Op::I64Add):
+            CHECK(EmitAdd(f, ValType::I64, MIRType::Int64));
+          case uint16_t(Op::I64Sub):
+            CHECK(EmitSub(f, ValType::I64, MIRType::Int64));
+          case uint16_t(Op::I64Mul):
+            CHECK(EmitMul(f, ValType::I64, MIRType::Int64));
+          case uint16_t(Op::I64DivS):
+          case uint16_t(Op::I64DivU):
+            CHECK(EmitDiv(f, ValType::I64, MIRType::Int64, Op(op) == Op::I64DivU));
+          case uint16_t(Op::I64RemS):
+          case uint16_t(Op::I64RemU):
+            CHECK(EmitRem(f, ValType::I64, MIRType::Int64, Op(op) == Op::I64RemU));
+          case uint16_t(Op::I64And):
+            CHECK(EmitBitwise<MBitAnd>(f, ValType::I64, MIRType::Int64));
+          case uint16_t(Op::I64Or):
+            CHECK(EmitBitwise<MBitOr>(f, ValType::I64, MIRType::Int64));
+          case uint16_t(Op::I64Xor):
+            CHECK(EmitBitwise<MBitXor>(f, ValType::I64, MIRType::Int64));
+          case uint16_t(Op::I64Shl):
+            CHECK(EmitBitwise<MLsh>(f, ValType::I64, MIRType::Int64));
+          case uint16_t(Op::I64ShrS):
+            CHECK(EmitBitwise<MRsh>(f, ValType::I64, MIRType::Int64));
+          case uint16_t(Op::I64ShrU):
+            CHECK(EmitBitwise<MUrsh>(f, ValType::I64, MIRType::Int64));
+          case uint16_t(Op::I64Rotl):
+          case uint16_t(Op::I64Rotr):
+            CHECK(EmitRotate(f, ValType::I64, Op(op) == Op::I64Rotl));
+          case uint16_t(Op::F32Abs):
+            CHECK(EmitUnaryWithType<MAbs>(f, ValType::F32, MIRType::Float32));
+          case uint16_t(Op::F32Neg):
+            CHECK(EmitUnaryWithType<MAsmJSNeg>(f, ValType::F32, MIRType::Float32));
+          case uint16_t(Op::F32Ceil):
+            CHECK(EmitUnaryMathBuiltinCall(f, SymbolicAddress::CeilF, ValType::F32));
+          case uint16_t(Op::F32Floor):
+            CHECK(EmitUnaryMathBuiltinCall(f, SymbolicAddress::FloorF, ValType::F32));
+          case uint16_t(Op::F32Trunc):
+            CHECK(EmitUnaryMathBuiltinCall(f, SymbolicAddress::TruncF, ValType::F32));
+          case uint16_t(Op::F32Nearest):
+            CHECK(EmitUnaryMathBuiltinCall(f, SymbolicAddress::NearbyIntF, ValType::F32));
+          case uint16_t(Op::F32Sqrt):
+            CHECK(EmitUnaryWithType<MSqrt>(f, ValType::F32, MIRType::Float32));
+          case uint16_t(Op::F32Add):
+            CHECK(EmitAdd(f, ValType::F32, MIRType::Float32));
+          case uint16_t(Op::F32Sub):
+            CHECK(EmitSub(f, ValType::F32, MIRType::Float32));
+          case uint16_t(Op::F32Mul):
+            CHECK(EmitMul(f, ValType::F32, MIRType::Float32));
+          case uint16_t(Op::F32Div):
+            CHECK(EmitDiv(f, ValType::F32, MIRType::Float32, /* isUnsigned = */ false));
+          case uint16_t(Op::F32Min):
+          case uint16_t(Op::F32Max):
+            CHECK(EmitMinMax(f, ValType::F32, MIRType::Float32, Op(op) == Op::F32Max));
+          case uint16_t(Op::F32CopySign):
+            CHECK(EmitCopySign(f, ValType::F32));
+          case uint16_t(Op::F64Abs):
+            CHECK(EmitUnaryWithType<MAbs>(f, ValType::F64, MIRType::Double));
+          case uint16_t(Op::F64Neg):
+            CHECK(EmitUnaryWithType<MAsmJSNeg>(f, ValType::F64, MIRType::Double));
+          case uint16_t(Op::F64Ceil):
+            CHECK(EmitUnaryMathBuiltinCall(f, SymbolicAddress::CeilD, ValType::F64));
+          case uint16_t(Op::F64Floor):
+            CHECK(EmitUnaryMathBuiltinCall(f, SymbolicAddress::FloorD, ValType::F64));
+          case uint16_t(Op::F64Trunc):
+            CHECK(EmitUnaryMathBuiltinCall(f, SymbolicAddress::TruncD, ValType::F64));
+          case uint16_t(Op::F64Nearest):
+            CHECK(EmitUnaryMathBuiltinCall(f, SymbolicAddress::NearbyIntD, ValType::F64));
+          case uint16_t(Op::F64Sqrt):
+            CHECK(EmitUnaryWithType<MSqrt>(f, ValType::F64, MIRType::Double));
+          case uint16_t(Op::F64Add):
+            CHECK(EmitAdd(f, ValType::F64, MIRType::Double));
+          case uint16_t(Op::F64Sub):
+            CHECK(EmitSub(f, ValType::F64, MIRType::Double));
+          case uint16_t(Op::F64Mul):
+            CHECK(EmitMul(f, ValType::F64, MIRType::Double));
+          case uint16_t(Op::F64Div):
+            CHECK(EmitDiv(f, ValType::F64, MIRType::Double, /* isUnsigned = */ false));
+          case uint16_t(Op::F64Min):
+          case uint16_t(Op::F64Max):
+            CHECK(EmitMinMax(f, ValType::F64, MIRType::Double, Op(op) == Op::F64Max));
+          case uint16_t(Op::F64CopySign):
+            CHECK(EmitCopySign(f, ValType::F64));
+
+          // Conversions
+          case uint16_t(Op::I32WrapI64):
+            CHECK(EmitConversion<MWrapInt64ToInt32>(f, ValType::I64, ValType::I32));
+          case uint16_t(Op::I32TruncSF32):
+          case uint16_t(Op::I32TruncUF32):
+            CHECK(EmitTruncate(f, ValType::F32, ValType::I32, Op(op) == Op::I32TruncUF32));
+          case uint16_t(Op::I32TruncSF64):
+          case uint16_t(Op::I32TruncUF64):
+            CHECK(EmitTruncate(f, ValType::F64, ValType::I32, Op(op) == Op::I32TruncUF64));
+          case uint16_t(Op::I64ExtendSI32):
+          case uint16_t(Op::I64ExtendUI32):
+            CHECK(EmitExtendI32(f, Op(op) == Op::I64ExtendUI32));
+          case uint16_t(Op::I64TruncSF32):
+          case uint16_t(Op::I64TruncUF32):
+            CHECK(EmitTruncate(f, ValType::F32, ValType::I64, Op(op) == Op::I64TruncUF32));
+          case uint16_t(Op::I64TruncSF64):
+          case uint16_t(Op::I64TruncUF64):
+            CHECK(EmitTruncate(f, ValType::F64, ValType::I64, Op(op) == Op::I64TruncUF64));
+          case uint16_t(Op::F32ConvertSI32):
+            CHECK(EmitConversion<MToFloat32>(f, ValType::I32, ValType::F32));
+          case uint16_t(Op::F32ConvertUI32):
+            CHECK(EmitConversion<MWasmUnsignedToFloat32>(f, ValType::I32, ValType::F32));
+          case uint16_t(Op::F32ConvertSI64):
+          case uint16_t(Op::F32ConvertUI64):
+            CHECK(EmitConvertI64ToFloatingPoint(f, ValType::F32, MIRType::Float32, Op(op) == Op::F32ConvertUI64));
+          case uint16_t(Op::F32DemoteF64):
+            CHECK(EmitConversion<MToFloat32>(f, ValType::F64, ValType::F32));
+          case uint16_t(Op::F64ConvertSI32):
+            CHECK(EmitConversion<MToDouble>(f, ValType::I32, ValType::F64));
+          case uint16_t(Op::F64ConvertUI32):
+            CHECK(EmitConversion<MWasmUnsignedToDouble>(f, ValType::I32, ValType::F64));
+          case uint16_t(Op::F64ConvertSI64):
+          case uint16_t(Op::F64ConvertUI64):
+            CHECK(EmitConvertI64ToFloatingPoint(f, ValType::F64, MIRType::Double, Op(op) == Op::F64ConvertUI64));
+          case uint16_t(Op::F64PromoteF32):
+            CHECK(EmitConversion<MToDouble>(f, ValType::F32, ValType::F64));
+
+          // Reinterpretations
+          case uint16_t(Op::I32ReinterpretF32):
+            CHECK(EmitReinterpret(f, ValType::I32, ValType::F32, MIRType::Int32));
+          case uint16_t(Op::I64ReinterpretF64):
+            CHECK(EmitReinterpret(f, ValType::I64, ValType::F64, MIRType::Int64));
+          case uint16_t(Op::F32ReinterpretI32):
+            CHECK(EmitReinterpret(f, ValType::F32, ValType::I32, MIRType::Float32));
+          case uint16_t(Op::F64ReinterpretI64):
+            CHECK(EmitReinterpret(f, ValType::F64, ValType::I64, MIRType::Double));
+
+          // asm.js-specific operators
+
+          case uint16_t(Op::TeeGlobal):
+            CHECK_ASMJS(EmitTeeGlobal(f));
+          case uint16_t(Op::I32Min):
+          case uint16_t(Op::I32Max):
+            CHECK_ASMJS(EmitMinMax(f, ValType::I32, MIRType::Int32, Op(op) == Op::I32Max));
+          case uint16_t(Op::I32Neg):
+            CHECK_ASMJS(EmitUnaryWithType<MAsmJSNeg>(f, ValType::I32, MIRType::Int32));
+          case uint16_t(Op::I32BitNot):
+            CHECK_ASMJS(EmitBitNot(f, ValType::I32));
+          case uint16_t(Op::I32Abs):
+            CHECK_ASMJS(EmitUnaryWithType<MAbs>(f, ValType::I32, MIRType::Int32));
+          case uint16_t(Op::F32TeeStoreF64):
+            CHECK_ASMJS(EmitTeeStoreWithCoercion(f, ValType::F32, Scalar::Float64));
+          case uint16_t(Op::F64TeeStoreF32):
+            CHECK_ASMJS(EmitTeeStoreWithCoercion(f, ValType::F64, Scalar::Float32));
+          case uint16_t(Op::I32TeeStore8):
+            CHECK_ASMJS(EmitTeeStore(f, ValType::I32, Scalar::Int8));
+          case uint16_t(Op::I32TeeStore16):
+            CHECK_ASMJS(EmitTeeStore(f, ValType::I32, Scalar::Int16));
+          case uint16_t(Op::I64TeeStore8):
+            CHECK_ASMJS(EmitTeeStore(f, ValType::I64, Scalar::Int8));
+          case uint16_t(Op::I64TeeStore16):
+            CHECK_ASMJS(EmitTeeStore(f, ValType::I64, Scalar::Int16));
+          case uint16_t(Op::I64TeeStore32):
+            CHECK_ASMJS(EmitTeeStore(f, ValType::I64, Scalar::Int32));
+          case uint16_t(Op::I32TeeStore):
+            CHECK_ASMJS(EmitTeeStore(f, ValType::I32, Scalar::Int32));
+          case uint16_t(Op::I64TeeStore):
+            CHECK_ASMJS(EmitTeeStore(f, ValType::I64, Scalar::Int64));
+          case uint16_t(Op::F32TeeStore):
+            CHECK_ASMJS(EmitTeeStore(f, ValType::F32, Scalar::Float32));
+          case uint16_t(Op::F64TeeStore):
+            CHECK_ASMJS(EmitTeeStore(f, ValType::F64, Scalar::Float64));
+          case uint16_t(Op::F64Mod):
+            CHECK_ASMJS(EmitRem(f, ValType::F64, MIRType::Double, /* isUnsigned = */ false));
+          case uint16_t(Op::F64Sin):
+            CHECK_ASMJS(EmitUnaryMathBuiltinCall(f, SymbolicAddress::SinD, ValType::F64));
+          case uint16_t(Op::F64Cos):
+            CHECK_ASMJS(EmitUnaryMathBuiltinCall(f, SymbolicAddress::CosD, ValType::F64));
+          case uint16_t(Op::F64Tan):
+            CHECK_ASMJS(EmitUnaryMathBuiltinCall(f, SymbolicAddress::TanD, ValType::F64));
+          case uint16_t(Op::F64Asin):
+            CHECK_ASMJS(EmitUnaryMathBuiltinCall(f, SymbolicAddress::ASinD, ValType::F64));
+          case uint16_t(Op::F64Acos):
+            CHECK_ASMJS(EmitUnaryMathBuiltinCall(f, SymbolicAddress::ACosD, ValType::F64));
+          case uint16_t(Op::F64Atan):
+            CHECK_ASMJS(EmitUnaryMathBuiltinCall(f, SymbolicAddress::ATanD, ValType::F64));
+          case uint16_t(Op::F64Exp):
+            CHECK_ASMJS(EmitUnaryMathBuiltinCall(f, SymbolicAddress::ExpD, ValType::F64));
+          case uint16_t(Op::F64Log):
+            CHECK_ASMJS(EmitUnaryMathBuiltinCall(f, SymbolicAddress::LogD, ValType::F64));
+          case uint16_t(Op::F64Pow):
+            CHECK_ASMJS(EmitBinaryMathBuiltinCall(f, SymbolicAddress::PowD, ValType::F64));
+          case uint16_t(Op::F64Atan2):
+            CHECK_ASMJS(EmitBinaryMathBuiltinCall(f, SymbolicAddress::ATan2D, ValType::F64));
+          case uint16_t(Op::OldCallIndirect):
+            CHECK_ASMJS(EmitCallIndirect(f, /* oldStyle = */ true));
+
+          // Atomics
+          case uint16_t(Op::I32AtomicsLoad):
+            CHECK_ASMJS(EmitAtomicsLoad(f));
+          case uint16_t(Op::I32AtomicsStore):
+            CHECK_ASMJS(EmitAtomicsStore(f));
+          case uint16_t(Op::I32AtomicsBinOp):
+            CHECK_ASMJS(EmitAtomicsBinOp(f));
+          case uint16_t(Op::I32AtomicsCompareExchange):
+            CHECK_ASMJS(EmitAtomicsCompareExchange(f));
+          case uint16_t(Op::I32AtomicsExchange):
+            CHECK_ASMJS(EmitAtomicsExchange(f));
+
+          // SIMD
 #define CASE(TYPE, OP, SIGN)                                                    \
-      case Op::TYPE##OP:                                                      \
-        return EmitSimdOp(f, ValType::TYPE, SimdOperation::Fn_##OP, SIGN);
+          case uint16_t(Op::TYPE##OP):                                          \
+            CHECK_ASMJS(EmitSimdOp(f, ValType::TYPE, SimdOperation::Fn_##OP, SIGN));
 #define I8x16CASE(OP) CASE(I8x16, OP, SimdSign::Signed)
 #define I16x8CASE(OP) CASE(I16x8, OP, SimdSign::Signed)
 #define I32x4CASE(OP) CASE(I32x4, OP, SimdSign::Signed)
 #define F32x4CASE(OP) CASE(F32x4, OP, SimdSign::NotApplicable)
 #define B8x16CASE(OP) CASE(B8x16, OP, SimdSign::NotApplicable)
 #define B16x8CASE(OP) CASE(B16x8, OP, SimdSign::NotApplicable)
 #define B32x4CASE(OP) CASE(B32x4, OP, SimdSign::NotApplicable)
 #define ENUMERATE(TYPE, FORALL, DO)                                             \
-      case Op::TYPE##Constructor:                                             \
-        return EmitSimdOp(f, ValType::TYPE, SimdOperation::Constructor, SimdSign::NotApplicable); \
-      FORALL(DO)
-
-      ENUMERATE(I8x16, FORALL_INT8X16_ASMJS_OP, I8x16CASE)
-      ENUMERATE(I16x8, FORALL_INT16X8_ASMJS_OP, I16x8CASE)
-      ENUMERATE(I32x4, FORALL_INT32X4_ASMJS_OP, I32x4CASE)
-      ENUMERATE(F32x4, FORALL_FLOAT32X4_ASMJS_OP, F32x4CASE)
-      ENUMERATE(B8x16, FORALL_BOOL_SIMD_OP, B8x16CASE)
-      ENUMERATE(B16x8, FORALL_BOOL_SIMD_OP, B16x8CASE)
-      ENUMERATE(B32x4, FORALL_BOOL_SIMD_OP, B32x4CASE)
+          case uint16_t(Op::TYPE##Constructor):                                 \
+            CHECK_ASMJS(EmitSimdOp(f, ValType::TYPE, SimdOperation::Constructor, SimdSign::NotApplicable)); \
+          FORALL(DO)
+
+          ENUMERATE(I8x16, FORALL_INT8X16_ASMJS_OP, I8x16CASE)
+          ENUMERATE(I16x8, FORALL_INT16X8_ASMJS_OP, I16x8CASE)
+          ENUMERATE(I32x4, FORALL_INT32X4_ASMJS_OP, I32x4CASE)
+          ENUMERATE(F32x4, FORALL_FLOAT32X4_ASMJS_OP, F32x4CASE)
+          ENUMERATE(B8x16, FORALL_BOOL_SIMD_OP, B8x16CASE)
+          ENUMERATE(B16x8, FORALL_BOOL_SIMD_OP, B16x8CASE)
+          ENUMERATE(B32x4, FORALL_BOOL_SIMD_OP, B32x4CASE)
 
 #undef CASE
 #undef I8x16CASE
 #undef I16x8CASE
 #undef I32x4CASE
 #undef F32x4CASE
 #undef B8x16CASE
 #undef B16x8CASE
 #undef B32x4CASE
 #undef ENUMERATE
 
-      case Op::I8x16Const: {
-        I8x16 i8x16;
-        if (!f.iter().readI8x16Const(&i8x16))
-            return false;
-
-        f.iter().setResult(f.constant(SimdConstant::CreateX16(i8x16), MIRType::Int8x16));
-        return true;
-      }
-      case Op::I16x8Const: {
-        I16x8 i16x8;
-        if (!f.iter().readI16x8Const(&i16x8))
-            return false;
-
-        f.iter().setResult(f.constant(SimdConstant::CreateX8(i16x8), MIRType::Int16x8));
-        return true;
-      }
-      case Op::I32x4Const: {
-        I32x4 i32x4;
-        if (!f.iter().readI32x4Const(&i32x4))
-            return false;
-
-        f.iter().setResult(f.constant(SimdConstant::CreateX4(i32x4), MIRType::Int32x4));
-        return true;
-      }
-      case Op::F32x4Const: {
-        F32x4 f32x4;
-        if (!f.iter().readF32x4Const(&f32x4))
-            return false;
-
-        f.iter().setResult(f.constant(SimdConstant::CreateX4(f32x4), MIRType::Float32x4));
-        return true;
-      }
-      case Op::B8x16Const: {
-        I8x16 i8x16;
-        if (!f.iter().readB8x16Const(&i8x16))
-            return false;
-
-        f.iter().setResult(f.constant(SimdConstant::CreateX16(i8x16), MIRType::Bool8x16));
-        return true;
-      }
-      case Op::B16x8Const: {
-        I16x8 i16x8;
-        if (!f.iter().readB16x8Const(&i16x8))
-            return false;
-
-        f.iter().setResult(f.constant(SimdConstant::CreateX8(i16x8), MIRType::Bool16x8));
-        return true;
-      }
-      case Op::B32x4Const: {
-        I32x4 i32x4;
-        if (!f.iter().readB32x4Const(&i32x4))
-            return false;
-
-        f.iter().setResult(f.constant(SimdConstant::CreateX4(i32x4), MIRType::Bool32x4));
-        return true;
-      }
-
-      // SIMD unsigned integer operations.
-      case Op::I8x16addSaturateU:
-        return EmitSimdOp(f, ValType::I8x16, SimdOperation::Fn_addSaturate, SimdSign::Unsigned);
-      case Op::I8x16subSaturateU:
-        return EmitSimdOp(f, ValType::I8x16, SimdOperation::Fn_subSaturate, SimdSign::Unsigned);
-      case Op::I8x16shiftRightByScalarU:
-        return EmitSimdOp(f, ValType::I8x16, SimdOperation::Fn_shiftRightByScalar, SimdSign::Unsigned);
-      case Op::I8x16lessThanU:
-        return EmitSimdOp(f, ValType::I8x16, SimdOperation::Fn_lessThan, SimdSign::Unsigned);
-      case Op::I8x16lessThanOrEqualU:
-        return EmitSimdOp(f, ValType::I8x16, SimdOperation::Fn_lessThanOrEqual, SimdSign::Unsigned);
-      case Op::I8x16greaterThanU:
-        return EmitSimdOp(f, ValType::I8x16, SimdOperation::Fn_greaterThan, SimdSign::Unsigned);
-      case Op::I8x16greaterThanOrEqualU:
-        return EmitSimdOp(f, ValType::I8x16, SimdOperation::Fn_greaterThanOrEqual, SimdSign::Unsigned);
-      case Op::I8x16extractLaneU:
-        return EmitSimdOp(f, ValType::I8x16, SimdOperation::Fn_extractLane, SimdSign::Unsigned);
-
-      case Op::I16x8addSaturateU:
-        return EmitSimdOp(f, ValType::I16x8, SimdOperation::Fn_addSaturate, SimdSign::Unsigned);
-      case Op::I16x8subSaturateU:
-        return EmitSimdOp(f, ValType::I16x8, SimdOperation::Fn_subSaturate, SimdSign::Unsigned);
-      case Op::I16x8shiftRightByScalarU:
-        return EmitSimdOp(f, ValType::I16x8, SimdOperation::Fn_shiftRightByScalar, SimdSign::Unsigned);
-      case Op::I16x8lessThanU:
-        return EmitSimdOp(f, ValType::I16x8, SimdOperation::Fn_lessThan, SimdSign::Unsigned);
-      case Op::I16x8lessThanOrEqualU:
-        return EmitSimdOp(f, ValType::I16x8, SimdOperation::Fn_lessThanOrEqual, SimdSign::Unsigned);
-      case Op::I16x8greaterThanU:
-        return EmitSimdOp(f, ValType::I16x8, SimdOperation::Fn_greaterThan, SimdSign::Unsigned);
-      case Op::I16x8greaterThanOrEqualU:
-        return EmitSimdOp(f, ValType::I16x8, SimdOperation::Fn_greaterThanOrEqual, SimdSign::Unsigned);
-      case Op::I16x8extractLaneU:
-        return EmitSimdOp(f, ValType::I16x8, SimdOperation::Fn_extractLane, SimdSign::Unsigned);
-
-      case Op::I32x4shiftRightByScalarU:
-        return EmitSimdOp(f, ValType::I32x4, SimdOperation::Fn_shiftRightByScalar, SimdSign::Unsigned);
-      case Op::I32x4lessThanU:
-        return EmitSimdOp(f, ValType::I32x4, SimdOperation::Fn_lessThan, SimdSign::Unsigned);
-      case Op::I32x4lessThanOrEqualU:
-        return EmitSimdOp(f, ValType::I32x4, SimdOperation::Fn_lessThanOrEqual, SimdSign::Unsigned);
-      case Op::I32x4greaterThanU:
-        return EmitSimdOp(f, ValType::I32x4, SimdOperation::Fn_greaterThan, SimdSign::Unsigned);
-      case Op::I32x4greaterThanOrEqualU:
-        return EmitSimdOp(f, ValType::I32x4, SimdOperation::Fn_greaterThanOrEqual, SimdSign::Unsigned);
-      case Op::I32x4fromFloat32x4U:
-        return EmitSimdOp(f, ValType::I32x4, SimdOperation::Fn_fromFloat32x4, SimdSign::Unsigned);
-
-      // Atomics
-      case Op::I32AtomicsLoad:
-        return EmitAtomicsLoad(f);
-      case Op::I32AtomicsStore:
-        return EmitAtomicsStore(f);
-      case Op::I32AtomicsBinOp:
-        return EmitAtomicsBinOp(f);
-      case Op::I32AtomicsCompareExchange:
-        return EmitAtomicsCompareExchange(f);
-      case Op::I32AtomicsExchange:
-        return EmitAtomicsExchange(f);
-      // Memory Operators
-      case Op::GrowMemory:
-        return EmitGrowMemory(f);
-      case Op::CurrentMemory:
-        return EmitCurrentMemory(f);
-      case Op::Limit:;
+          case uint16_t(Op::I8x16Const):
+            CHECK_ASMJS(EmitI8x16Const(f));
+          case uint16_t(Op::I16x8Const):
+            CHECK_ASMJS(EmitI16x8Const(f));
+          case uint16_t(Op::I32x4Const):
+            CHECK_ASMJS(EmitI32x4Const(f));
+          case uint16_t(Op::F32x4Const):
+            CHECK_ASMJS(EmitF32x4Const(f));
+          case uint16_t(Op::B8x16Const):
+            CHECK_ASMJS(EmitB8x16Const(f));
+          case uint16_t(Op::B16x8Const):
+            CHECK_ASMJS(EmitB16x8Const(f));
+          case uint16_t(Op::B32x4Const):
+            CHECK_ASMJS(EmitB32x4Const(f));
+
+          case uint16_t(Op::I8x16addSaturateU):
+            CHECK_ASMJS(EmitSimdOp(f, ValType::I8x16, SimdOperation::Fn_addSaturate, SimdSign::Unsigned));
+          case uint16_t(Op::I8x16subSaturateU):
+            CHECK_ASMJS(EmitSimdOp(f, ValType::I8x16, SimdOperation::Fn_subSaturate, SimdSign::Unsigned));
+          case uint16_t(Op::I8x16shiftRightByScalarU):
+            CHECK_ASMJS(EmitSimdOp(f, ValType::I8x16, SimdOperation::Fn_shiftRightByScalar, SimdSign::Unsigned));
+          case uint16_t(Op::I8x16lessThanU):
+            CHECK_ASMJS(EmitSimdOp(f, ValType::I8x16, SimdOperation::Fn_lessThan, SimdSign::Unsigned));
+          case uint16_t(Op::I8x16lessThanOrEqualU):
+            CHECK_ASMJS(EmitSimdOp(f, ValType::I8x16, SimdOperation::Fn_lessThanOrEqual, SimdSign::Unsigned));
+          case uint16_t(Op::I8x16greaterThanU):
+            CHECK_ASMJS(EmitSimdOp(f, ValType::I8x16, SimdOperation::Fn_greaterThan, SimdSign::Unsigned));
+          case uint16_t(Op::I8x16greaterThanOrEqualU):
+            CHECK_ASMJS(EmitSimdOp(f, ValType::I8x16, SimdOperation::Fn_greaterThanOrEqual, SimdSign::Unsigned));
+          case uint16_t(Op::I8x16extractLaneU):
+            CHECK_ASMJS(EmitSimdOp(f, ValType::I8x16, SimdOperation::Fn_extractLane, SimdSign::Unsigned));
+
+          case uint16_t(Op::I16x8addSaturateU):
+            CHECK_ASMJS(EmitSimdOp(f, ValType::I16x8, SimdOperation::Fn_addSaturate, SimdSign::Unsigned));
+          case uint16_t(Op::I16x8subSaturateU):
+            CHECK_ASMJS(EmitSimdOp(f, ValType::I16x8, SimdOperation::Fn_subSaturate, SimdSign::Unsigned));
+          case uint16_t(Op::I16x8shiftRightByScalarU):
+            CHECK_ASMJS(EmitSimdOp(f, ValType::I16x8, SimdOperation::Fn_shiftRightByScalar, SimdSign::Unsigned));
+          case uint16_t(Op::I16x8lessThanU):
+            CHECK_ASMJS(EmitSimdOp(f, ValType::I16x8, SimdOperation::Fn_lessThan, SimdSign::Unsigned));
+          case uint16_t(Op::I16x8lessThanOrEqualU):
+            CHECK_ASMJS(EmitSimdOp(f, ValType::I16x8, SimdOperation::Fn_lessThanOrEqual, SimdSign::Unsigned));
+          case uint16_t(Op::I16x8greaterThanU):
+            CHECK_ASMJS(EmitSimdOp(f, ValType::I16x8, SimdOperation::Fn_greaterThan, SimdSign::Unsigned));
+          case uint16_t(Op::I16x8greaterThanOrEqualU):
+            CHECK_ASMJS(EmitSimdOp(f, ValType::I16x8, SimdOperation::Fn_greaterThanOrEqual, SimdSign::Unsigned));
+          case uint16_t(Op::I16x8extractLaneU):
+            CHECK_ASMJS(EmitSimdOp(f, ValType::I16x8, SimdOperation::Fn_extractLane, SimdSign::Unsigned));
+
+          case uint16_t(Op::I32x4shiftRightByScalarU):
+            CHECK_ASMJS(EmitSimdOp(f, ValType::I32x4, SimdOperation::Fn_shiftRightByScalar, SimdSign::Unsigned));
+          case uint16_t(Op::I32x4lessThanU):
+            CHECK_ASMJS(EmitSimdOp(f, ValType::I32x4, SimdOperation::Fn_lessThan, SimdSign::Unsigned));
+          case uint16_t(Op::I32x4lessThanOrEqualU):
+            CHECK_ASMJS(EmitSimdOp(f, ValType::I32x4, SimdOperation::Fn_lessThanOrEqual, SimdSign::Unsigned));
+          case uint16_t(Op::I32x4greaterThanU):
+            CHECK_ASMJS(EmitSimdOp(f, ValType::I32x4, SimdOperation::Fn_greaterThan, SimdSign::Unsigned));
+          case uint16_t(Op::I32x4greaterThanOrEqualU):
+            CHECK_ASMJS(EmitSimdOp(f, ValType::I32x4, SimdOperation::Fn_greaterThanOrEqual, SimdSign::Unsigned));
+          case uint16_t(Op::I32x4fromFloat32x4U):
+            CHECK_ASMJS(EmitSimdOp(f, ValType::I32x4, SimdOperation::Fn_fromFloat32x4, SimdSign::Unsigned));
+
+          default:
+            return f.iter().unrecognizedOpcode(op);
+        }
     }
 
-    MOZ_CRASH("unexpected wasm opcode");
+    MOZ_CRASH("unreachable");
+
+#undef CHECK
+#undef CHECK_ASMJS
 }
 
 bool
 wasm::IonCompileFunction(CompileTask* task, FuncCompileUnit* unit, UniqueChars* error)
 {
     MOZ_ASSERT(task->mode() == CompileMode::Ion);
 
     const FuncBytes& func = unit->func();
     const ModuleEnvironment& env = task->env();
-    uint32_t bodySize = func.bytes().length();
 
     Decoder d(func.bytes().begin(), func.bytes().end(), func.lineOrBytecode(), error);
 
-    if (!env.isAsmJS()) {
-        if (!ValidateFunctionBody(task->env(), func.index(), bodySize, d))
-            return false;
-
-        d.rollbackPosition(d.begin());
-    }
-
     // Build the local types vector.
 
     ValTypeVector locals;
     if (!locals.appendAll(func.sig().args()))
         return false;
     if (!DecodeLocalEntries(d, env.kind, &locals))
         return false;
 
@@ -3712,30 +3798,17 @@ wasm::IonCompileFunction(CompileTask* ta
     {
         FunctionCompiler f(env, d, func, locals, mir);
         if (!f.init())
             return false;
 
         if (!f.startBlock())
             return false;
 
-        if (!f.iter().readFunctionStart(f.sig().ret()))
-            return false;
-
-        while (!f.done()) {
-            if (!EmitExpr(f))
-                return false;
-        }
-
-        if (f.inDeadCode() || IsVoid(f.sig().ret()))
-            f.returnVoid();
-        else
-            f.returnExpr(f.iter().getResult());
-
-        if (!f.iter().readFunctionEnd())
+        if (!EmitBodyExprs(f))
             return false;
 
         f.finish();
     }
 
     // Compile MIR graph
     {
         jit::SpewBeginFunction(&mir, nullptr);
--- a/js/src/wasm/WasmValidate.cpp
+++ b/js/src/wasm/WasmValidate.cpp
@@ -295,285 +295,364 @@ wasm::DecodeLocalEntries(Decoder& d, Mod
             return false;
     }
 
     return true;
 }
 
 // Function body validation.
 
-struct ValidatingPolicy : OpIterPolicy
+struct ValidatingPolicy
 {
-    // Validation is what we're all about here.
-    static const bool Validate = true;
+    typedef Nothing Value;
+    typedef Nothing ControlItem;
 };
 
 typedef OpIter<ValidatingPolicy> ValidatingOpIter;
 
 static bool
 DecodeFunctionBodyExprs(const ModuleEnvironment& env, const Sig& sig, const ValTypeVector& locals,
-                        Decoder* d)
+                        const uint8_t* bodyEnd, Decoder* d)
 {
     ValidatingOpIter iter(env, *d);
 
     if (!iter.readFunctionStart(sig.ret()))
         return false;
 
 #define CHECK(c) if (!(c)) return false; break
 
     while (true) {
         uint16_t op;
         if (!iter.readOp(&op))
             return false;
 
+        Nothing nothing;
+
         switch (op) {
-          case uint16_t(Op::End):
-            if (!iter.readEnd(nullptr, nullptr, nullptr))
+          case uint16_t(Op::End): {
+            LabelKind unusedKind;
+            ExprType unusedType;
+            if (!iter.readEnd(&unusedKind, &unusedType, &nothing))
                 return false;
             iter.popEnd();
             if (iter.controlStackEmpty())
-                return iter.readFunctionEnd();
+                return iter.readFunctionEnd(bodyEnd);
             break;
+          }
           case uint16_t(Op::Nop):
             CHECK(iter.readNop());
           case uint16_t(Op::Drop):
             CHECK(iter.readDrop());
-          case uint16_t(Op::Call):
-            CHECK(iter.readCall(nullptr, nullptr));
-          case uint16_t(Op::CallIndirect):
-            CHECK(iter.readCallIndirect(nullptr, nullptr, nullptr));
-          case uint16_t(Op::I32Const):
-            CHECK(iter.readI32Const(nullptr));
-          case uint16_t(Op::I64Const):
-            CHECK(iter.readI64Const(nullptr));
-          case uint16_t(Op::F32Const):
-            CHECK(iter.readF32Const(nullptr));
-          case uint16_t(Op::F64Const):
-            CHECK(iter.readF64Const(nullptr));
-          case uint16_t(Op::GetLocal):
-            CHECK(iter.readGetLocal(locals, nullptr));
-          case uint16_t(Op::SetLocal):
-            CHECK(iter.readSetLocal(locals, nullptr, nullptr));
-          case uint16_t(Op::TeeLocal):
-            CHECK(iter.readTeeLocal(locals, nullptr, nullptr));
-          case uint16_t(Op::GetGlobal):
-            CHECK(iter.readGetGlobal(nullptr));
-          case uint16_t(Op::SetGlobal):
-            CHECK(iter.readSetGlobal(nullptr, nullptr));
-          case uint16_t(Op::Select):
-            CHECK(iter.readSelect(nullptr, nullptr, nullptr, nullptr));
+          case uint16_t(Op::Call): {
+            uint32_t unusedIndex;
+            ValidatingOpIter::ValueVector unusedArgs;
+            CHECK(iter.readCall(&unusedIndex, &unusedArgs));
+          }
+          case uint16_t(Op::CallIndirect): {
+            uint32_t unusedIndex;
+            ValidatingOpIter::ValueVector unusedArgs;
+            CHECK(iter.readCallIndirect(&unusedIndex, &nothing, &unusedArgs));
+          }
+          case uint16_t(Op::I32Const): {
+            int32_t unused;
+            CHECK(iter.readI32Const(&unused));
+          }
+          case uint16_t(Op::I64Const): {
+            int64_t unused;
+            CHECK(iter.readI64Const(&unused));
+          }
+          case uint16_t(Op::F32Const): {
+            float unused;
+            CHECK(iter.readF32Const(&unused));
+          }
+          case uint16_t(Op::F64Const): {
+            double unused;
+            CHECK(iter.readF64Const(&unused));
+          }
+          case uint16_t(Op::GetLocal): {
+            uint32_t unused;
+            CHECK(iter.readGetLocal(locals, &unused));
+          }
+          case uint16_t(Op::SetLocal): {
+            uint32_t unused;
+            CHECK(iter.readSetLocal(locals, &unused, &nothing));
+          }
+          case uint16_t(Op::TeeLocal): {
+            uint32_t unused;
+            CHECK(iter.readTeeLocal(locals, &unused, &nothing));
+          }
+          case uint16_t(Op::GetGlobal): {
+            uint32_t unused;
+            CHECK(iter.readGetGlobal(&unused));
+          }
+          case uint16_t(Op::SetGlobal): {
+            uint32_t unused;
+            CHECK(iter.readSetGlobal(&unused, &nothing));
+          }
+          case uint16_t(Op::Select): {
+            StackType unused;
+            CHECK(iter.readSelect(&unused, &nothing, &nothing, &nothing));
+          }
           case uint16_t(Op::Block):
             CHECK(iter.readBlock());
           case uint16_t(Op::Loop):
             CHECK(iter.readLoop());
           case uint16_t(Op::If):
-            CHECK(iter.readIf(nullptr));
-          case uint16_t(Op::Else):
-            CHECK(iter.readElse(nullptr, nullptr));
+            CHECK(iter.readIf(&nothing));
+          case uint16_t(Op::Else): {
+            ExprType type;
+            CHECK(iter.readElse(&type, &nothing));
+          }
           case uint16_t(Op::I32Clz):
           case uint16_t(Op::I32Ctz):
           case uint16_t(Op::I32Popcnt):
-            CHECK(iter.readUnary(ValType::I32, nullptr));
+            CHECK(iter.readUnary(ValType::I32, &nothing));
           case uint16_t(Op::I64Clz):
           case uint16_t(Op::I64Ctz):
           case uint16_t(Op::I64Popcnt):
-            CHECK(iter.readUnary(ValType::I64, nullptr));
+            CHECK(iter.readUnary(ValType::I64, &nothing));
           case uint16_t(Op::F32Abs):
           case uint16_t(Op::F32Neg):
           case uint16_t(Op::F32Ceil):
           case uint16_t(Op::F32Floor):
           case uint16_t(Op::F32Sqrt):
           case uint16_t(Op::F32Trunc):
           case uint16_t(Op::F32Nearest):
-            CHECK(iter.readUnary(ValType::F32, nullptr));
+            CHECK(iter.readUnary(ValType::F32, &nothing));
           case uint16_t(Op::F64Abs):
           case uint16_t(Op::F64Neg):
           case uint16_t(Op::F64Ceil):
           case uint16_t(Op::F64Floor):
           case uint16_t(Op::F64Sqrt):
           case uint16_t(Op::F64Trunc):
           case uint16_t(Op::F64Nearest):
-            CHECK(iter.readUnary(ValType::F64, nullptr));
+            CHECK(iter.readUnary(ValType::F64, &nothing));
           case uint16_t(Op::I32Add):
           case uint16_t(Op::I32Sub):
           case uint16_t(Op::I32Mul):
           case uint16_t(Op::I32DivS):
           case uint16_t(Op::I32DivU):
           case uint16_t(Op::I32RemS):
           case uint16_t(Op::I32RemU):
           case uint16_t(Op::I32And):
           case uint16_t(Op::I32Or):
           case uint16_t(Op::I32Xor):
           case uint16_t(Op::I32Shl):
           case uint16_t(Op::I32ShrS):
           case uint16_t(Op::I32ShrU):
           case uint16_t(Op::I32Rotl):
           case uint16_t(Op::I32Rotr):
-            CHECK(iter.readBinary(ValType::I32, nullptr, nullptr));
+            CHECK(iter.readBinary(ValType::I32, &nothing, &nothing));
           case uint16_t(Op::I64Add):
           case uint16_t(Op::I64Sub):
           case uint16_t(Op::I64Mul):
           case uint16_t(Op::I64DivS):
           case uint16_t(Op::I64DivU):
           case uint16_t(Op::I64RemS):
           case uint16_t(Op::I64RemU):
           case uint16_t(Op::I64And):
           case uint16_t(Op::I64Or):
           case uint16_t(Op::I64Xor):
           case uint16_t(Op::I64Shl):
           case uint16_t(Op::I64ShrS):
           case uint16_t(Op::I64ShrU):
           case uint16_t(Op::I64Rotl):
           case uint16_t(Op::I64Rotr):
-            CHECK(iter.readBinary(ValType::I64, nullptr, nullptr));
+            CHECK(iter.readBinary(ValType::I64, &nothing, &nothing));
           case uint16_t(Op::F32Add):
           case uint16_t(Op::F32Sub):
           case uint16_t(Op::F32Mul):
           case uint16_t(Op::F32Div):
           case uint16_t(Op::F32Min):
           case uint16_t(Op::F32Max):
           case uint16_t(Op::F32CopySign):
-            CHECK(iter.readBinary(ValType::F32, nullptr, nullptr));
+            CHECK(iter.readBinary(ValType::F32, &nothing, &nothing));
           case uint16_t(Op::F64Add):
           case uint16_t(Op::F64Sub):
           case uint16_t(Op::F64Mul):
           case uint16_t(Op::F64Div):
           case uint16_t(Op::F64Min):
           case uint16_t(Op::F64Max):
           case uint16_t(Op::F64CopySign):
-            CHECK(iter.readBinary(ValType::F64, nullptr, nullptr));
+            CHECK(iter.readBinary(ValType::F64, &nothing, &nothing));
           case uint16_t(Op::I32Eq):
           case uint16_t(Op::I32Ne):
           case uint16_t(Op::I32LtS):
           case uint16_t(Op::I32LtU):
           case uint16_t(Op::I32LeS):
           case uint16_t(Op::I32LeU):
           case uint16_t(Op::I32GtS):
           case uint16_t(Op::I32GtU):
           case uint16_t(Op::I32GeS):
           case uint16_t(Op::I32GeU):
-            CHECK(iter.readComparison(ValType::I32, nullptr, nullptr));
+            CHECK(iter.readComparison(ValType::I32, &nothing, &nothing));
           case uint16_t(Op::I64Eq):
           case uint16_t(Op::I64Ne):
           case uint16_t(Op::I64LtS):
           case uint16_t(Op::I64LtU):
           case uint16_t(Op::I64LeS):
           case uint16_t(Op::I64LeU):
           case uint16_t(Op::I64GtS):
           case uint16_t(Op::I64GtU):
           case uint16_t(Op::I64GeS):
           case uint16_t(Op::I64GeU):
-            CHECK(iter.readComparison(ValType::I64, nullptr, nullptr));
+            CHECK(iter.readComparison(ValType::I64, &nothing, &nothing));
           case uint16_t(Op::F32Eq):
           case uint16_t(Op::F32Ne):
           case uint16_t(Op::F32Lt):
           case uint16_t(Op::F32Le):
           case uint16_t(Op::F32Gt):
           case uint16_t(Op::F32Ge):
-            CHECK(iter.readComparison(ValType::F32, nullptr, nullptr));
+            CHECK(iter.readComparison(ValType::F32, &nothing, &nothing));
           case uint16_t(Op::F64Eq):
           case uint16_t(Op::F64Ne):
           case uint16_t(Op::F64Lt):
           case uint16_t(Op::F64Le):
           case uint16_t(Op::F64Gt):
           case uint16_t(Op::F64Ge):
-            CHECK(iter.readComparison(ValType::F64, nullptr, nullptr));
+            CHECK(iter.readComparison(ValType::F64, &nothing, &nothing));
           case uint16_t(Op::I32Eqz):
-            CHECK(iter.readConversion(ValType::I32, ValType::I32, nullptr));
+            CHECK(iter.readConversion(ValType::I32, ValType::I32, &nothing));
           case uint16_t(Op::I64Eqz):
           case uint16_t(Op::I32WrapI64):
-            CHECK(iter.readConversion(ValType::I64, ValType::I32, nullptr));
+            CHECK(iter.readConversion(ValType::I64, ValType::I32, &nothing));
           case uint16_t(Op::I32TruncSF32):
           case uint16_t(Op::I32TruncUF32):
           case uint16_t(Op::I32ReinterpretF32):
-            CHECK(iter.readConversion(ValType::F32, ValType::I32, nullptr));
+            CHECK(iter.readConversion(ValType::F32, ValType::I32, &nothing));
           case uint16_t(Op::I32TruncSF64):
           case uint16_t(Op::I32TruncUF64):
-            CHECK(iter.readConversion(ValType::F64, ValType::I32, nullptr));
+            CHECK(iter.readConversion(ValType::F64, ValType::I32, &nothing));
           case uint16_t(Op::I64ExtendSI32):
           case uint16_t(Op::I64ExtendUI32):
-            CHECK(iter.readConversion(ValType::I32, ValType::I64, nullptr));
+            CHECK(iter.readConversion(ValType::I32, ValType::I64, &nothing));
           case uint16_t(Op::I64TruncSF32):
           case uint16_t(Op::I64TruncUF32):
-            CHECK(iter.readConversion(ValType::F32, ValType::I64, nullptr));
+            CHECK(iter.readConversion(ValType::F32, ValType::I64, &nothing));
           case uint16_t(Op::I64TruncSF64):
           case uint16_t(Op::I64TruncUF64):
           case uint16_t(Op::I64ReinterpretF64):
-            CHECK(iter.readConversion(ValType::F64, ValType::I64, nullptr));
+            CHECK(iter.readConversion(ValType::F64, ValType::I64, &nothing));
           case uint16_t(Op::F32ConvertSI32):
           case uint16_t(Op::F32ConvertUI32):
           case uint16_t(Op::F32ReinterpretI32):
-            CHECK(iter.readConversion(ValType::I32, ValType::F32, nullptr));
+            CHECK(iter.readConversion(ValType::I32, ValType::F32, &nothing));
           case uint16_t(Op::F32ConvertSI64):
           case uint16_t(Op::F32ConvertUI64):
-            CHECK(iter.readConversion(ValType::I64, ValType::F32, nullptr));
+            CHECK(iter.readConversion(ValType::I64, ValType::F32, &nothing));
           case uint16_t(Op::F32DemoteF64):
-            CHECK(iter.readConversion(ValType::F64, ValType::F32, nullptr));
+            CHECK(iter.readConversion(ValType::F64, ValType::F32, &nothing));
           case uint16_t(Op::F64ConvertSI32):
           case uint16_t(Op::F64ConvertUI32):
-            CHECK(iter.readConversion(ValType::I32, ValType::F64, nullptr));
+            CHECK(iter.readConversion(ValType::I32, ValType::F64, &nothing));
           case uint16_t(Op::F64ConvertSI64):
           case uint16_t(Op::F64ConvertUI64):
           case uint16_t(Op::F64ReinterpretI64):
-            CHECK(iter.readConversion(ValType::I64, ValType::F64, nullptr));
+            CHECK(iter.readConversion(ValType::I64, ValType::F64, &nothing));
           case uint16_t(Op::F64PromoteF32):
-            CHECK(iter.readConversion(ValType::F32, ValType::F64, nullptr));
+            CHECK(iter.readConversion(ValType::F32, ValType::F64, &nothing));
           case uint16_t(Op::I32Load8S):
-          case uint16_t(Op::I32Load8U):
-            CHECK(iter.readLoad(ValType::I32, 1, nullptr));
+          case uint16_t(Op::I32Load8U): {
+            LinearMemoryAddress<Nothing> addr;
+            CHECK(iter.readLoad(ValType::I32, 1, &addr));
+          }
           case uint16_t(Op::I32Load16S):
-          case uint16_t(Op::I32Load16U):
-            CHECK(iter.readLoad(ValType::I32, 2, nullptr));
-          case uint16_t(Op::I32Load):
-            CHECK(iter.readLoad(ValType::I32, 4, nullptr));
+          case uint16_t(Op::I32Load16U): {
+            LinearMemoryAddress<Nothing> addr;
+            CHECK(iter.readLoad(ValType::I32, 2, &addr));
+          }
+          case uint16_t(Op::I32Load): {
+            LinearMemoryAddress<Nothing> addr;
+            CHECK(iter.readLoad(ValType::I32, 4, &addr));
+          }
           case uint16_t(Op::I64Load8S):
-          case uint16_t(Op::I64Load8U):
-            CHECK(iter.readLoad(ValType::I64, 1, nullptr));
+          case uint16_t(Op::I64Load8U): {
+            LinearMemoryAddress<Nothing> addr;
+            CHECK(iter.readLoad(ValType::I64, 1, &addr));
+          }
           case uint16_t(Op::I64Load16S):
-          case uint16_t(Op::I64Load16U):
-            CHECK(iter.readLoad(ValType::I64, 2, nullptr));
+          case uint16_t(Op::I64Load16U): {
+            LinearMemoryAddress<Nothing> addr;
+            CHECK(iter.readLoad(ValType::I64, 2, &addr));
+          }
           case uint16_t(Op::I64Load32S):
-          case uint16_t(Op::I64Load32U):
-            CHECK(iter.readLoad(ValType::I64, 4, nullptr));
-          case uint16_t(Op::I64Load):
-            CHECK(iter.readLoad(ValType::I64, 8, nullptr));
-          case uint16_t(Op::F32Load):
-            CHECK(iter.readLoad(ValType::F32, 4, nullptr));
-          case uint16_t(Op::F64Load):
-            CHECK(iter.readLoad(ValType::F64, 8, nullptr));
-          case uint16_t(Op::I32Store8):
-            CHECK(iter.readStore(ValType::I32, 1, nullptr, nullptr));
-          case uint16_t(Op::I32Store16):
-            CHECK(iter.readStore(ValType::I32, 2, nullptr, nullptr));
-          case uint16_t(Op::I32Store):
-            CHECK(iter.readStore(ValType::I32, 4, nullptr, nullptr));
-          case uint16_t(Op::I64Store8):
-            CHECK(iter.readStore(ValType::I64, 1, nullptr, nullptr));
-          case uint16_t(Op::I64Store16):
-            CHECK(iter.readStore(ValType::I64, 2, nullptr, nullptr));
-          case uint16_t(Op::I64Store32):
-            CHECK(iter.readStore(ValType::I64, 4, nullptr, nullptr));
-          case uint16_t(Op::I64Store):
-            CHECK(iter.readStore(ValType::I64, 8, nullptr, nullptr));
-          case uint16_t(Op::F32Store):
-            CHECK(iter.readStore(ValType::F32, 4, nullptr, nullptr));
-          case uint16_t(Op::F64Store):
-            CHECK(iter.readStore(ValType::F64, 8, nullptr, nullptr));
+          case uint16_t(Op::I64Load32U): {
+            LinearMemoryAddress<Nothing> addr;
+            CHECK(iter.readLoad(ValType::I64, 4, &addr));
+          }
+          case uint16_t(Op::I64Load): {
+            LinearMemoryAddress<Nothing> addr;
+            CHECK(iter.readLoad(ValType::I64, 8, &addr));
+          }
+          case uint16_t(Op::F32Load): {
+            LinearMemoryAddress<Nothing> addr;
+            CHECK(iter.readLoad(ValType::F32, 4, &addr));
+          }
+          case uint16_t(Op::F64Load): {
+            LinearMemoryAddress<Nothing> addr;
+            CHECK(iter.readLoad(ValType::F64, 8, &addr));
+          }
+          case uint16_t(Op::I32Store8): {
+            LinearMemoryAddress<Nothing> addr;
+            CHECK(iter.readStore(ValType::I32, 1, &addr, &nothing));
+          }
+          case uint16_t(Op::I32Store16): {
+            LinearMemoryAddress<Nothing> addr;
+            CHECK(iter.readStore(ValType::I32, 2, &addr, &nothing));
+          }
+          case uint16_t(Op::I32Store): {
+            LinearMemoryAddress<Nothing> addr;
+            CHECK(iter.readStore(ValType::I32, 4, &addr, &nothing));
+          }
+          case uint16_t(Op::I64Store8): {
+            LinearMemoryAddress<Nothing> addr;
+            CHECK(iter.readStore(ValType::I64, 1, &addr, &nothing));
+          }
+          case uint16_t(Op::I64Store16): {
+            LinearMemoryAddress<Nothing> addr;
+            CHECK(iter.readStore(ValType::I64, 2, &addr, &nothing));
+          }
+          case uint16_t(Op::I64Store32): {
+            LinearMemoryAddress<Nothing> addr;
+            CHECK(iter.readStore(ValType::I64, 4, &addr, &nothing));
+          }
+          case uint16_t(Op::I64Store): {
+            LinearMemoryAddress<Nothing> addr;
+            CHECK(iter.readStore(ValType::I64, 8, &addr, &nothing));
+          }
+          case uint16_t(Op::F32Store): {
+            LinearMemoryAddress<Nothing> addr;
+            CHECK(iter.readStore(ValType::F32, 4, &addr, &nothing));
+          }
+          case uint16_t(Op::F64Store): {
+            LinearMemoryAddress<Nothing> addr;
+            CHECK(iter.readStore(ValType::F64, 8, &addr, &nothing));
+          }
           case uint16_t(Op::GrowMemory):
-            CHECK(iter.readGrowMemory(nullptr));
+            CHECK(iter.readGrowMemory(&nothing));
           case uint16_t(Op::CurrentMemory):
             CHECK(iter.readCurrentMemory());
-          case uint16_t(Op::Br):
-            CHECK(iter.readBr(nullptr, nullptr, nullptr));
-          case uint16_t(Op::BrIf):
-            CHECK(iter.readBrIf(nullptr, nullptr, nullptr, nullptr));
-          case uint16_t(Op::BrTable):
-            CHECK(iter.readBrTable(nullptr, nullptr, nullptr, nullptr, nullptr));
+          case uint16_t(Op::Br): {
+            uint32_t unusedDepth;
+            ExprType unusedType;
+            CHECK(iter.readBr(&unusedDepth, &unusedType, &nothing));
+          }
+          case uint16_t(Op::BrIf): {
+            uint32_t unusedDepth;
+            ExprType unusedType;
+            CHECK(iter.readBrIf(&unusedDepth, &unusedType, &nothing, &nothing));
+          }
+          case uint16_t(Op::BrTable): {
+            Uint32Vector unusedDepths;
+            uint32_t unusedDefault;
+            ExprType unusedType;
+            CHECK(iter.readBrTable(&unusedDepths, &unusedDefault, &unusedType, &nothing, &nothing));
+          }
           case uint16_t(Op::Return):
-            CHECK(iter.readReturn(nullptr));
+            CHECK(iter.readReturn(&nothing));
           case uint16_t(Op::Unreachable):
             CHECK(iter.readUnreachable());
           default:
             return iter.unrecognizedOpcode(op);
         }
     }
 
     MOZ_CRASH("unreachable");
@@ -591,22 +670,19 @@ wasm::ValidateFunctionBody(const ModuleE
     if (!locals.appendAll(sig.args()))
         return false;
 
     const uint8_t* bodyBegin = d.currentPosition();
 
     if (!DecodeLocalEntries(d, ModuleKind::Wasm, &locals))
         return false;
 
-    if (!DecodeFunctionBodyExprs(env, sig, locals, &d))
+    if (!DecodeFunctionBodyExprs(env, sig, locals, bodyBegin + bodySize, &d))
         return false;
 
-    if (d.currentPosition() != bodyBegin + bodySize)
-        return d.fail("function body length mismatch");
-
     return true;
 }
 
 // Section macros.
 
 static bool
 DecodePreamble(Decoder& d)
 {
--- a/js/src/wasm/WasmValidate.h
+++ b/js/src/wasm/WasmValidate.h
@@ -437,16 +437,19 @@ class Decoder
         return cur_;
     }
     size_t currentOffset() const {
         return offsetInModule_ + (cur_ - beg_);
     }
     const uint8_t* begin() const {
         return beg_;
     }
+    const uint8_t* end() const {
+        return end_;
+    }
 
     // Fixed-size encoding operations simply copy the literal bytes (without
     // attempting to align).
 
     MOZ_MUST_USE bool readFixedU8(uint8_t* i) {
         return read<uint8_t>(i);
     }
     MOZ_MUST_USE bool readFixedU32(uint32_t* u) {
--- a/layout/xul/nsMenuPopupFrame.cpp
+++ b/layout/xul/nsMenuPopupFrame.cpp
@@ -1111,17 +1111,23 @@ nsMenuPopupFrame::AdjustPositionForAncho
   if (mPosition == POPUPPOSITION_SELECTION) {
     MOZ_ASSERT(popupAnchor == POPUPALIGNMENT_BOTTOMLEFT ||
               popupAnchor == POPUPALIGNMENT_BOTTOMRIGHT);
     MOZ_ASSERT(popupAlign == POPUPALIGNMENT_TOPLEFT ||
                popupAlign == POPUPALIGNMENT_TOPRIGHT);
 
     nsIFrame* selectedItemFrame = GetSelectedItemForAlignment();
     if (selectedItemFrame) {
-      pnt.y -= originalAnchorRect.height + selectedItemFrame->GetRect().y;
+      int32_t scrolly = 0;
+      nsIScrollableFrame *scrollframe = do_QueryFrame(nsBox::GetChildXULBox(this));
+      if (scrollframe) {
+        scrolly = scrollframe->GetScrollPosition().y;
+      }
+
+      pnt.y -= originalAnchorRect.height + selectedItemFrame->GetRect().y - scrolly;
     }
   }
 
   // Flipping horizontally is allowed as long as the popup is above or below
   // the anchor. This will happen if both the anchor and alignment are top or
   // both are bottom, but different values. Similarly, flipping vertically is
   // allowed if the popup is to the left or right of the anchor. In this case,
   // the values of the constants are such that both must be positive or both
--- a/layout/xul/nsXULPopupManager.cpp
+++ b/layout/xul/nsXULPopupManager.cpp
@@ -135,30 +135,29 @@ NS_IMPL_ISUPPORTS(nsXULPopupManager,
                   nsIObserver)
 
 nsXULPopupManager::nsXULPopupManager() :
   mRangeOffset(0),
   mCachedMousePoint(0, 0),
   mCachedModifiers(0),
   mActiveMenuBar(nullptr),
   mPopups(nullptr),
-  mNoHidePanels(nullptr),
   mTimerMenu(nullptr)
 {
   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
   if (obs) {
     obs->AddObserver(this, "xpcom-shutdown", false);
   }
   Preferences::AddBoolVarCache(&sDevtoolsDisableAutoHide,
                                kPrefDevtoolsDisableAutoHide, false);
 }
 
 nsXULPopupManager::~nsXULPopupManager() 
 {
-  NS_ASSERTION(!mPopups && !mNoHidePanels, "XUL popups still open");
+  NS_ASSERTION(!mPopups, "XUL popups still open");
 }
 
 nsresult
 nsXULPopupManager::Init()
 {
   sInstance = new nsXULPopupManager();
   NS_ENSURE_TRUE(sInstance, NS_ERROR_OUT_OF_MEMORY);
   NS_ADDREF(sInstance);
@@ -429,22 +428,22 @@ nsXULPopupManager::AdjustPopupsOnWindowC
 {
   // When the parent window is moved, adjust any child popups. Dismissable
   // menus and panels are expected to roll up when a window is moved, so there
   // is no need to check these popups, only the noautohide popups.
 
   // The items are added to a list so that they can be adjusted bottom to top.
   nsTArray<nsMenuPopupFrame *> list;
 
-  nsMenuChainItem* item = mNoHidePanels;
+  nsMenuChainItem* item = mPopups;
   while (item) {
     // only move popups that are within the same window and where auto
     // positioning has not been disabled
     nsMenuPopupFrame* frame = item->Frame();
-    if (frame->GetAutoPosition()) {
+    if (item->IsNoAutoHide() && frame->GetAutoPosition()) {
       nsIContent* popup = frame->GetContent();
       if (popup) {
         nsIDocument* document = popup->GetUncomposedDoc();
         if (document) {
           if (nsPIDOMWindowOuter* window = document->GetWindow()) {
             window = window->GetPrivateRoot();
             if (window == aWindow) {
               list.AppendElement(frame);
@@ -575,19 +574,24 @@ nsXULPopupManager::GetPopupFrameForConte
 
   return do_QueryFrame(aContent->GetPrimaryFrame());
 }
 
 nsMenuChainItem*
 nsXULPopupManager::GetTopVisibleMenu()
 {
   nsMenuChainItem* item = mPopups;
-  while (item && item->Frame()->PopupState() == ePopupInvisible)
+  while (item) {
+    if (!item->IsNoAutoHide() && item->Frame()->PopupState() != ePopupInvisible) {
+      return item;
+    }
     item = item->GetParent();
-  return item;
+  }
+
+  return nullptr;
 }
 
 void
 nsXULPopupManager::GetMouseLocation(nsIDOMNode** aNode, int32_t* aOffset)
 {
   *aNode = mRangeParent;
   NS_IF_ADDREF(*aNode);
   *aOffset = mRangeOffset;
@@ -905,18 +909,24 @@ void
 nsXULPopupManager::ShowPopupCallback(nsIContent* aPopup,
                                      nsMenuPopupFrame* aPopupFrame,
                                      bool aIsContextMenu,
                                      bool aSelectFirstItem)
 {
   nsPopupType popupType = aPopupFrame->PopupType();
   bool ismenu = (popupType == ePopupTypeMenu);
 
+  // Popups normally hide when an outside click occurs. Panels may use
+  // the noautohide attribute to disable this behaviour. It is expected
+  // that the application will hide these popups manually. The tooltip
+  // listener will handle closing the tooltip also.
+  bool isNoAutoHide = aPopupFrame->IsNoAutoHide() || popupType == ePopupTypeTooltip;
+
   nsMenuChainItem* item =
-    new nsMenuChainItem(aPopupFrame, aIsContextMenu, popupType);
+    new nsMenuChainItem(aPopupFrame, isNoAutoHide, aIsContextMenu, popupType);
   if (!item)
     return;
 
   // install keyboard event listeners for navigating menus. For panels, the
   // escape key may be used to close the panel. However, the ignorekeys
   // attribute may be used to disable adding these event listeners for popups
   // that want to handle their own keyboard events.
   nsAutoString ignorekeys;
@@ -939,28 +949,23 @@ nsXULPopupManager::ShowPopupCallback(nsI
   AutoWeakFrame weakFrame(aPopupFrame);
   aPopupFrame->ShowPopup(aIsContextMenu);
   ENSURE_TRUE(weakFrame.IsAlive());
 
   // popups normally hide when an outside click occurs. Panels may use
   // the noautohide attribute to disable this behaviour. It is expected
   // that the application will hide these popups manually. The tooltip
   // listener will handle closing the tooltip also.
-  if (aPopupFrame->IsNoAutoHide() || popupType == ePopupTypeTooltip) {
-    item->SetParent(mNoHidePanels);
-    mNoHidePanels = item;
+  nsIContent* oldmenu = nullptr;
+  if (mPopups) {
+    oldmenu = mPopups->Content();
   }
-  else {
-    nsIContent* oldmenu = nullptr;
-    if (mPopups)
-      oldmenu = mPopups->Content();
-    item->SetParent(mPopups);
-    mPopups = item;
-    SetCaptureState(oldmenu);
-  }
+  item->SetParent(mPopups);
+  mPopups = item;
+  SetCaptureState(oldmenu);
 
   item->UpdateFollowAnchor();
 
   if (aSelectFirstItem) {
     nsMenuFrame* next = GetNextMenuItem(aPopupFrame, nullptr, true, false);
     aPopupFrame->SetCurrentMenuItem(next);
   }
 
@@ -975,130 +980,111 @@ nsXULPopupManager::ShowPopupCallback(nsI
 void
 nsXULPopupManager::HidePopup(nsIContent* aPopup,
                              bool aHideChain,
                              bool aDeselectMenu,
                              bool aAsynchronous,
                              bool aIsCancel,
                              nsIContent* aLastPopup)
 {
-  // if the popup is on the nohide panels list, remove it but don't close any
-  // other panels
-  nsMenuPopupFrame* popupFrame = nullptr;
-  bool foundPanel = false;
-  nsMenuChainItem* item = mNoHidePanels;
-  while (item) {
-    if (item->Content() == aPopup) {
-      foundPanel = true;
-      popupFrame = item->Frame();
+  nsMenuPopupFrame* popupFrame = do_QueryFrame(aPopup->GetPrimaryFrame());
+  if (!popupFrame) {
+    return;
+  }
+
+  nsMenuChainItem* foundPopup = mPopups;
+  while (foundPopup) {
+    if (foundPopup->Content() == aPopup) {
       break;
     }
-    item = item->GetParent();
+    foundPopup = foundPopup->GetParent();
   }
 
-  // when removing a menu, all of the child popups must be closed
-  nsMenuChainItem* foundMenu = nullptr;
-  item = mPopups;
-  while (item) {
-    if (item->Content() == aPopup) {
-      foundMenu = item;
-      break;
-    }
-    item = item->GetParent();
-  }
-
-  nsPopupType type = ePopupTypePanel;
   bool deselectMenu = false;
   nsCOMPtr<nsIContent> popupToHide, nextPopup, lastPopup;
-  if (foundMenu) {
-    // at this point, foundMenu will be set to the found item in the list. If
-    // foundMenu is the topmost menu, the one to remove, then there are no other
-    // popups to hide. If foundMenu is not the topmost menu, then there may be
-    // open submenus below it. In this case, we need to make sure that those
-    // submenus are closed up first. To do this, we scan up the menu list to
-    // find the topmost popup with only menus between it and foundMenu and
-    // close that menu first. In synchronous mode, the FirePopupHidingEvent
-    // method will be called which in turn calls HidePopupCallback to close up
-    // the next popup in the chain. These two methods will be called in
-    // sequence recursively to close up all the necessary popups. In
-    // asynchronous mode, a similar process occurs except that the
-    // FirePopupHidingEvent method is called asynchronously. In either case,
-    // nextPopup is set to the content node of the next popup to close, and
-    // lastPopup is set to the last popup in the chain to close, which will be
-    // aPopup, or null to close up all menus.
-
-    nsMenuChainItem* topMenu = foundMenu;
-    // Use IsMenu to ensure that foundMenu is a menu and scan down the child
-    // list until a non-menu is found. If foundMenu isn't a menu at all, don't
-    // scan and just close up this menu.
-    if (foundMenu->IsMenu()) {
-      item = topMenu->GetChild();
-      while (item && item->IsMenu()) {
-        topMenu = item;
-        item = item->GetChild();
+
+  if (foundPopup) {
+    if (foundPopup->IsNoAutoHide()) {
+      // If this is a noautohide panel, remove it but don't close any other panels.
+      popupToHide = aPopup;
+    } else {
+      // At this point, foundPopup will be set to the found item in the list. If
+      // foundPopup is the topmost menu, the one to remove, then there are no other
+      // popups to hide. If foundPopup is not the topmost menu, then there may be
+      // open submenus below it. In this case, we need to make sure that those
+      // submenus are closed up first. To do this, we scan up the menu list to
+      // find the topmost popup with only menus between it and foundPopup and
+      // close that menu first. In synchronous mode, the FirePopupHidingEvent
+      // method will be called which in turn calls HidePopupCallback to close up
+      // the next popup in the chain. These two methods will be called in
+      // sequence recursively to close up all the necessary popups. In
+      // asynchronous mode, a similar process occurs except that the
+      // FirePopupHidingEvent method is called asynchronously. In either case,
+      // nextPopup is set to the content node of the next popup to close, and
+      // lastPopup is set to the last popup in the chain to close, which will be
+      // aPopup, or null to close up all menus.
+
+      nsMenuChainItem* topMenu = foundPopup;
+      // Use IsMenu to ensure that foundPopup is a menu and scan down the child
+      // list until a non-menu is found. If foundPopup isn't a menu at all, don't
+      // scan and just close up this menu.
+      if (foundPopup->IsMenu()) {
+        nsMenuChainItem* child = foundPopup->GetChild();
+        while (child && child->IsMenu()) {
+          topMenu = child;
+          child = child->GetChild();
+        }
       }
+      
+      deselectMenu = aDeselectMenu;
+      popupToHide = topMenu->Content();
+      popupFrame = topMenu->Frame();
+
+      // Close up another popup if there is one, and we are either hiding the
+      // entire chain or the item to hide isn't the topmost popup.
+      nsMenuChainItem* parent = topMenu->GetParent();
+      if (parent && (aHideChain || topMenu != foundPopup)) {
+        nextPopup = parent->Content();
+      }
+
+      lastPopup = aLastPopup ? aLastPopup : (aHideChain ? nullptr : aPopup);
     }
-    
-    deselectMenu = aDeselectMenu;
-    popupToHide = topMenu->Content();
-    popupFrame = topMenu->Frame();
-    type = popupFrame->PopupType();
-
-    nsMenuChainItem* parent = topMenu->GetParent();
-
-    // close up another popup if there is one, and we are either hiding the
-    // entire chain or the item to hide isn't the topmost popup.
-    if (parent && (aHideChain || topMenu != foundMenu))
-      nextPopup = parent->Content();
-
-    lastPopup = aLastPopup ? aLastPopup : (aHideChain ? nullptr : aPopup);
-  }
-  else if (foundPanel) {
-    popupToHide = aPopup;
-  } else {
+  } else if (popupFrame->PopupState() == ePopupPositioning) {
     // When the popup is in the popuppositioning state, it will not be in the
     // mPopups list. We need another way to find it and make sure it does not
     // continue the popup showing process.
-    popupFrame = do_QueryFrame(aPopup->GetPrimaryFrame());
-    if (popupFrame) {
-      if (popupFrame->PopupState() == ePopupPositioning) {
-        // Do basically the same thing we would have done if we had found the
-        // popup in the mPopups list.
-        deselectMenu = aDeselectMenu;
-        popupToHide = aPopup;
-        type = popupFrame->PopupType();
-      } else {
-        // The popup is not positioning. If we were supposed to have handled
-        // closing it, it should have been in mPopups or mNoHidePanels
-        popupFrame = nullptr;
-      }
+    deselectMenu = aDeselectMenu;
+    popupToHide = aPopup;
+  }
+
+  if (popupToHide) {
+    nsPopupState state = popupFrame->PopupState();
+    // If the popup is already being hidden, don't attempt to hide it again
+    if (state == ePopupHiding) {
+      return;
     }
-  }
-
-  if (popupFrame) {
-    nsPopupState state = popupFrame->PopupState();
-    // if the popup is already being hidden, don't attempt to hide it again
-    if (state == ePopupHiding)
-      return;
-    // change the popup state to hiding. Don't set the hiding state if the
+
+    // Change the popup state to hiding. Don't set the hiding state if the
     // popup is invisible, otherwise nsMenuPopupFrame::HidePopup will
     // run again. In the invisible state, we just want the events to fire.
-    if (state != ePopupInvisible)
+    if (state != ePopupInvisible) {
       popupFrame->SetPopupState(ePopupHiding);
-
-    // for menus, popupToHide is always the frontmost item in the list to hide.
+    }
+
+    // For menus, popupToHide is always the frontmost item in the list to hide.
     if (aAsynchronous) {
       nsCOMPtr<nsIRunnable> event =
         new nsXULPopupHidingEvent(popupToHide, nextPopup, lastPopup,
-                                  type, deselectMenu, aIsCancel);
+                                  popupFrame->PopupType(), deselectMenu, aIsCancel);
         NS_DispatchToCurrentThread(event);
     }
     else {
       FirePopupHidingEvent(popupToHide, nextPopup, lastPopup,
-                           popupFrame->PresContext(), type, deselectMenu, aIsCancel);
+                           popupFrame->PresContext(), popupFrame->PopupType(),
+                           deselectMenu, aIsCancel);
     }
   }
 }
 
 // This is used to hide the popup after a transition finishes.
 class TransitionEnder : public nsIDOMEventListener
 {
 protected:
@@ -1159,37 +1145,26 @@ nsXULPopupManager::HidePopupCallback(nsI
     mTimerMenu = nullptr;
   }
 
   // The popup to hide is aPopup. Search the list again to find the item that
   // corresponds to the popup to hide aPopup. This is done because it's
   // possible someone added another item (attempted to open another popup)
   // or removed a popup frame during the event processing so the item isn't at
   // the front anymore.
-  nsMenuChainItem* item = mNoHidePanels;
+  nsMenuChainItem* item = mPopups;
   while (item) {
     if (item->Content() == aPopup) {
-      item->Detach(&mNoHidePanels);
+      item->Detach(&mPopups);
+      SetCaptureState(aPopup);
       break;
     }
     item = item->GetParent();
   }
 
-  if (!item) {
-    item = mPopups;
-    while (item) {
-      if (item->Content() == aPopup) {
-        item->Detach(&mPopups);
-        SetCaptureState(aPopup);
-        break;
-      }
-      item = item->GetParent();
-    }
-  }
-
   delete item;
 
   AutoWeakFrame weakFrame(aPopupFrame);
   aPopupFrame->HidePopup(aDeselectMenu, ePopupClosed);
   ENSURE_TRUE(weakFrame.IsAlive());
 
   // send the popuphidden event synchronously. This event has no default
   // behaviour.
@@ -1283,43 +1258,29 @@ nsXULPopupManager::HidePopupsInList(cons
 
   SetCaptureState(nullptr);
 }
 
 void
 nsXULPopupManager::EnableRollup(nsIContent* aPopup, bool aShouldRollup)
 {
 #ifndef MOZ_GTK
-  if (aShouldRollup) {
-    nsMenuChainItem* item = mNoHidePanels;
-    while (item) {
-      if (item->Content() == aPopup) {
-        item->Detach(&mNoHidePanels);
-        nsIContent* oldmenu = nullptr;
-        if (mPopups)
-          oldmenu = mPopups->Content();
-        item->SetParent(mPopups);
-        mPopups = item;
-        SetCaptureState(oldmenu);
-        return;
+  nsMenuChainItem* item = mPopups;
+  while (item) {
+    if (item->Content() == aPopup) {
+      nsIContent* oldmenu = nullptr;
+      if (mPopups) {
+        oldmenu = mPopups->Content();
       }
-      item = item->GetParent();
+
+      item->SetNoAutoHide(!aShouldRollup);
+      SetCaptureState(oldmenu);
+      return;
     }
-  } else {
-    nsMenuChainItem* item = mPopups;
-    while (item) {
-      if (item->Content() == aPopup) {
-        item->Detach(&mPopups);
-        item->SetParent(mNoHidePanels);
-        mNoHidePanels = item;
-        SetCaptureState(nullptr);
-        return;
-      }
-      item = item->GetParent();
-    }
+    item = item->GetParent();
   }
 #endif
 }
 
 bool
 nsXULPopupManager::IsChildOfDocShell(nsIDocument* aDoc, nsIDocShellTreeItem* aExpected)
 {
   nsCOMPtr<nsIDocShellTreeItem> docShellItem(aDoc->GetDocShell());
@@ -1349,65 +1310,43 @@ nsXULPopupManager::HidePopupsInDocShell(
       nsMenuPopupFrame* frame = item->Frame();
       item->Detach(&mPopups);
       delete item;
       popupsToHide.AppendElement(frame);
     }
     item = parent;
   }
 
-  // now look for panels to hide
-  item = mNoHidePanels;
-  while (item) {
-    nsMenuChainItem* parent = item->GetParent();
-    if (item->Frame()->PopupState() != ePopupInvisible &&
-        IsChildOfDocShell(item->Content()->OwnerDoc(), aDocShellToHide)) {
-      nsMenuPopupFrame* frame = item->Frame();
-      item->Detach(&mNoHidePanels);
-      delete item;
-      popupsToHide.AppendElement(frame);
-    }
-    item = parent;
-  }
-
   HidePopupsInList(popupsToHide);
 }
 
 void
 nsXULPopupManager::UpdatePopupPositions(nsRefreshDriver* aRefreshDriver)
 {
-  if (!mPopups && !mNoHidePanels) {
-    return;
-  }
-
-  for (int32_t i = 0; i < 2; i++) {
-    nsMenuChainItem* item = i == 0 ? mPopups : mNoHidePanels;
-    while (item) {
-      if (item->Frame()->PresContext()->RefreshDriver() == aRefreshDriver) {
-        item->CheckForAnchorChange();
-      }
-
-      item = item->GetParent();
+  nsMenuChainItem* item = mPopups;
+  while (item) {
+    if (item->Frame()->PresContext()->RefreshDriver() == aRefreshDriver) {
+      item->CheckForAnchorChange();
     }
+
+    item = item->GetParent();
   }
 }
 
 void
 nsXULPopupManager::UpdateFollowAnchor(nsMenuPopupFrame* aPopup)
 {
-  for (int32_t i = 0; i < 2; i++) {
-    nsMenuChainItem* item = i == 0 ? mPopups : mNoHidePanels;
-    while (item) {
-      if (item->Frame() == aPopup) {
-        item->UpdateFollowAnchor();
-        break;
-      }
-
-      item = item->GetParent();
+  nsMenuChainItem* item = mPopups;
+  while (item) {
+    if (item->Frame() == aPopup) {
+      item->UpdateFollowAnchor();
+      break;
     }
+
+    item = item->GetParent();
   }
 }
 
 void
 nsXULPopupManager::ExecuteMenu(nsIContent* aMenu, nsXULMenuCommandEvent* aEvent)
 {
   CloseMenuMode cmm = CloseMenuMode_Auto;
 
@@ -1674,28 +1613,16 @@ nsXULPopupManager::IsPopupOpen(nsIConten
                    item->Frame()->PopupState() == ePopupHiding ||
                    item->Frame()->PopupState() == ePopupInvisible,
                    "popup in open list not actually open");
       return true;
     }
     item = item->GetParent();
   }
 
-  item = mNoHidePanels;
-  while (item) {
-    if (item->Content() == aPopup) {
-      NS_ASSERTION(item->Frame()->IsOpen() ||
-                   item->Frame()->PopupState() == ePopupHiding ||
-                   item->Frame()->PopupState() == ePopupInvisible,
-                   "popup in open list not actually open");
-      return true;
-    }
-    item = item->GetParent();
-  }
-
   return false;
 }
 
 bool
 nsXULPopupManager::IsPopupOpenForMenuParent(nsMenuParent* aMenuParent)
 {
   nsMenuChainItem* item = GetTopVisibleMenu();
   while (item) {
@@ -1710,48 +1637,43 @@ nsXULPopupManager::IsPopupOpenForMenuPar
   }
 
   return false;
 }
 
 nsIFrame*
 nsXULPopupManager::GetTopPopup(nsPopupType aType)
 {
-  if ((aType == ePopupTypePanel || aType == ePopupTypeTooltip) && mNoHidePanels)
-    return mNoHidePanels->Frame();
-
-  nsMenuChainItem* item = GetTopVisibleMenu();
+  nsMenuChainItem* item = mPopups;
   while (item) {
-    if (item->PopupType() == aType || aType == ePopupTypeAny)
+    if (item->Frame()->IsVisible() &&
+        (item->PopupType() == aType || aType == ePopupTypeAny)) {
       return item->Frame();
+    }
+
     item = item->GetParent();
   }
 
   return nullptr;
 }
 
 void
 nsXULPopupManager::GetVisiblePopups(nsTArray<nsIFrame *>& aPopups)
 {
   aPopups.Clear();
 
-  // Iterate over both lists of popups
   nsMenuChainItem* item = mPopups;
-  for (int32_t list = 0; list < 2; list++) {
-    while (item) {
-      // Skip panels which are not visible as well as popups that
-      // are transparent to mouse events.
-      if (item->Frame()->IsVisible() && !item->Frame()->IsMouseTransparent()) {
-        aPopups.AppendElement(item->Frame());
-      }
-
-      item = item->GetParent();
+  while (item) {
+    // Skip panels which are not visible as well as popups that
+    // are transparent to mouse events.
+    if (item->Frame()->IsVisible() && !item->Frame()->IsMouseTransparent()) {
+      aPopups.AppendElement(item->Frame());
     }
 
-    item = mNoHidePanels;
+    item = item->GetParent();
   }
 }
 
 already_AddRefed<nsIDOMNode>
 nsXULPopupManager::GetLastTriggerNode(nsIDocument* aDocument, bool aIsTooltip)
 {
   if (!aDocument)
     return nullptr;
@@ -1761,17 +1683,17 @@ nsXULPopupManager::GetLastTriggerNode(ns
   // if mOpeningPopup is set, it means that a popupshowing event is being
   // fired. In this case, just use the cached node, as the popup is not yet in
   // the list of open popups.
   if (mOpeningPopup && mOpeningPopup->GetUncomposedDoc() == aDocument &&
       aIsTooltip == mOpeningPopup->IsXULElement(nsGkAtoms::tooltip)) {
     node = do_QueryInterface(nsMenuPopupFrame::GetTriggerContent(GetPopupFrameForContent(mOpeningPopup, false)));
   }
   else {
-    nsMenuChainItem* item = aIsTooltip ? mNoHidePanels : mPopups;
+    nsMenuChainItem* item = mPopups;
     while (item) {
       // look for a popup of the same type and document.
       if ((item->PopupType() == ePopupTypeTooltip) == aIsTooltip &&
           item->Content()->GetUncomposedDoc() == aDocument) {
         node = do_QueryInterface(nsMenuPopupFrame::GetTriggerContent(item->Frame()));
         if (node)
           break;
       }
@@ -1882,33 +1804,24 @@ nsXULPopupManager::PopupDestroyed(nsMenu
   if (mTimerMenu == aPopup) {
     if (mCloseTimer) {
       mCloseTimer->Cancel();
       mCloseTimer = nullptr;
     }
     mTimerMenu = nullptr;
   }
 
-  nsMenuChainItem* item = mNoHidePanels;
-  while (item) {
-    if (item->Frame() == aPopup) {
-      item->Detach(&mNoHidePanels);
-      delete item;
-      break;
-    }
-    item = item->GetParent();
-  }
-
   nsTArray<nsMenuPopupFrame *> popupsToHide;
 
-  item = mPopups;
+  nsMenuChainItem* item = mPopups;
   while (item) {
     nsMenuPopupFrame* frame = item->Frame();
     if (frame == aPopup) {
-      if (frame->PopupState() != ePopupInvisible) {
+      // XXXndeakin shouldn't this only happen for menus?
+      if (!item->IsNoAutoHide() && frame->PopupState() != ePopupInvisible) {
         // Iterate through any child menus and hide them as well, since the
         // parent is going away. We won't remove them from the list yet, just
         // hide them, as they will be removed from the list when this function
         // gets called for that child frame.
         nsMenuChainItem* child = item->GetChild();
         while (child) {
           // if the popup is a child frame of the menu that was destroyed, add
           // it to the list of popups to hide. Don't bother with the events
@@ -2374,17 +2287,17 @@ nsXULPopupManager::HandleKeyboardEventWi
       HidePopup(aTopVisibleMenuItem->Content(), false, false, false, true);
       aKeyEvent->AsEvent()->StopPropagation();
       aKeyEvent->AsEvent()->StopCrossProcessForwarding();
       aKeyEvent->AsEvent()->PreventDefault();
     }
     return true;
   }
 
-  bool consume = (mPopups || mActiveMenuBar);
+  bool consume = (aTopVisibleMenuItem || mActiveMenuBar);
   switch (keyCode) {
     case nsIDOMKeyEvent::DOM_VK_UP:
     case nsIDOMKeyEvent::DOM_VK_DOWN:
 #ifndef XP_MACOSX
       // roll up the popup when alt+up/down are pressed within a menulist.
       bool alt;
       aKeyEvent->GetAltKey(&alt);
       if (alt && aTopVisibleMenuItem && aTopVisibleMenuItem->Frame()->IsMenuList()) {
@@ -2739,17 +2652,17 @@ nsXULPopupManager::KeyDown(nsIDOMKeyEven
         aKeyEvent->GetShiftKey(&shift);
       bool meta=false;
       if (menuAccessKey != nsIDOMKeyEvent::DOM_VK_META)
         aKeyEvent->GetMetaKey(&meta);
       if (!(ctrl || alt || shift || meta)) {
         // The access key just went down and no other
         // modifiers are already down.
         nsMenuChainItem* item = GetTopVisibleMenu();
-        if (mPopups && item && !item->Frame()->IsMenuList()) {
+        if (item && !item->Frame()->IsMenuList()) {
           Rollup(0, false, nullptr, nullptr);
         } else if (mActiveMenuBar) {
           mActiveMenuBar->MenuClosed();
         }
 
         // Clear the item to avoid bugs as it may have been deleted during rollup.
         item = nullptr; 
       }
@@ -2771,17 +2684,17 @@ nsXULPopupManager::KeyPress(nsIDOMKeyEve
   if (item &&
       (item->Frame()->IsMenuLocked() || item->PopupType() != ePopupTypeMenu)) {
     return NS_OK;
   }
 
   nsCOMPtr<nsIDOMKeyEvent> keyEvent = do_QueryInterface(aKeyEvent);
   NS_ENSURE_TRUE(keyEvent, NS_ERROR_UNEXPECTED);
   // if a menu is open or a menubar is active, it consumes the key event
-  bool consume = (mPopups || mActiveMenuBar);
+  bool consume = (item || mActiveMenuBar);
 
   WidgetInputEvent* evt = aKeyEvent->AsEvent()->WidgetEventPtr()->AsInputEvent();
   bool isAccel = evt && evt->IsAccel();
 
   // When ignorekeys="shortcuts" is used, we don't call preventDefault on the
   // key event when the accelerator key is pressed. This allows another
   // listener to handle keys. For instance, this allows global shortcuts to
   // still apply while a menu is open.
--- a/layout/xul/nsXULPopupManager.h
+++ b/layout/xul/nsXULPopupManager.h
@@ -35,18 +35,17 @@
  * There are two types that are used:
  *   - dismissable popups such as menus, which should close up when there is a
  *     click outside the popup. In this situation, the entire chain of menus
  *     above should also be closed.
  *   - panels, which stay open until a request is made to close them. This
  *     type is used by tooltips.
  *
  * When a new popup is opened, it is appended to the popup chain, stored in a
- * linked list in mPopups for dismissable menus and panels or mNoHidePanels
- * for tooltips and panels with noautohide="true".
+ * linked list in mPopups.
  * Popups are stored in this list linked from newest to oldest. When a click
  * occurs outside one of the open dismissable popups, the chain is closed by
  * calling Rollup.
  */
 
 class nsContainerFrame;
 class nsMenuFrame;
 class nsMenuPopupFrame;
@@ -130,34 +129,37 @@ extern const nsNavigationDirection Direc
 // nsMenuChainItem holds info about an open popup. Items are stored in a
 // doubly linked list. Note that the linked list is stored beginning from
 // the lowest child in a chain of menus, as this is the active submenu.
 class nsMenuChainItem
 {
 private:
   nsMenuPopupFrame* mFrame; // the popup frame
   nsPopupType mPopupType; // the popup type of the frame
+  bool mNoAutoHide; // true for noautohide panels
   bool mIsContext; // true for context menus
   bool mOnMenuBar; // true if the menu is on a menu bar
   nsIgnoreKeys mIgnoreKeys; // indicates how keyboard listeners should be used
 
   // True if the popup should maintain its position relative to the anchor when
   // the anchor moves.
   bool mFollowAnchor;
 
   // The last seen position of the anchor, relative to the screen.
   nsRect mCurrentRect;
 
   nsMenuChainItem* mParent;
   nsMenuChainItem* mChild;
 
 public:
-  nsMenuChainItem(nsMenuPopupFrame* aFrame, bool aIsContext, nsPopupType aPopupType)
+  nsMenuChainItem(nsMenuPopupFrame* aFrame, bool aNoAutoHide, bool aIsContext,
+                  nsPopupType aPopupType)
     : mFrame(aFrame),
       mPopupType(aPopupType),
+      mNoAutoHide(aNoAutoHide),
       mIsContext(aIsContext),
       mOnMenuBar(false),
       mIgnoreKeys(eIgnoreKeys_False),
       mFollowAnchor(false),
       mParent(nullptr),
       mChild(nullptr)
   {
     NS_ASSERTION(aFrame, "null frame passed to nsMenuChainItem constructor");
@@ -167,16 +169,18 @@ public:
   ~nsMenuChainItem()
   {
     MOZ_COUNT_DTOR(nsMenuChainItem);
   }
 
   nsIContent* Content();
   nsMenuPopupFrame* Frame() { return mFrame; }
   nsPopupType PopupType() { return mPopupType; }
+  bool IsNoAutoHide() { return mNoAutoHide; }
+  void SetNoAutoHide(bool aNoAutoHide) { mNoAutoHide = aNoAutoHide; }
   bool IsMenu() { return mPopupType == ePopupTypeMenu; }
   bool IsContextMenu() { return mIsContext; }
   nsIgnoreKeys IgnoreKeys() { return mIgnoreKeys; }
   void SetIgnoreKeys(nsIgnoreKeys aIgnoreKeys) { mIgnoreKeys = aIgnoreKeys; }
   bool IsOnMenuBar() { return mOnMenuBar; }
   void SetOnMenuBar(bool aOnMenuBar) { mOnMenuBar = aOnMenuBar; }
   nsMenuChainItem* GetParent() { return mParent; }
   nsMenuChainItem* GetChild() { return mChild; }
@@ -562,17 +566,17 @@ public:
   /**
    * Return true if the popup for the supplied menu parent is open.
    */
   bool IsPopupOpenForMenuParent(nsMenuParent* aMenuParent);
 
   /**
    * Return the frame for the topmost open popup of a given type, or null if
    * no popup of that type is open. If aType is ePopupTypeAny, a menu of any
-   * type is returned, except for popups in the mNoHidePanels list.
+   * type is returned.
    */
   nsIFrame* GetTopPopup(nsPopupType aType);
 
   /**
    * Return an array of all the open and visible popup frames for
    * menus, in order from top to bottom.
    */
   void GetVisiblePopups(nsTArray<nsIFrame *>& aPopups);
@@ -824,19 +828,16 @@ protected:
   mozilla::Modifiers mCachedModifiers;
 
   // set to the currently active menu bar, if any
   nsMenuBarFrame* mActiveMenuBar;
 
   // linked list of normal menus and panels.
   nsMenuChainItem* mPopups;
 
-  // linked list of noautohide panels and tooltips.
-  nsMenuChainItem* mNoHidePanels;
-
   // timer used for HidePopupAfterDelay
   nsCOMPtr<nsITimer> mCloseTimer;
 
   // a popup that is waiting on the timer
   nsMenuPopupFrame* mTimerMenu;
 
   // the popup that is currently being opened, stored only during the
   // popupshowing event
--- a/media/webrtc/signaling/src/mediapipeline/MediaPipeline.cpp
+++ b/media/webrtc/signaling/src/mediapipeline/MediaPipeline.cpp
@@ -45,16 +45,17 @@
 #include "mozilla/PeerIdentity.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/TaskQueue.h"
 #include "mozilla/gfx/Point.h"
 #include "mozilla/gfx/Types.h"
 #include "mozilla/UniquePtr.h"
 #include "mozilla/UniquePtrExtensions.h"
 #include "mozilla/Sprintf.h"
+#include "mozilla/SizePrintfMacros.h"
 
 #include "webrtc/common_types.h"
 #include "webrtc/common_video/libyuv/include/webrtc_libyuv.h"
 
 #include "logging.h"
 
 // Max size given stereo is 480*2*2 = 1920 (10ms of 16-bits stereo audio at
 // 48KHz)
@@ -67,16 +68,17 @@ using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::gfx;
 using namespace mozilla::layers;
 
 // Logging context
 MOZ_MTLOG_MODULE("mediapipeline")
 
 namespace mozilla {
+extern mozilla::LogModule* AudioLogModule();
 
 class VideoConverterListener
 {
 public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(VideoConverterListener)
 
   virtual void OnVideoFrameConverted(unsigned char* aVideoFrame,
                                      unsigned int aVideoFrameLength,
@@ -1834,16 +1836,17 @@ static void AddListener(MediaStream* sou
 
 class GenericReceiveListener : public MediaStreamListener
 {
  public:
   GenericReceiveListener(SourceMediaStream *source, TrackID track_id)
     : source_(source),
       track_id_(track_id),
       played_ticks_(0),
+      last_log_(0),
       principal_handle_(PRINCIPAL_HANDLE_NONE) {}
 
   virtual ~GenericReceiveListener() {}
 
   void AddSelf()
   {
     AddListener(source_, this);
   }
@@ -1883,16 +1886,17 @@ class GenericReceiveListener : public Me
   {
     principal_handle_ = principal_handle;
   }
 
  protected:
   SourceMediaStream *source_;
   const TrackID track_id_;
   TrackTicks played_ticks_;
+  TrackTicks last_log_; // played_ticks_ when we last logged
   PrincipalHandle principal_handle_;
 };
 
 MediaPipelineReceive::MediaPipelineReceive(
     const std::string& pc,
     nsCOMPtr<nsIEventTarget> main_thread,
     nsCOMPtr<nsIEventTarget> sts_thread,
     SourceMediaStream *stream,
@@ -2009,16 +2013,24 @@ public:
       outputChannels.AppendElements(channels);
 
       segment.AppendFrames(samples.forget(), outputChannels, frames,
                            principal_handle_);
 
       // Handle track not actually added yet or removed/finished
       if (source_->AppendToTrack(track_id_, &segment)) {
         played_ticks_ += frames;
+        if (MOZ_LOG_TEST(AudioLogModule(), LogLevel::Debug)) {
+          if (played_ticks_ > last_log_ + WEBRTC_DEFAULT_SAMPLE_RATE) { // ~ 1 second
+            MOZ_LOG(AudioLogModule(), LogLevel::Debug,
+                    ("%p: Inserting %" PRIuSIZE " samples into track %d, total = %" PRIu64,
+                     (void*) this, frames, track_id_, played_ticks_));
+            last_log_ = played_ticks_;
+          }
+        }
       } else {
         MOZ_MTLOG(ML_ERROR, "AppendToTrack failed");
         // we can't un-read the data, but that's ok since we don't want to
         // buffer - but don't i-loop!
         return;
       }
     }
   }
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoView.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoView.java
@@ -15,16 +15,17 @@ import org.mozilla.gecko.mozglue.JNIObje
 import org.mozilla.gecko.NativeQueue.StateHolder;
 import org.mozilla.gecko.util.BundleEventListener;
 import org.mozilla.gecko.util.EventCallback;
 import org.mozilla.gecko.util.GeckoBundle;
 
 import android.app.Activity;
 import android.content.Context;
 import android.content.SharedPreferences;
+import android.net.Uri;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.AttributeSet;
 import android.util.DisplayMetrics;
@@ -328,32 +329,51 @@ public class GeckoView extends LayerView
     }
 
     @WrapForJNI public static final int LOAD_DEFAULT = 0;
     @WrapForJNI public static final int LOAD_NEW_TAB = 1;
     @WrapForJNI public static final int LOAD_SWITCH_TAB = 2;
 
     /**
     * Load the given URI.
+    * Note: Only for Fennec support.
     * @param uri The URI of the resource to load.
     * @param flags The load flags (TODO).
     */
     public void loadUri(String uri, int flags) {
         if (window == null) {
             throw new IllegalStateException("Not attached to window");
         }
 
         if (GeckoThread.isRunning()) {
             window.loadUri(uri, flags);
         }  else {
             GeckoThread.queueNativeCall(window, "loadUri", String.class, uri, flags);
         }
     }
 
     /**
+    * Load the given URI.
+    * @param uri The URI of the resource to load.
+    */
+    public void loadUri(String uri) {
+        final GeckoBundle msg = new GeckoBundle();
+        msg.putString("uri", uri);
+        mEventDispatcher.dispatch("GeckoView:LoadUri", msg);
+    }
+
+    /**
+    * Load the given URI.
+    * @param uri The URI of the resource to load.
+    */
+    public void loadUri(Uri uri) {
+        loadUri(uri.toString());
+    }
+
+    /**
     * Reload the current URI.
     */
     public void reload() {
         mEventDispatcher.dispatch("GeckoView:Reload", null);
     }
 
     /* package */ void setInputConnectionListener(final InputConnectionListener icl) {
         mInputConnectionListener = icl;
--- a/mobile/android/modules/GeckoViewContent.jsm
+++ b/mobile/android/modules/GeckoViewContent.jsm
@@ -14,18 +14,16 @@ var dump = Cu.import("resource://gre/mod
            .AndroidLog.d.bind(null, "ViewContent");
 
 function debug(aMsg) {
   // dump(aMsg);
 }
 
 class GeckoViewContent extends GeckoViewModule {
   init() {
-    this.window.QueryInterface(Ci.nsIDOMChromeWindow).browserDOMWindow = this;
-
     this.messageManager.loadFrameScript("chrome://browser/content/GeckoViewContent.js", true);
     this.messageManager.addMessageListener("GeckoView:DOMTitleChanged", this);
   }
 
   handleEvent(aEvent) {
     debug("handleEvent: aEvent.type=" + aEvent.type);
   }
 
--- a/mobile/android/modules/GeckoViewNavigation.jsm
+++ b/mobile/android/modules/GeckoViewNavigation.jsm
@@ -37,16 +37,17 @@ class GeckoViewNavigation extends GeckoV
     // event may be dispatched before this object is constructed.
     this.registerProgressListener();
 
     this.eventDispatcher.registerListener(this, [
       "GeckoViewNavigation:Active",
       "GeckoViewNavigation:Inactive",
       "GeckoView:GoBack",
       "GeckoView:GoForward",
+      "GeckoView:LoadUri",
       "GeckoView:Reload",
     ]);
   }
 
   // Bundle event handler.
   onEvent(aEvent, aData, aCallback) {
     debug("onEvent: aEvent=" + aEvent + ", aData=" + JSON.stringify(aData));
 
@@ -58,16 +59,19 @@ class GeckoViewNavigation extends GeckoV
         this.unregisterProgressListener();
         break;
       case "GeckoView:GoBack":
         this.browser.goBack();
         break;
       case "GeckoView:GoForward":
         this.browser.goForward();
         break;
+      case "GeckoView:LoadUri":
+        this.browser.loadURI(aData.uri, null, null, null);
+        break;
       case "GeckoView:Reload":
         this.browser.reload();
         break;
     }
   }
 
   // Message manager event handler.
   receiveMessage(aMsg) {
--- a/mobile/android/modules/GeckoViewProgress.jsm
+++ b/mobile/android/modules/GeckoViewProgress.jsm
@@ -18,18 +18,16 @@ var dump = Cu.import("resource://gre/mod
            .AndroidLog.d.bind(null, "ViewNavigation");
 
 function debug(aMsg) {
   // dump(aMsg);
 }
 
 class GeckoViewProgress extends GeckoViewModule {
   init() {
-    this.window.QueryInterface(Ci.nsIDOMChromeWindow).browserDOMWindow = this;
-
     this.registerProgressListener();
   }
 
   registerProgressListener() {
     debug("registerProgressListeners()");
 
     let flags = Ci.nsIWebProgress.NOTIFY_STATE_NETWORK | Ci.nsIWebProgress.NOTIFY_SECURITY;
     this.progressFilter =
--- a/toolkit/crashreporter/nsExceptionHandler.cpp
+++ b/toolkit/crashreporter/nsExceptionHandler.cpp
@@ -15,16 +15,20 @@
 #include "nsIObserverService.h"
 #include "mozilla/Unused.h"
 #include "mozilla/Printf.h"
 #include "mozilla/Sprintf.h"
 #include "mozilla/SyncRunnable.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/ipc/CrashReporterClient.h"
 
+#ifdef XP_WIN
+#include "mozilla/TlsAllocationTracker.h"
+#endif
+
 #include "nsThreadUtils.h"
 #include "nsXULAppAPI.h"
 #include "jsfriendapi.h"
 
 #if defined(XP_WIN32)
 #ifdef WIN32_LEAN_AND_MEAN
 #undef WIN32_LEAN_AND_MEAN
 #endif
@@ -39,17 +43,16 @@
 #include <string.h>
 #include "nsDirectoryServiceUtils.h"
 
 #include "nsWindowsDllInterceptor.h"
 #elif defined(XP_MACOSX)
 #include "breakpad-client/mac/crash_generation/client_info.h"
 #include "breakpad-client/mac/crash_generation/crash_generation_server.h"
 #include "breakpad-client/mac/handler/exception_handler.h"
-#include <string>
 #include <Carbon/Carbon.h>
 #include <CoreFoundation/CoreFoundation.h>
 #include <crt_externs.h>
 #include <fcntl.h>
 #include <mach/mach.h>
 #include <sys/types.h>
 #include <spawn.h>
 #include <unistd.h>
@@ -1409,16 +1412,23 @@ PrepareChildExceptionTimeAnnotations()
     }
     if (gTopPendingIPCName) {
       WriteAnnotation(apiData, "TopPendingIPCName", gTopPendingIPCName);
     }
     if (topPendingIPCTypeBuffer[2]) {
       WriteAnnotation(apiData, "TopPendingIPCType", topPendingIPCTypeBuffer);
     }
   }
+
+#ifdef XP_WIN
+  const char* tlsAllocations = mozilla::GetTlsAllocationStacks();
+  if (tlsAllocations) {
+    WriteAnnotation(apiData, "TlsAllocations", tlsAllocations);
+  }
+#endif
 }
 
 #ifdef XP_WIN
 static void
 ReserveBreakpadVM()
 {
   if (!gBreakpadReservedVM) {
     gBreakpadReservedVM = VirtualAlloc(nullptr, kReserveSize, MEM_RESERVE,
--- a/toolkit/modules/sessionstore/SessionHistory.jsm
+++ b/toolkit/modules/sessionstore/SessionHistory.jsm
@@ -197,28 +197,20 @@ var SessionHistoryInternal = {
     } else {
       let x = {}, y = {};
       shEntry.getScrollPosition(x, y);
       if (x.value != 0 || y.value != 0)
         entry.scroll = x.value + "," + y.value;
     }
 
     // Collect triggeringPrincipal data for the current history entry.
-    // Please note that before Bug 1297338 there was no concept of a
-    // principalToInherit. To remain backward/forward compatible we
-    // serialize the principalToInherit as triggeringPrincipal_b64.
-    // Once principalToInherit is well established (within FF55)
-    // we can update this code, remove triggeringPrincipal_b64 and
-    // just keep triggeringPrincipal_base64 as well as
-    // principalToInherit_base64;  see Bug 1301666.
     if (shEntry.principalToInherit) {
       try {
         let principalToInherit = Utils.serializePrincipal(shEntry.principalToInherit);
         if (principalToInherit) {
-          entry.triggeringPrincipal_b64 = principalToInherit;
           entry.principalToInherit_base64 = principalToInherit;
         }
       } catch (e) {
         debug(e);
       }
     }
 
     if (shEntry.triggeringPrincipal) {
@@ -406,37 +398,21 @@ var SessionHistoryInternal = {
         matchingEntry = {shEntry, childDocIdents};
         docIdentMap[entry.docIdentifier] = matchingEntry;
       } else {
         shEntry.adoptBFCacheEntry(matchingEntry.shEntry);
         childDocIdents = matchingEntry.childDocIdents;
       }
     }
 
-    // Before introducing the concept of principalToInherit we only had
-    // a triggeringPrincipal within every entry which basically is the
-    // equivalent of the new principalToInherit. To avoid compatibility
-    // issues, we first check if the entry has entries for
-    // triggeringPrincipal_base64 and principalToInherit_base64. If not
-    // we fall back to using the principalToInherit (which is stored
-    // as triggeringPrincipal_b64) as the triggeringPrincipal and
-    // the principalToInherit.
-    // FF55 will remove the triggeringPrincipal_b64, see Bug 1301666.
-    if (entry.triggeringPrincipal_base64 || entry.principalToInherit_base64) {
-      if (entry.triggeringPrincipal_base64) {
-        shEntry.triggeringPrincipal =
-          Utils.deserializePrincipal(entry.triggeringPrincipal_base64);
-      }
-      if (entry.principalToInherit_base64) {
-        shEntry.principalToInherit =
-          Utils.deserializePrincipal(entry.principalToInherit_base64);
-      }
-    } else if (entry.triggeringPrincipal_b64) {
-      shEntry.triggeringPrincipal = Utils.deserializePrincipal(entry.triggeringPrincipal_b64);
-      shEntry.principalToInherit = shEntry.triggeringPrincipal;
+    if (entry.triggeringPrincipal_base64) {
+      shEntry.triggeringPrincipal = Utils.deserializePrincipal(entry.triggeringPrincipal_base64);
+    }
+    if (entry.principalToInherit_base64) {
+      shEntry.principalToInherit = Utils.deserializePrincipal(entry.principalToInherit_base64);
     }
 
     if (entry.children && shEntry instanceof Ci.nsISHContainer) {
       for (var i = 0; i < entry.children.length; i++) {
         // XXXzpao Wallpaper patch for bug 514751
         if (!entry.children[i].url)
           continue;
 
--- a/toolkit/xre/nsEmbedFunctions.cpp
+++ b/toolkit/xre/nsEmbedFunctions.cpp
@@ -20,16 +20,17 @@
 #include "nsIDirectoryService.h"
 #include "nsIFile.h"
 #include "nsIToolkitChromeRegistry.h"
 #include "nsIToolkitProfile.h"
 
 #ifdef XP_WIN
 #include <process.h>
 #include "mozilla/ipc/WindowsMessageLoop.h"
+#include "mozilla/TlsAllocationTracker.h"
 #endif
 
 #include "nsAppDirectoryServiceDefs.h"
 #include "nsAppRunner.h"
 #include "nsAutoRef.h"
 #include "nsDirectoryServiceDefs.h"
 #include "nsExceptionHandler.h"
 #include "nsString.h"
@@ -353,16 +354,24 @@ XRE_InitChildProcess(int aArgc,
 #endif
 
 #ifdef MOZ_JPROF
   // Call the code to install our handler
   setupProfilingStuff();
 #endif
 
 #if defined(XP_WIN)
+#ifndef DEBUG
+  // XXX Bug 1320134: added for diagnosing the crashes because we're running out
+  // of TLS indices on Windows. Remove after the root cause is found.
+  if (XRE_GetProcessType() == GeckoProcessType_Content) {
+    mozilla::InitTlsAllocationTracker();
+  }
+#endif
+
   // From the --attach-console support in nsNativeAppSupportWin.cpp, but
   // here we are a content child process, so we always attempt to attach
   // to the parent's (ie, the browser's) console.
   // Try to attach console to the parent process.
   // It will succeed when the parent process is a command line,
   // so that stdio will be displayed in it.
   if (AttachConsole(ATTACH_PARENT_PROCESS)) {
     // Change std handles to refer to new console handles.
@@ -692,16 +701,24 @@ XRE_InitChildProcess(int aArgc,
 
 #if defined(XP_MACOSX)
       // Everybody should be done using shared memory by now.
       mozilla::ipc::SharedMemoryBasic::Shutdown();
 #endif
     }
   }
 
+#if defined(XP_WIN) && !defined(DEBUG)
+  // XXX Bug 1320134: added for diagnosing the crashes because we're running out
+  // of TLS indices on Windows. Remove after the root cause is found.
+  if (XRE_GetProcessType() == GeckoProcessType_Content) {
+    mozilla::ShutdownTlsAllocationTracker();
+  }
+#endif
+
   Telemetry::DestroyStatisticsRecorder();
   return XRE_DeinitCommandLine();
 }
 
 MessageLoop*
 XRE_GetIOMessageLoop()
 {
   if (sChildProcessType == GeckoProcessType_Default) {
new file mode 100644
--- /dev/null
+++ b/xpcom/build/TlsAllocationTracker.cpp
@@ -0,0 +1,249 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "TlsAllocationTracker.h"
+
+#include <stdint.h>
+
+#include <windows.h>
+
+#include "mozilla/Atomics.h"
+#include "mozilla/JSONWriter.h"
+#include "mozilla/Move.h"
+#include "mozilla/StackWalk.h"
+#include "mozilla/StaticMutex.h"
+#include "mozilla/StaticPtr.h"
+
+#include "nsDebug.h"
+#include "nsHashKeys.h"
+#include "nsString.h"
+#include "nsTArray.h"
+#include "nsTHashtable.h"
+#include "nsWindowsDllInterceptor.h"
+
+namespace mozilla {
+namespace {
+
+static StaticAutoPtr<nsCString> sTlsAllocationStacks;
+
+struct nsCStringWriter : public JSONWriteFunc {
+  explicit nsCStringWriter(nsCString* aData)
+    : mData(aData)
+  {
+    MOZ_ASSERT(mData);
+  }
+
+  void Write(const char* aStr)
+  {
+    mData->AppendASCII(aStr);
+  }
+
+  nsCString* mData;
+};
+
+// Start recording TlsAlloc() call stacks when we observed kStartTrackingTlsAt
+// allocations. We choose this value close to the maximum number of TLS indexes
+// in a Windows process, which is 1088, in the hope of catching TLS leaks
+// without impacting normal users.
+const uint32_t kStartTrackingTlsAt = 950;
+
+using stack_t = nsTArray<uintptr_t>;
+
+struct StackEntry : public nsUint32HashKey {
+  explicit StackEntry(KeyTypePointer aKey)
+    : nsUint32HashKey(aKey)
+  { }
+
+  stack_t mStack;
+};
+
+using stacks_t = nsTHashtable<StackEntry>;
+
+static StaticAutoPtr<stacks_t> sRecentTlsAllocationStacks;
+
+static Atomic<bool> sInitialized;
+static StaticMutex sMutex;
+static Atomic<uint32_t> sCurrentTlsSlots{0};
+
+using TlsAllocFn = DWORD (*)();
+using TlsFreeFn = BOOL(*)(DWORD);
+
+TlsAllocFn gOriginalTlsAlloc;
+TlsFreeFn gOriginalTlsFree;
+
+static void
+MaybeRecordCurrentStack(DWORD aTlsIndex)
+{
+  if (sCurrentTlsSlots < kStartTrackingTlsAt) {
+    return;
+  }
+
+  stack_t rawStack;
+  auto callback = [](uint32_t, void* aPC, void*, void* aClosure) {
+    auto stack = static_cast<stack_t*>(aClosure);
+    stack->AppendElement(reinterpret_cast<uintptr_t>(aPC));
+  };
+  MozStackWalk(callback, /* skip 2 frames */ 2,
+              /* maxFrames */ 0, &rawStack, 0, nullptr);
+
+  StaticMutexAutoLock lock(sMutex);
+  if (!sRecentTlsAllocationStacks) {
+    return;
+  }
+
+  StackEntry* stack = sRecentTlsAllocationStacks->PutEntry(aTlsIndex);
+
+  MOZ_ASSERT(!stack->mStack.IsEmpty());
+  stack->mStack = Move(rawStack);
+}
+
+static void
+MaybeDeleteRecordedStack(DWORD aTlsIndex)
+{
+  StaticMutexAutoLock lock(sMutex);
+  if (!sRecentTlsAllocationStacks) {
+    return;
+  }
+
+  auto entry = sRecentTlsAllocationStacks->GetEntry(aTlsIndex);
+  if (entry) {
+    sRecentTlsAllocationStacks->RemoveEntry(aTlsIndex);
+  }
+}
+
+
+static void
+AnnotateRecentTlsStacks()
+{
+  StaticMutexAutoLock lock(sMutex);
+  if (!sRecentTlsAllocationStacks) {
+    // Maybe another thread steals the stack vector content and is dumping the
+    // stacks
+    return;
+  }
+
+  // Move the content to prevent further requests to this function.
+  UniquePtr<stacks_t> stacks = MakeUnique<stacks_t>();
+  sRecentTlsAllocationStacks->SwapElements(*stacks.get());
+  sRecentTlsAllocationStacks = nullptr;
+
+  sTlsAllocationStacks = new nsCString();
+  JSONWriter output(
+    MakeUnique<nsCStringWriter, nsCString*>(sTlsAllocationStacks.get()));
+
+  output.Start(JSONWriter::SingleLineStyle);
+  for (auto iter = stacks->Iter(); !iter.Done(); iter.Next()) {
+    const stack_t& stack = iter.Get()->mStack;
+
+    output.StartArrayElement();
+    for (auto pc : stack) {
+      output.IntElement(pc);
+    }
+    output.EndArray();
+  }
+  output.End();
+}
+
+DWORD WINAPI
+InterposedTlsAlloc()
+{
+  if (!sInitialized) {
+    // Don't touch the function decoration before init or after shutdown.
+    return gOriginalTlsAlloc();
+  }
+
+  sCurrentTlsSlots += 1;
+
+  DWORD tlsAllocRv = gOriginalTlsAlloc();
+
+  MaybeRecordCurrentStack(tlsAllocRv);
+
+  if (tlsAllocRv == TLS_OUT_OF_INDEXES) {
+    AnnotateRecentTlsStacks();
+  }
+
+  return tlsAllocRv;
+}
+
+BOOL WINAPI
+InterposedTlsFree(DWORD aTlsIndex)
+{
+  if (!sInitialized) {
+    // Don't touch the function decoration before init or after shutdown.
+    return gOriginalTlsFree(aTlsIndex);
+  }
+
+  sCurrentTlsSlots -= 1;
+
+  MaybeDeleteRecordedStack(aTlsIndex);
+
+  return gOriginalTlsFree(aTlsIndex);
+}
+
+}  // Anonymous namespace.
+
+void
+InitTlsAllocationTracker()
+{
+  if (sInitialized) {
+    return;
+  }
+
+  sRecentTlsAllocationStacks = new stacks_t();
+
+  // Windows DLL interceptor
+  static WindowsDllInterceptor sKernel32DllInterceptor{};
+
+  // Initialize dll interceptor and add hook.
+  sKernel32DllInterceptor.Init("kernel32.dll");
+  bool succeeded = sKernel32DllInterceptor.AddHook(
+    "TlsAlloc",
+    reinterpret_cast<intptr_t>(InterposedTlsAlloc),
+    reinterpret_cast<void**>(&gOriginalTlsAlloc));
+
+  if (!succeeded) {
+    return;
+  }
+
+  succeeded = sKernel32DllInterceptor.AddHook(
+    "TlsFree",
+    reinterpret_cast<intptr_t>(InterposedTlsFree),
+    reinterpret_cast<void**>(&gOriginalTlsFree));
+
+  if (!succeeded) {
+    return;
+  }
+
+  sInitialized = true;
+}
+
+const char*
+GetTlsAllocationStacks()
+{
+  StaticMutexAutoLock lock(sMutex);
+
+  if (!sTlsAllocationStacks) {
+    return nullptr;
+  }
+
+  return sTlsAllocationStacks->BeginReading();
+}
+
+void
+ShutdownTlsAllocationTracker()
+{
+  if (!sInitialized) {
+    return;
+  }
+  sInitialized = false;
+
+  StaticMutexAutoLock lock(sMutex);
+
+  sRecentTlsAllocationStacks = nullptr;
+  sTlsAllocationStacks = nullptr;
+}
+
+}  // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/xpcom/build/TlsAllocationTracker.h
@@ -0,0 +1,27 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_TlsAllocationTracker_h
+#define mozilla_TlsAllocationTracker_h
+
+#include <string>
+
+#include "mozilla/Types.h"
+
+namespace mozilla {
+
+void
+InitTlsAllocationTracker();
+
+const char*
+GetTlsAllocationStacks();
+
+void
+ShutdownTlsAllocationTracker();
+
+}
+
+#endif
--- a/xpcom/build/moz.build
+++ b/xpcom/build/moz.build
@@ -17,27 +17,29 @@ EXPORTS += [
 EXPORTS.mozilla += [
     'FileLocation.h',
     'IOInterposer.h',
     'LateWriteChecks.h',
     'Omnijar.h',
     'PoisonIOInterposer.h',
     'ServiceList.h',
     'Services.h',
+    'TlsAllocationTracker.h',
     'XPCOM.h',
     'XREAppData.h',
 ]
 
 if CONFIG['OS_ARCH'] == 'WINNT':
     EXPORTS += ['nsWindowsDllInterceptor.h']
     EXPORTS.mozilla += ['perfprobe.h']
     SOURCES += [
         'perfprobe.cpp',
         'PoisonIOInterposerBase.cpp',
         'PoisonIOInterposerWin.cpp',
+        'TlsAllocationTracker.cpp',
     ]
 elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
     UNIFIED_SOURCES += [
         'PoisonIOInterposerBase.cpp',
         'PoisonIOInterposerMac.cpp',
     ]
     SOURCES += ['mach_override.c']
     SOURCES['mach_override.c'].flags += ['-Wno-unused-function']