merge mozilla-inbound to mozilla-central. r=merge a=merge
authorSebastian Hengst <archaeopteryx@coole-files.de>
Thu, 28 Sep 2017 11:44:21 +0200
changeset 383414 76a26ef7c493311c170ae83eb0c1d6592a21396d
parent 383405 82c2eecf82ba820c4593aa4a9749662f7d54d9a7 (current diff)
parent 383413 9e8b40ab6e924e0371da24f7f9887549ca930e49 (diff)
child 383415 3177c1b64ffe7ba5c08851791bd495421dcedb05
child 383457 36994cdc6201f085f16c83aaee85611613dda5f6
child 383563 b981cbc24c83c2058c20056a8b940d1770f32a8e
push id32591
push userarchaeopteryx@coole-files.de
push dateThu, 28 Sep 2017 09:44:43 +0000
treeherdermozilla-central@76a26ef7c493 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge, merge
milestone58.0a1
first release with
nightly linux32
76a26ef7c493 / 58.0a1 / 20170928100123 / files
nightly linux64
76a26ef7c493 / 58.0a1 / 20170928100123 / files
nightly mac
76a26ef7c493 / 58.0a1 / 20170928100123 / files
nightly win32
76a26ef7c493 / 58.0a1 / 20170928100123 / files
nightly win64
76a26ef7c493 / 58.0a1 / 20170928100123 / 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 mozilla-inbound to mozilla-central. r=merge a=merge MozReview-Commit-ID: 74cERYrHFIG
--- a/browser/installer/removed-files.in
+++ b/browser/installer/removed-files.in
@@ -50,67 +50,20 @@
 #   will succeed.
 # * If a file within the directory can't be removed the update will fail.
 #
 # Example: path/to/dir/*
 
 # Due to Apple Mac OS X packaging requirements files that are in the same
 # directory on other platforms must be located in different directories on
 # Mac OS X. The following defines allow specifying the Mac OS X bundle
-# location which also work on other platforms.
+# location which will also work on other platforms.
 #
 # @DIR_MACOS@
 # Equals Contents/MacOS/ on Mac OS X and is an empty string on other platforms.
 #
 # @DIR_RESOURCES@
 # Equals Contents/Resources/ on Mac OS X and is an empty string on other
 # platforms.
 
-# Common File Removals
-# This is located under the "distribution/" directory and it was added before
-# Firefox 27
-@DIR_MACOS@distribution/extensions/testpilot@labs.mozilla.com.xpi
-
-# Mac OS X v2 signing removals
-#ifdef XP_MACOSX
-  @DIR_MACOS@active-update.xml
-  @DIR_MACOS@update-settings.ini
-  @DIR_MACOS@updates.xml
-  @DIR_MACOS@defaults/*
-  @DIR_MACOS@updates/*
-#endif
-
-# Common Directory removals
-@DIR_MACOS@chrome/
-#ifdef XP_UNIX
-  #ifndef XP_MACOSX
-    chrome/icons/
-    chrome/icons/default/
-  #endif
-#endif
-@DIR_MACOS@chrome/overlayinfo/
-@DIR_MACOS@components/
-@DIR_MACOS@defaults/autoconfig/
-@DIR_MACOS@defaults/profile/
-@DIR_MACOS@defaults/profile/chrome/
-@DIR_MACOS@defaults/profile/US/*
-@DIR_MACOS@defaults/profile/extensions/
-@DIR_MACOS@defaults/profile/extensions/{972ce4c6-7e08-4474-a285-3208198ce6fd}/*
-@DIR_MACOS@distribution/
-@DIR_MACOS@distribution/extensions/
-@DIR_MACOS@extensions/
-@DIR_MACOS@extensions/inspector@mozilla.org/*
-@DIR_MACOS@extensions/reporter@mozilla.org/*
-@DIR_MACOS@extensions/talkback@mozilla.org/*
-@DIR_MACOS@extensions/testpilot@labs.mozilla.com/*
-@DIR_MACOS@extensions/{641d8d09-7dda-4850-8228-ac0ab65e2ac9}/*
-@DIR_MACOS@extensions/{972ce4c6-7e08-4474-a285-3208198ce6fd}/*
-@DIR_MACOS@greprefs/
-@DIR_MACOS@jssubloader/
-@DIR_MACOS@modules/
-#ifdef XP_MACOSX
-  @DIR_MACOS@plugins/Default Plugin.plugin/*
-  @DIR_MACOS@plugins/JavaEmbeddingPlugin.bundle/*
-  @DIR_MACOS@plugins/MRJPlugin.plugin/*
-  Contents/Plug-Ins/PrintPDE.plugin/*
-#endif
-@DIR_MACOS@searchplugins/*
-@DIR_MACOS@webapprt/components/
+# An update watershed was required to update to Firefox 56 for LZMA and SHA384
+# support. This made it possible to delete all of the removal instructions in
+# this file.
--- a/dom/canvas/WebGLContextExtensions.cpp
+++ b/dom/canvas/WebGLContextExtensions.cpp
@@ -274,47 +274,16 @@ WebGLContext::GetExtension(JSContext* cx
         WebGLExtensionID extension = WebGLExtensionID(i);
 
         if (CompareWebGLExtensionName(name, GetExtensionString(extension))) {
             ext = extension;
             break;
         }
     }
 
-    if (ext == WebGLExtensionID::Unknown) {
-        // We keep backward compatibility for these deprecated vendor-prefixed
-        // alias. Do not add new ones anymore. Hide it behind the
-        // webgl.enable-draft-extensions flag instead.
-
-        if (CompareWebGLExtensionName(name, "MOZ_WEBGL_lose_context")) {
-            ext = WebGLExtensionID::WEBGL_lose_context;
-
-        } else if (CompareWebGLExtensionName(name, "MOZ_WEBGL_compressed_texture_s3tc")) {
-            ext = WebGLExtensionID::WEBGL_compressed_texture_s3tc;
-
-        } else if (CompareWebGLExtensionName(name, "MOZ_WEBGL_compressed_texture_atc")) {
-            ext = WebGLExtensionID::WEBGL_compressed_texture_atc;
-
-        } else if (CompareWebGLExtensionName(name, "MOZ_WEBGL_compressed_texture_pvrtc")) {
-            ext = WebGLExtensionID::WEBGL_compressed_texture_pvrtc;
-
-        } else if (CompareWebGLExtensionName(name, "MOZ_WEBGL_depth_texture")) {
-            ext = WebGLExtensionID::WEBGL_depth_texture;
-        }
-
-        if (ext != WebGLExtensionID::Unknown) {
-            GenerateWarning("getExtension('%s'): MOZ_ prefixed WebGL extension"
-                            " strings are deprecated. Support for them will be"
-                            " removed in the future. Use unprefixed extension"
-                            " strings. To get draft extensions, set the"
-                            " webgl.enable-draft-extensions preference.",
-                            name.get());
-        }
-    }
-
     if (ext == WebGLExtensionID::Unknown)
         return;
 
     // step 2: check if the extension is supported
     if (!IsExtensionSupported(callerType, ext))
         return;
 
     // step 3: if the extension hadn't been previously been created, create it now, thus enabling it
@@ -470,31 +439,11 @@ WebGLContext::GetSupportedExtensions(dom
         if (extension == WebGLExtensionID::MOZ_debug)
             continue; // Hide MOZ_debug from this list.
 
         if (IsExtensionSupported(callerType, extension)) {
             const char* extStr = GetExtensionString(extension);
             arr.AppendElement(NS_ConvertUTF8toUTF16(extStr));
         }
     }
-
-    /**
-     * We keep backward compatibility for these deprecated vendor-prefixed
-     * alias. Do not add new ones anymore. Hide it behind the
-     * webgl.enable-draft-extensions flag instead.
-     */
-    if (IsExtensionSupported(callerType, WebGLExtensionID::WEBGL_lose_context))
-        arr.AppendElement(NS_LITERAL_STRING("MOZ_WEBGL_lose_context"));
-    if (IsExtensionSupported(callerType,
-                             WebGLExtensionID::WEBGL_compressed_texture_s3tc))
-        arr.AppendElement(NS_LITERAL_STRING("MOZ_WEBGL_compressed_texture_s3tc"));
-    if (IsExtensionSupported(callerType,
-                             WebGLExtensionID::WEBGL_compressed_texture_atc))
-        arr.AppendElement(NS_LITERAL_STRING("MOZ_WEBGL_compressed_texture_atc"));
-    if (IsExtensionSupported(callerType,
-                             WebGLExtensionID::WEBGL_compressed_texture_pvrtc))
-        arr.AppendElement(NS_LITERAL_STRING("MOZ_WEBGL_compressed_texture_pvrtc"));
-    if (IsExtensionSupported(callerType,
-                             WebGLExtensionID::WEBGL_depth_texture))
-        arr.AppendElement(NS_LITERAL_STRING("MOZ_WEBGL_depth_texture"));
 }
 
 } // namespace mozilla
--- a/js/src/ds/Fifo.h
+++ b/js/src/ds/Fifo.h
@@ -4,17 +4,18 @@
  * 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 js_Fifo_h
 #define js_Fifo_h
 
 #include "mozilla/Move.h"
 
-#include "js/Utility.h"
+#include "jsutil.h"
+
 #include "js/Vector.h"
 
 namespace js {
 
 // A first-in-first-out queue container type. Fifo calls constructors and
 // destructors of all elements added so non-PODs may be used safely. |Fifo|
 // stores the first |MinInlineCapacity| elements in-place before resorting to
 // dynamic allocation.
@@ -41,29 +42,21 @@ class Fifo
     // Invariant 2: Entries within |front_| are sorted from younger to older.
     // Invariant 3: Entries within |rear_| are sorted from older to younger.
     // Invariant 4: If the |Fifo| is not empty, then |front_| is not empty.
     Vector<T, MinInlineCapacity / 2, AllocPolicy> front_;
     Vector<T, MinInlineCapacity / 2, AllocPolicy> rear_;
 
   private:
     // Maintain invariants after adding or removing entries.
-    bool fixup() {
-        if (!front_.empty())
-            return true;
-
-        if (!front_.reserve(rear_.length()))
-            return false;
-
-        while (!rear_.empty()) {
-            front_.infallibleAppend(mozilla::Move(rear_.back()));
-            rear_.popBack();
+    void fixup() {
+        if (front_.empty() && !rear_.empty()) {
+            front_.swap(rear_);
+            Reverse(front_.begin(), front_.end());
         }
-
-        return true;
     }
 
   public:
     explicit Fifo(AllocPolicy alloc = AllocPolicy())
         : front_(alloc)
         , rear_(alloc)
     { }
 
@@ -93,64 +86,64 @@ class Fifo
     }
 
     // Push an element to the back of the queue. This method can take either a
     // |const T&| or a |T&&|.
     template <typename U>
     MOZ_MUST_USE bool pushBack(U&& u) {
         if (!rear_.append(mozilla::Forward<U>(u)))
             return false;
-        if (!fixup()) {
-            rear_.popBack();
-            return false;
-        }
+        fixup();
         return true;
     }
 
     // Construct a T in-place at the back of the queue.
     template <typename... Args>
     MOZ_MUST_USE bool emplaceBack(Args&&... args) {
         if (!rear_.emplaceBack(mozilla::Forward<Args>(args)...))
             return false;
-        if (!fixup()) {
-            rear_.popBack();
-            return false;
-        }
+        fixup();
         return true;
     }
 
     // Access the element at the front of the queue.
     T& front() {
         MOZ_ASSERT(!empty());
         return front_.back();
     }
     const T& front() const {
         MOZ_ASSERT(!empty());
         return front_.back();
     }
 
     // Remove the front element from the queue.
-    MOZ_MUST_USE bool popFront() {
+    void popFront() {
         MOZ_ASSERT(!empty());
-        T t(mozilla::Move(front()));
         front_.popBack();
-        if (!fixup()) {
-            // Attempt to remain in a valid state by reinserting the element
-            // back at the front. If we can't remain in a valid state in the
-            // face of OOMs, crash.
-            AutoEnterOOMUnsafeRegion oomUnsafe;
-            if (!front_.append(mozilla::Move(t)))
-                oomUnsafe.crash("js::Fifo::popFront");
-            return false;
-        }
-        return true;
+        fixup();
+    }
+
+    // Convenience utility.
+    T popCopyFront() {
+        T ret = front();
+        popFront();
+        return ret;
     }
 
     // Clear all elements from the queue.
     void clear() {
         front_.clear();
         rear_.clear();
     }
+
+    // Clear all elements for which the given predicate returns 'true'. Return
+    // the number of elements removed.
+    template <class Pred>
+    size_t eraseIf(Pred pred) {
+        size_t erased = EraseIf(front_, pred);
+        erased += EraseIf(rear_, pred);
+        return erased;
+    }
 };
 
 } // namespace js
 
 #endif /* js_Fifo_h */
--- a/js/src/ds/TraceableFifo.h
+++ b/js/src/ds/TraceableFifo.h
@@ -74,15 +74,15 @@ class MutableWrappedPtrOperations<Tracea
   public:
     T& front() { return fifo().front(); }
 
     template<typename U> bool pushBack(U&& u) { return fifo().pushBack(mozilla::Forward<U>(u)); }
     template<typename... Args> bool emplaceBack(Args&&... args) {
         return fifo().emplaceBack(mozilla::Forward<Args...>(args...));
     }
 
-    bool popFront() { return fifo().popFront(); }
+    void popFront() { fifo().popFront(); }
     void clear() { fifo().clear(); }
 };
 
 } // namespace js
 
 #endif // js_TraceableFifo_h
--- a/js/src/jsapi-tests/testGCExactRooting.cpp
+++ b/js/src/jsapi-tests/testGCExactRooting.cpp
@@ -318,17 +318,17 @@ BEGIN_TEST(testTraceableFifo)
     for (size_t i = 0; i < 10; ++i) {
         // Check the shape to ensure it did not get collected.
         char buffer[2];
         buffer[0] = 'a' + i;
         buffer[1] = '\0';
         bool match;
         CHECK(JS_StringEqualsAscii(cx, JSID_TO_STRING(shapes.front()->propid()), buffer, &match));
         CHECK(match);
-        CHECK(shapes.popFront());
+        shapes.popFront();
     }
 
     CHECK(shapes.empty());
     return true;
 }
 END_TEST(testTraceableFifo)
 
 using ShapeVec = GCVector<Shape*>;
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -2293,20 +2293,17 @@ Debugger::appendAllocationSite(JSContext
     auto inNursery = gc::IsInsideNursery(obj);
 
     if (!allocationsLog.emplaceBack(wrappedFrame, when, className, ctorName, size, inNursery)) {
         ReportOutOfMemory(cx);
         return false;
     }
 
     if (allocationsLog.length() > maxAllocationsLogLength) {
-        if (!allocationsLog.popFront()) {
-            ReportOutOfMemory(cx);
-            return false;
-        }
+        allocationsLog.popFront();
         MOZ_ASSERT(allocationsLog.length() == maxAllocationsLogLength);
         allocationsLogOverflowed = true;
     }
 
     return true;
 }
 
 JSTrapStatus
--- a/js/src/vm/DebuggerMemory.cpp
+++ b/js/src/vm/DebuggerMemory.cpp
@@ -233,20 +233,17 @@ DebuggerMemory::drainAllocationsLog(JSCo
         if (!DefineDataProperty(cx, obj, cx->names().inNursery, inNursery))
             return false;
 
         result->setDenseElement(i, ObjectValue(*obj));
 
         // Pop the front queue entry, and delete it immediately, so that the GC
         // sees the AllocationsLogEntry's HeapPtr barriers run atomically with
         // the change to the graph (the queue link).
-        if (!dbg->allocationsLog.popFront()) {
-            ReportOutOfMemory(cx);
-            return false;
-        }
+        dbg->allocationsLog.popFront();
     }
 
     dbg->allocationsLogOverflowed = false;
     args.rval().setObject(*result);
     return true;
 }
 
 /* static */ bool
@@ -273,22 +270,18 @@ DebuggerMemory::setMaxAllocationsLogLeng
                                   "(set maxAllocationsLogLength)'s parameter",
                                   "not a positive integer");
         return false;
     }
 
     Debugger* dbg = memory->getDebugger();
     dbg->maxAllocationsLogLength = max;
 
-    while (dbg->allocationsLog.length() > dbg->maxAllocationsLogLength) {
-        if (!dbg->allocationsLog.popFront()) {
-            ReportOutOfMemory(cx);
-            return false;
-        }
-    }
+    while (dbg->allocationsLog.length() > dbg->maxAllocationsLogLength)
+        dbg->allocationsLog.popFront();
 
     args.rval().setUndefined();
     return true;
 }
 
 /* static */ bool
 DebuggerMemory::getAllocationSamplingProbability(JSContext* cx, unsigned argc, Value* vp)
 {
--- a/js/src/vm/HelperThreads.cpp
+++ b/js/src/vm/HelperThreads.cpp
@@ -101,17 +101,17 @@ js::SetFakeCPUCount(size_t count)
     HelperThreadState().threadCount = ThreadCountForCPUCount(count);
 }
 
 bool
 js::StartOffThreadWasmCompile(wasm::CompileTask* task, wasm::CompileMode mode)
 {
     AutoLockHelperThreadState lock;
 
-    if (!HelperThreadState().wasmWorklist(lock, mode).append(task))
+    if (!HelperThreadState().wasmWorklist(lock, mode).pushBack(task))
         return false;
 
     HelperThreadState().notifyOne(GlobalHelperThreadState::PRODUCER, lock);
     return true;
 }
 
 void
 js::StartOffThreadWasmTier2Generator(wasm::UniqueTier2GeneratorTask task)
@@ -1797,17 +1797,17 @@ HelperThread::ThreadMain(void* arg)
 }
 
 void
 HelperThread::handleWasmWorkload(AutoLockHelperThreadState& locked, wasm::CompileMode mode)
 {
     MOZ_ASSERT(HelperThreadState().canStartWasmCompile(locked, mode));
     MOZ_ASSERT(idle());
 
-    currentTask.emplace(HelperThreadState().wasmWorklist(locked, mode).popCopy());
+    currentTask.emplace(HelperThreadState().wasmWorklist(locked, mode).popCopyFront());
 
     wasm::CompileTask* task = wasmTask();
     {
         AutoUnlockHelperThreadState unlock(locked);
         wasm::ExecuteCompileTaskFromHelperThread(task);
     }
 
     // No active thread should be waiting on the CONSUMER mutex.
--- a/js/src/vm/HelperThreads.h
+++ b/js/src/vm/HelperThreads.h
@@ -18,16 +18,17 @@
 #include "mozilla/PodOperations.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/TypeTraits.h"
 #include "mozilla/Variant.h"
 
 #include "jsapi.h"
 #include "jscntxt.h"
 
+#include "ds/Fifo.h"
 #include "jit/Ion.h"
 #include "threading/ConditionVariable.h"
 #include "vm/MutexIDs.h"
 
 namespace JS {
 struct Zone;
 } // namespace JS
 
@@ -52,17 +53,17 @@ enum class ParseTaskKind
     Module,
     ScriptDecode,
     MultiScriptsDecode
 };
 
 namespace wasm {
 
 struct CompileTask;
-typedef Vector<CompileTask*, 0, SystemAllocPolicy> CompileTaskPtrVector;
+typedef Fifo<CompileTask*, 0, SystemAllocPolicy> CompileTaskPtrFifo;
 
 struct Tier2GeneratorTask
 {
     virtual ~Tier2GeneratorTask() = default;
     virtual void cancel() = 0;
     virtual void execute() = 0;
 };
 
@@ -102,18 +103,18 @@ class GlobalHelperThreadState
 
   private:
     // The lists below are all protected by |lock|.
 
     // Ion compilation worklist and finished jobs.
     IonBuilderVector ionWorklist_, ionFinishedList_, ionFreeList_;
 
     // wasm worklists.
-    wasm::CompileTaskPtrVector wasmWorklist_tier1_;
-    wasm::CompileTaskPtrVector wasmWorklist_tier2_;
+    wasm::CompileTaskPtrFifo wasmWorklist_tier1_;
+    wasm::CompileTaskPtrFifo wasmWorklist_tier2_;
     wasm::Tier2GeneratorTaskPtrVector wasmTier2GeneratorWorklist_;
 
     // Count of finished Tier2Generator tasks.
     uint32_t wasmTier2GeneratorsFinished_;
 
     // Async tasks that, upon completion, are dispatched back to the JSContext's
     // owner thread via embedding callbacks instead of a finished list.
     PromiseHelperTaskVector promiseHelperTasks_;
@@ -195,17 +196,17 @@ class GlobalHelperThreadState
     }
     IonBuilderVector& ionFinishedList(const AutoLockHelperThreadState&) {
         return ionFinishedList_;
     }
     IonBuilderVector& ionFreeList(const AutoLockHelperThreadState&) {
         return ionFreeList_;
     }
 
-    wasm::CompileTaskPtrVector& wasmWorklist(const AutoLockHelperThreadState&, wasm::CompileMode m) {
+    wasm::CompileTaskPtrFifo& wasmWorklist(const AutoLockHelperThreadState&, wasm::CompileMode m) {
         switch (m) {
           case wasm::CompileMode::Once:
           case wasm::CompileMode::Tier1:
             return wasmWorklist_tier1_;
           case wasm::CompileMode::Tier2:
             return wasmWorklist_tier2_;
           default:
             MOZ_CRASH();
--- a/js/src/wasm/AsmJS.cpp
+++ b/js/src/wasm/AsmJS.cpp
@@ -361,19 +361,19 @@ struct js::AsmJSMetadata : Metadata, Asm
     }
     const char16_t* displayURL() const override {
         return scriptSource.get()->hasDisplayURL() ? scriptSource.get()->displayURL() : nullptr;
     }
     ScriptSource* maybeScriptSource() const override {
         return scriptSource.get();
     }
     bool getFuncName(const Bytes* maybeBytecode, uint32_t funcIndex, UTF8Bytes* name) const override {
-        // asm.js doesn't allow exporting imports or putting imports in tables
-        MOZ_ASSERT(funcIndex >= AsmJSFirstDefFuncIndex);
-        const char* p = asmJSFuncNames[funcIndex - AsmJSFirstDefFuncIndex].get();
+        const char* p = asmJSFuncNames[funcIndex].get();
+        if (!p)
+            return true;
         return name->append(p, strlen(p));
     }
 
     AsmJSMetadataCacheablePod& pod() { return *this; }
     const AsmJSMetadataCacheablePod& pod() const { return *this; }
 
     WASM_DECLARE_SERIALIZABLE_OVERRIDE(AsmJSMetadata)
 };
@@ -1380,81 +1380,95 @@ static const unsigned VALIDATION_LIFO_DE
 // ModuleValidator is marked as rooted in the rooting analysis.  Don't add
 // non-JSAtom pointers, or this will break!
 class MOZ_STACK_CLASS ModuleValidator
 {
   public:
     class Func
     {
         PropertyName* name_;
+        uint32_t sigIndex_;
         uint32_t firstUse_;
-        uint32_t index_;
+        uint32_t funcDefIndex_;
+
+        bool defined_;
+
+        // Available when defined:
         uint32_t srcBegin_;
         uint32_t srcEnd_;
-        bool defined_;
+        uint32_t line_;
+        Bytes bytes_;
+        Uint32Vector callSiteLineNums_;
 
       public:
-        Func(PropertyName* name, uint32_t firstUse, uint32_t index)
-          : name_(name), firstUse_(firstUse), index_(index),
-            srcBegin_(0), srcEnd_(0), defined_(false)
+        Func(PropertyName* name, uint32_t sigIndex, uint32_t firstUse, uint32_t funcDefIndex)
+          : name_(name), sigIndex_(sigIndex), firstUse_(firstUse), funcDefIndex_(funcDefIndex),
+            defined_(false), srcBegin_(0), srcEnd_(0), line_(0)
         {}
 
         PropertyName* name() const { return name_; }
+        uint32_t sigIndex() const { return sigIndex_; }
         uint32_t firstUse() const { return firstUse_; }
         bool defined() const { return defined_; }
-        uint32_t index() const { return index_; }
-
-        void define(ParseNode* fn) {
+        uint32_t funcDefIndex() const { return funcDefIndex_; }
+
+        void define(ParseNode* fn, uint32_t line, Bytes&& bytes, Uint32Vector&& callSiteLineNums) {
             MOZ_ASSERT(!defined_);
             defined_ = true;
             srcBegin_ = fn->pn_pos.begin;
             srcEnd_ = fn->pn_pos.end;
+            line_ = line;
+            bytes_ = Move(bytes);
+            callSiteLineNums_ = Move(callSiteLineNums);
         }
 
         uint32_t srcBegin() const { MOZ_ASSERT(defined_); return srcBegin_; }
         uint32_t srcEnd() const { MOZ_ASSERT(defined_); return srcEnd_; }
+        uint32_t line() const { MOZ_ASSERT(defined_); return line_; }
+        const Bytes& bytes() const { MOZ_ASSERT(defined_); return bytes_; }
+        Uint32Vector& callSiteLineNums() { MOZ_ASSERT(defined_); return callSiteLineNums_; }
     };
 
     typedef Vector<const Func*> ConstFuncVector;
-    typedef Vector<Func*> FuncVector;
-
-    class FuncPtrTable
+    typedef Vector<Func> FuncVector;
+
+    class Table
     {
         uint32_t sigIndex_;
         PropertyName* name_;
         uint32_t firstUse_;
         uint32_t mask_;
         bool defined_;
 
-        FuncPtrTable(FuncPtrTable&& rhs) = delete;
+        Table(Table&& rhs) = delete;
 
       public:
-        FuncPtrTable(uint32_t sigIndex, PropertyName* name, uint32_t firstUse, uint32_t mask)
+        Table(uint32_t sigIndex, PropertyName* name, uint32_t firstUse, uint32_t mask)
           : sigIndex_(sigIndex), name_(name), firstUse_(firstUse), mask_(mask), defined_(false)
         {}
 
         uint32_t sigIndex() const { return sigIndex_; }
         PropertyName* name() const { return name_; }
         uint32_t firstUse() const { return firstUse_; }
         unsigned mask() const { return mask_; }
         bool defined() const { return defined_; }
         void define() { MOZ_ASSERT(!defined_); defined_ = true; }
     };
 
-    typedef Vector<FuncPtrTable*> FuncPtrTableVector;
+    typedef Vector<Table*> TableVector;
 
     class Global
     {
       public:
         enum Which {
             Variable,
             ConstantLiteral,
             ConstantImport,
             Function,
-            FuncPtrTable,
+            Table,
             FFI,
             ArrayView,
             ArrayViewCtor,
             MathBuiltinFunction,
             AtomicsBuiltinFunction,
             SimdCtor,
             SimdOp
         };
@@ -1462,18 +1476,18 @@ class MOZ_STACK_CLASS ModuleValidator
       private:
         Which which_;
         union {
             struct {
                 Type::Which type_;
                 unsigned index_;
                 NumLit literalValue_;
             } varOrConst;
-            uint32_t funcIndex_;
-            uint32_t funcPtrTableIndex_;
+            uint32_t funcDefIndex_;
+            uint32_t tableIndex_;
             uint32_t ffiIndex_;
             struct {
                 Scalar::Type viewType_;
             } viewInfo;
             AsmJSMathBuiltinFunction mathBuiltinFunc_;
             AsmJSAtomicsBuiltinFunction atomicsBuiltinFunc_;
             SimdType simdCtorType_;
             struct {
@@ -1501,23 +1515,23 @@ class MOZ_STACK_CLASS ModuleValidator
         }
         bool isConst() const {
             return which_ == ConstantLiteral || which_ == ConstantImport;
         }
         NumLit constLiteralValue() const {
             MOZ_ASSERT(which_ == ConstantLiteral);
             return u.varOrConst.literalValue_;
         }
-        uint32_t funcIndex() const {
+        uint32_t funcDefIndex() const {
             MOZ_ASSERT(which_ == Function);
-            return u.funcIndex_;
-        }
-        uint32_t funcPtrTableIndex() const {
-            MOZ_ASSERT(which_ == FuncPtrTable);
-            return u.funcPtrTableIndex_;
+            return u.funcDefIndex_;
+        }
+        uint32_t tableIndex() const {
+            MOZ_ASSERT(which_ == Table);
+            return u.tableIndex_;
         }
         unsigned ffiIndex() const {
             MOZ_ASSERT(which_ == FFI);
             return u.ffiIndex_;
         }
         bool isAnyArrayView() const {
             return which_ == ArrayView || which_ == ArrayViewCtor;
         }
@@ -1584,47 +1598,70 @@ class MOZ_STACK_CLASS ModuleValidator
           : name(name), type(type)
         {}
 
         PropertyName* name;
         Scalar::Type type;
     };
 
   private:
-    class NamedSig
+    class HashableSig
+    {
+        uint32_t sigIndex_;
+        const SigWithIdVector& sigs_;
+
+      public:
+        HashableSig(uint32_t sigIndex, const SigWithIdVector& sigs)
+          : sigIndex_(sigIndex), sigs_(sigs)
+        {}
+        uint32_t sigIndex() const {
+            return sigIndex_;
+        }
+        const Sig& sig() const {
+            return sigs_[sigIndex_];
+        }
+
+        // Implement HashPolicy:
+        typedef const Sig& Lookup;
+        static HashNumber hash(Lookup l) {
+            return l.hash();
+        }
+        static bool match(HashableSig lhs, Lookup rhs) {
+            return lhs.sig() == rhs;
+        }
+    };
+
+    class NamedSig : public HashableSig
     {
         PropertyName* name_;
-        const SigWithId* sig_;
 
       public:
-        NamedSig(PropertyName* name, const SigWithId& sig)
-          : name_(name), sig_(&sig)
+        NamedSig(PropertyName* name, uint32_t sigIndex, const SigWithIdVector& sigs)
+          : HashableSig(sigIndex, sigs), name_(name)
         {}
         PropertyName* name() const {
             return name_;
         }
-        const Sig& sig() const {
-            return *sig_;
-        }
 
         // Implement HashPolicy:
         struct Lookup {
             PropertyName* name;
             const Sig& sig;
             Lookup(PropertyName* name, const Sig& sig) : name(name), sig(sig) {}
         };
         static HashNumber hash(Lookup l) {
             return HashGeneric(l.name, l.sig.hash());
         }
         static bool match(NamedSig lhs, Lookup rhs) {
-            return lhs.name_ == rhs.name && *lhs.sig_ == rhs.sig;
+            return lhs.name() == rhs.name && lhs.sig() == rhs.sig;
         }
     };
-    typedef HashMap<NamedSig, uint32_t, NamedSig> ImportMap;
-    typedef HashMap<const SigWithId*, uint32_t, SigHashPolicy> SigMap;
+
+    typedef HashSet<HashableSig, HashableSig> SigSet;
+    typedef HashMap<NamedSig, uint32_t, NamedSig> FuncImportMap;
     typedef HashMap<PropertyName*, Global*> GlobalMap;
     typedef HashMap<PropertyName*, MathBuiltin> MathNameMap;
     typedef HashMap<PropertyName*, AsmJSAtomicsBuiltinFunction> AtomicsNameMap;
     typedef HashMap<PropertyName*, SimdOperation> SimdOperationNameMap;
     typedef Vector<ArrayView> ArrayViewVector;
 
     JSContext*            cx_;
     AsmJSParser&          parser_;
@@ -1635,28 +1672,27 @@ class MOZ_STACK_CLASS ModuleValidator
     PropertyName*         bufferArgumentName_;
     MathNameMap           standardLibraryMathNames_;
     AtomicsNameMap        standardLibraryAtomicsNames_;
     SimdOperationNameMap  standardLibrarySimdOpNames_;
     RootedFunction        dummyFunction_;
 
     // Validation-internal state:
     LifoAlloc             validationLifo_;
-    FuncVector            functions_;
-    FuncPtrTableVector    funcPtrTables_;
+    FuncVector            funcDefs_;
+    TableVector           tables_;
     GlobalMap             globalMap_;
-    SigMap                sigMap_;
-    ImportMap             importMap_;
+    SigSet                sigSet_;
+    FuncImportMap         funcImportMap_;
     ArrayViewVector       arrayViews_;
     bool                  atomicsPresent_;
     bool                  simdPresent_;
 
     // State used to build the AsmJSModule in finish():
     ModuleEnvironment     env_;
-    ModuleGenerator       mg_;
     MutableAsmJSMetadata  asmJSMetadata_;
 
     // Error reporting:
     UniqueChars           errorString_;
     uint32_t              errorOffset_;
     bool                  errorOverRecursed_;
 
     // Helpers:
@@ -1682,65 +1718,63 @@ class MOZ_STACK_CLASS ModuleValidator
     }
     bool addStandardLibrarySimdOpName(const char* name, SimdOperation op) {
         JSAtom* atom = Atomize(cx_, name, strlen(name));
         if (!atom)
             return false;
         return standardLibrarySimdOpNames_.putNew(atom->asPropertyName(), op);
     }
     bool newSig(Sig&& sig, uint32_t* sigIndex) {
-        *sigIndex = 0;
-        if (mg_.numSigs() >= AsmJSMaxTypes)
+        if (env_.sigs.length() >= MaxTypes)
             return failCurrentOffset("too many signatures");
 
-        *sigIndex = mg_.numSigs();
-        mg_.initSig(*sigIndex, Move(sig));
-        return true;
+        *sigIndex = env_.sigs.length();
+        return env_.sigs.append(Move(sig));
     }
     bool declareSig(Sig&& sig, uint32_t* sigIndex) {
-        SigMap::AddPtr p = sigMap_.lookupForAdd(sig);
+        SigSet::AddPtr p = sigSet_.lookupForAdd(sig);
         if (p) {
-            *sigIndex = p->value();
-            MOZ_ASSERT(mg_.sig(*sigIndex) == sig);
+            *sigIndex = p->sigIndex();
+            MOZ_ASSERT(env_.sigs[*sigIndex] == sig);
             return true;
         }
 
         return newSig(Move(sig), sigIndex) &&
-               sigMap_.add(p, &mg_.sig(*sigIndex), *sigIndex);
+               sigSet_.add(p, HashableSig(*sigIndex, env_.sigs));
     }
 
   public:
-    ModuleValidator(JSContext* cx, const CompileArgs& args, AsmJSParser& parser,
-                    ParseNode* moduleFunctionNode)
+    ModuleValidator(JSContext* cx, AsmJSParser& parser, ParseNode* moduleFunctionNode)
       : cx_(cx),
         parser_(parser),
         moduleFunctionNode_(moduleFunctionNode),
         moduleFunctionName_(FunctionName(moduleFunctionNode)),
         globalArgumentName_(nullptr),
         importArgumentName_(nullptr),
         bufferArgumentName_(nullptr),
         standardLibraryMathNames_(cx),
         standardLibraryAtomicsNames_(cx),
         standardLibrarySimdOpNames_(cx),
         dummyFunction_(cx),
         validationLifo_(VALIDATION_LIFO_DEFAULT_CHUNK_SIZE),
-        functions_(cx),
-        funcPtrTables_(cx),
+        funcDefs_(cx),
+        tables_(cx),
         globalMap_(cx),
-        sigMap_(cx),
-        importMap_(cx),
+        sigSet_(cx),
+        funcImportMap_(cx),
         arrayViews_(cx),
         atomicsPresent_(false),
         simdPresent_(false),
         env_(CompileMode::Once, Tier::Ion, DebugEnabled::False, ModuleKind::AsmJS),
-        mg_(args, &env_, nullptr, nullptr),
         errorString_(nullptr),
         errorOffset_(UINT32_MAX),
         errorOverRecursed_(false)
-    {}
+    {
+        env_.minMemoryLength = RoundUpToNextValidAsmJSHeapLength(0);
+    }
 
     ~ModuleValidator() {
         if (errorString_) {
             MOZ_ASSERT(errorOffset_ != UINT32_MAX);
             typeFailure(errorOffset_, errorString_.get());
         }
         if (errorOverRecursed_)
             ReportOverRecursed(cx_);
@@ -1785,17 +1819,17 @@ class MOZ_STACK_CLASS ModuleValidator
             return false;
 
         asmJSMetadata_->toStringStart = moduleFunctionNode_->pn_funbox->toStringStart;
         asmJSMetadata_->srcStart = moduleFunctionNode_->pn_body->pn_pos.begin;
         asmJSMetadata_->strict = parser_.pc->sc()->strict() &&
                                  !parser_.pc->sc()->hasExplicitUseStrict();
         asmJSMetadata_->scriptSource.reset(parser_.ss);
 
-        if (!globalMap_.init() || !sigMap_.init() || !importMap_.init())
+        if (!globalMap_.init() || !sigSet_.init() || !funcImportMap_.init())
             return false;
 
         if (!standardLibraryMathNames_.init() ||
             !addStandardLibraryMathName("sin", AsmJSMathBuiltin_sin) ||
             !addStandardLibraryMathName("cos", AsmJSMathBuiltin_cos) ||
             !addStandardLibraryMathName("tan", AsmJSMathBuiltin_tan) ||
             !addStandardLibraryMathName("asin", AsmJSMathBuiltin_asin) ||
             !addStandardLibraryMathName("acos", AsmJSMathBuiltin_acos) ||
@@ -1850,41 +1884,31 @@ class MOZ_STACK_CLASS ModuleValidator
 
         // This flows into FunctionBox, so must be tenured.
         dummyFunction_ = NewScriptedFunction(cx_, 0, JSFunction::INTERPRETED, nullptr,
                                              /* proto = */ nullptr, gc::AllocKind::FUNCTION,
                                              TenuredObject);
         if (!dummyFunction_)
             return false;
 
-        env_.minMemoryLength = RoundUpToNextValidAsmJSHeapLength(0);
-        if (!env_.sigs.resize(AsmJSMaxTypes) ||
-            !env_.funcSigs.resize(AsmJSMaxFuncs) ||
-            !env_.funcImportGlobalDataOffsets.resize(AsmJSMaxImports) ||
-            !env_.tables.resize(AsmJSMaxTables) ||
-            !env_.asmJSSigToTableIndex.resize(AsmJSMaxTypes))
-        {
-            return false;
-        }
-
-        return mg_.init(/* codeSectionSize (ignored) = */ 0, asmJSMetadata_.get());
+        return true;
     }
 
     JSContext* cx() const                    { return cx_; }
     PropertyName* moduleFunctionName() const { return moduleFunctionName_; }
     PropertyName* globalArgumentName() const { return globalArgumentName_; }
     PropertyName* importArgumentName() const { return importArgumentName_; }
     PropertyName* bufferArgumentName() const { return bufferArgumentName_; }
-    ModuleGenerator& mg()                    { return mg_; }
+    const ModuleEnvironment& env()           { return env_; }
     AsmJSParser& parser() const              { return parser_; }
     TokenStream& tokenStream() const         { return parser_.tokenStream; }
     RootedFunction& dummyFunction()          { return dummyFunction_; }
     bool supportsSimd() const                { return cx_->jitSupportsSimd(); }
     bool atomicsPresent() const              { return atomicsPresent_; }
-    uint32_t minMemoryLength() const         { return mg_.minMemoryLength(); }
+    uint32_t minMemoryLength() const         { return env_.minMemoryLength; }
 
     void initModuleFunctionName(PropertyName* name) {
         MOZ_ASSERT(!moduleFunctionName_);
         moduleFunctionName_ = name;
     }
     MOZ_MUST_USE bool initGlobalArgumentName(PropertyName* n) {
         globalArgumentName_ = n;
         if (n) {
@@ -1914,18 +1938,18 @@ class MOZ_STACK_CLASS ModuleValidator
                 return false;
         }
         return true;
     }
     bool addGlobalVarInit(PropertyName* var, const NumLit& lit, Type type, bool isConst) {
         MOZ_ASSERT(type.isGlobalVarType());
         MOZ_ASSERT(type == Type::canonicalize(Type::lit(lit)));
 
-        uint32_t index;
-        if (!mg_.addGlobal(type.canonicalToValType(), isConst, &index))
+        uint32_t index = env_.globals.length();
+        if (!env_.globals.emplaceBack(type.canonicalToValType(), !isConst, index))
             return false;
 
         Global::Which which = isConst ? Global::ConstantLiteral : Global::Variable;
         Global* global = validationLifo_.new_<Global>(which);
         if (!global)
             return false;
         global->u.varOrConst.index_ = index;
         global->u.varOrConst.type_ = (isConst ? Type::lit(lit) : type).which();
@@ -1941,19 +1965,19 @@ class MOZ_STACK_CLASS ModuleValidator
     }
     bool addGlobalVarImport(PropertyName* var, PropertyName* field, Type type, bool isConst) {
         MOZ_ASSERT(type.isGlobalVarType());
 
         UniqueChars fieldChars = StringToNewUTF8CharsZ(cx_, *field);
         if (!fieldChars)
             return false;
 
-        uint32_t index;
+        uint32_t index = env_.globals.length();
         ValType valType = type.canonicalToValType();
-        if (!mg_.addGlobal(valType, isConst, &index))
+        if (!env_.globals.emplaceBack(valType, !isConst, index))
             return false;
 
         Global::Which which = isConst ? Global::ConstantImport : Global::Variable;
         Global* global = validationLifo_.new_<Global>(which);
         if (!global)
             return false;
         global->u.varOrConst.index_ = index;
         global->u.varOrConst.type_ = type.which();
@@ -2145,95 +2169,121 @@ class MOZ_STACK_CLASS ModuleValidator
             fieldChars = StringToNewUTF8CharsZ(cx_, *maybeField);
         else
             fieldChars = DuplicateString("");
         if (!fieldChars)
             return false;
 
         // Declare which function is exported which gives us an index into the
         // module ExportVector.
-        if (!mg_.addExport(Move(fieldChars), func.index()))
+        uint32_t funcIndex = funcImportMap_.count() + func.funcDefIndex();
+        if (!env_.exports.emplaceBack(Move(fieldChars), funcIndex, DefinitionKind::Function))
             return false;
 
         // The exported function might have already been exported in which case
         // the index will refer into the range of AsmJSExports.
-        return asmJSMetadata_->asmJSExports.emplaceBack(func.index(),
+        return asmJSMetadata_->asmJSExports.emplaceBack(funcIndex,
                                                         func.srcBegin() - asmJSMetadata_->srcStart,
                                                         func.srcEnd() - asmJSMetadata_->srcStart);
     }
-    bool addFunction(PropertyName* name, uint32_t firstUse, Sig&& sig, Func** func) {
+    bool addFuncDef(PropertyName* name, uint32_t firstUse, Sig&& sig, Func** func) {
         uint32_t sigIndex;
         if (!declareSig(Move(sig), &sigIndex))
             return false;
-        uint32_t funcIndex = AsmJSFirstDefFuncIndex + numFunctions();
-        if (funcIndex >= AsmJSMaxFuncs)
+
+        uint32_t funcDefIndex = funcDefs_.length();
+        if (funcDefIndex >= MaxFuncs)
             return failCurrentOffset("too many functions");
-        mg_.initFuncSig(funcIndex, sigIndex);
+
         Global* global = validationLifo_.new_<Global>(Global::Function);
         if (!global)
             return false;
-        global->u.funcIndex_ = funcIndex;
+        global->u.funcDefIndex_ = funcDefIndex;
         if (!globalMap_.putNew(name, global))
             return false;
-        *func = validationLifo_.new_<Func>(name, firstUse, funcIndex);
-        return *func && functions_.append(*func);
+        if (!funcDefs_.emplaceBack(name, sigIndex, firstUse, funcDefIndex))
+            return false;
+        *func = &funcDefs_.back();
+        return true;
     }
     bool declareFuncPtrTable(Sig&& sig, PropertyName* name, uint32_t firstUse, uint32_t mask,
-                             uint32_t* index)
+                             uint32_t* tableIndex)
     {
         if (mask > MaxTableInitialLength)
             return failCurrentOffset("function pointer table too big");
+
+        MOZ_ASSERT(env_.tables.length() == tables_.length());
+        *tableIndex = env_.tables.length();
+
         uint32_t sigIndex;
         if (!newSig(Move(sig), &sigIndex))
             return false;
-        if (!mg_.initSigTableLength(sigIndex, mask + 1))
-            return false;
-        Global* global = validationLifo_.new_<Global>(Global::FuncPtrTable);
+
+        MOZ_ASSERT(sigIndex >= env_.asmJSSigToTableIndex.length());
+        if (!env_.asmJSSigToTableIndex.resize(sigIndex + 1))
+            return false;
+
+        env_.asmJSSigToTableIndex[sigIndex] = env_.tables.length();
+        if (!env_.tables.emplaceBack(TableKind::TypedFunction, Limits(mask + 1)))
+            return false;
+
+        Global* global = validationLifo_.new_<Global>(Global::Table);
         if (!global)
             return false;
-        global->u.funcPtrTableIndex_ = *index = funcPtrTables_.length();
+
+        global->u.tableIndex_ = *tableIndex;
         if (!globalMap_.putNew(name, global))
             return false;
-        FuncPtrTable* t = validationLifo_.new_<FuncPtrTable>(sigIndex, name, firstUse, mask);
-        return t && funcPtrTables_.append(t);
-    }
-    bool defineFuncPtrTable(uint32_t funcPtrTableIndex, Uint32Vector&& elems) {
-        FuncPtrTable& table = *funcPtrTables_[funcPtrTableIndex];
+
+        Table* t = validationLifo_.new_<Table>(sigIndex, name, firstUse, mask);
+        return t && tables_.append(t);
+    }
+    bool defineFuncPtrTable(uint32_t tableIndex, Uint32Vector&& elems) {
+        Table& table = *tables_[tableIndex];
         if (table.defined())
             return false;
+
         table.define();
-        return mg_.initSigTableElems(table.sigIndex(), Move(elems));
-    }
-    bool declareImport(PropertyName* name, Sig&& sig, unsigned ffiIndex, uint32_t* funcIndex) {
-        ImportMap::AddPtr p = importMap_.lookupForAdd(NamedSig::Lookup(name, sig));
+
+        for (uint32_t& index : elems)
+            index += funcImportMap_.count();
+
+        return env_.elemSegments.emplaceBack(tableIndex, InitExpr(Val(uint32_t(0))), Move(elems));
+    }
+    bool declareImport(PropertyName* name, Sig&& sig, unsigned ffiIndex, uint32_t* importIndex) {
+        FuncImportMap::AddPtr p = funcImportMap_.lookupForAdd(NamedSig::Lookup(name, sig));
         if (p) {
-            *funcIndex = p->value();
+            *importIndex = p->value();
             return true;
         }
-        *funcIndex = asmJSMetadata_->asmJSImports.length();
-        if (*funcIndex > AsmJSMaxImports)
+
+        *importIndex = funcImportMap_.count();
+        MOZ_ASSERT(*importIndex == asmJSMetadata_->asmJSImports.length());
+
+        if (*importIndex >= MaxImports)
             return failCurrentOffset("too many imports");
+
         if (!asmJSMetadata_->asmJSImports.emplaceBack(ffiIndex))
             return false;
+
         uint32_t sigIndex;
         if (!declareSig(Move(sig), &sigIndex))
             return false;
-        if (!mg_.initImport(*funcIndex, sigIndex))
-            return false;
-        return importMap_.add(p, NamedSig(name, mg_.sig(sigIndex)), *funcIndex);
+
+        return funcImportMap_.add(p, NamedSig(name, sigIndex, env_.sigs), *importIndex);
     }
 
     bool tryConstantAccess(uint64_t start, uint64_t width) {
         MOZ_ASSERT(UINT64_MAX - start > width);
         uint64_t len = start + width;
         if (len > uint64_t(INT32_MAX) + 1)
             return false;
         len = RoundUpToNextValidAsmJSHeapLength(len);
-        if (len > mg_.minMemoryLength())
-            mg_.bumpMinMemoryLength(len);
+        if (len > env_.minMemoryLength)
+            env_.minMemoryLength = len;
         return true;
     }
 
     // Error handling.
     bool hasAlreadyFailed() const {
         return !!errorString_;
     }
 
@@ -2298,42 +2348,40 @@ class MOZ_STACK_CLASS ModuleValidator
     }
 
     unsigned numArrayViews() const {
         return arrayViews_.length();
     }
     const ArrayView& arrayView(unsigned i) const {
         return arrayViews_[i];
     }
-    unsigned numFunctions() const {
-        return functions_.length();
-    }
-    Func& function(unsigned i) const {
-        return *functions_[i];
+    unsigned numFuncDefs() const {
+        return funcDefs_.length();
+    }
+    const Func& funcDef(unsigned i) const {
+        return funcDefs_[i];
     }
     unsigned numFuncPtrTables() const {
-        return funcPtrTables_.length();
-    }
-    FuncPtrTable& funcPtrTable(unsigned i) const {
-        return *funcPtrTables_[i];
+        return tables_.length();
+    }
+    Table& table(unsigned i) const {
+        return *tables_[i];
     }
 
     const Global* lookupGlobal(PropertyName* name) const {
         if (GlobalMap::Ptr p = globalMap_.lookup(name))
             return p->value();
         return nullptr;
     }
 
-    Func* lookupFunction(PropertyName* name) {
+    Func* lookupFuncDef(PropertyName* name) {
         if (GlobalMap::Ptr p = globalMap_.lookup(name)) {
             Global* value = p->value();
-            if (value->which() == Global::Function) {
-                MOZ_ASSERT(value->funcIndex() >= AsmJSFirstDefFuncIndex);
-                return functions_[value->funcIndex() - AsmJSFirstDefFuncIndex];
-            }
+            if (value->which() == Global::Function)
+                return &funcDefs_[value->funcDefIndex()];
         }
         return nullptr;
     }
 
     bool lookupStandardLibraryMathName(PropertyName* name, MathBuiltin* mathBuiltin) const {
         if (MathNameMap::Ptr p = standardLibraryMathNames_.lookup(name)) {
             *mathBuiltin = p->value();
             return true;
@@ -2352,48 +2400,99 @@ class MOZ_STACK_CLASS ModuleValidator
             *op = p->value();
             return true;
         }
         return false;
     }
 
     bool startFunctionBodies() {
         if (!arrayViews_.empty())
-            mg_.initMemoryUsage(atomicsPresent_ ? MemoryUsage::Shared : MemoryUsage::Unshared);
-
-        return mg_.startFuncDefs();
-    }
-    bool finishFunctionBodies() {
-        return mg_.finishFuncDefs();
+            env_.memoryUsage = atomicsPresent_ ? MemoryUsage::Shared : MemoryUsage::Unshared;
+        else
+            env_.memoryUsage = MemoryUsage::None;
+        return true;
     }
     SharedModule finish() {
+        MOZ_ASSERT(env_.funcSigs.empty());
+        if (!env_.funcSigs.resize(funcImportMap_.count() + funcDefs_.length()))
+            return nullptr;
+        for (FuncImportMap::Range r = funcImportMap_.all(); !r.empty(); r.popFront()) {
+            uint32_t funcIndex = r.front().value();
+            MOZ_ASSERT(!env_.funcSigs[funcIndex]);
+            env_.funcSigs[funcIndex] = &env_.sigs[r.front().key().sigIndex()];
+        }
+        for (const Func& func : funcDefs_) {
+            uint32_t funcIndex = funcImportMap_.count() + func.funcDefIndex();
+            MOZ_ASSERT(!env_.funcSigs[funcIndex]);
+            env_.funcSigs[funcIndex] = &env_.sigs[func.sigIndex()];
+        }
+
+        if (!env_.funcImportGlobalDataOffsets.resize(funcImportMap_.count()))
+            return nullptr;
+
         asmJSMetadata_->usesSimd = simdPresent_;
 
         MOZ_ASSERT(asmJSMetadata_->asmJSFuncNames.empty());
-        for (const Func* func : functions_) {
-            CacheableChars funcName = StringToNewUTF8CharsZ(cx_, *func->name());
+        if (!asmJSMetadata_->asmJSFuncNames.resize(funcImportMap_.count()))
+            return nullptr;
+        for (const Func& func : funcDefs_) {
+            CacheableChars funcName = StringToNewUTF8CharsZ(cx_, *func.name());
             if (!funcName || !asmJSMetadata_->asmJSFuncNames.emplaceBack(Move(funcName)))
                 return nullptr;
         }
 
         uint32_t endBeforeCurly = tokenStream().currentToken().pos.end;
         asmJSMetadata_->srcLength = endBeforeCurly - asmJSMetadata_->srcStart;
 
         TokenPos pos;
         JS_ALWAYS_TRUE(tokenStream().peekTokenPos(&pos, TokenStream::Operand));
         uint32_t endAfterCurly = pos.end;
         asmJSMetadata_->srcLengthWithRightBrace = endAfterCurly - asmJSMetadata_->srcStart;
 
+        ScriptedCaller scriptedCaller;
+        if (parser_.ss->filename()) {
+            scriptedCaller.line = scriptedCaller.column = 0;  // unused
+            scriptedCaller.filename = DuplicateString(parser_.ss->filename());
+            if (!scriptedCaller.filename)
+                return nullptr;
+        }
+
+        MutableCompileArgs args = cx_->new_<CompileArgs>();
+        if (!args || !args->initFromContext(cx_, Move(scriptedCaller)))
+            return nullptr;
+
+        uint32_t codeSectionSize = 0;
+        for (const Func& func : funcDefs_)
+            codeSectionSize += func.bytes().length();
+
         // asm.js does not have any wasm bytecode to save; view-source is
         // provided through the ScriptSource.
         SharedBytes bytes = cx_->new_<ShareableBytes>();
         if (!bytes)
             return nullptr;
 
-        return mg_.finishModule(*bytes);
+        ModuleGenerator mg(*args, &env_, nullptr, nullptr);
+        if (!mg.init(codeSectionSize, asmJSMetadata_.get()))
+            return nullptr;
+
+        if (!mg.startFuncDefs())
+            return nullptr;
+
+        for (Func& func : funcDefs_) {
+            if (!mg.compileFuncDef(funcImportMap_.count() + func.funcDefIndex(), func.line(),
+                                   func.bytes().begin(), func.bytes().end(),
+                                   Move(func.callSiteLineNums()))) {
+                return nullptr;
+            }
+        }
+
+        if (!mg.finishFuncDefs())
+            return nullptr;
+
+        return mg.finishModule(*bytes);
     }
 };
 
 /*****************************************************************************/
 // Numeric literal utilities
 
 static bool
 IsNumericNonFloatLiteral(ParseNode* pn)
@@ -2922,23 +3021,23 @@ class MOZ_STACK_CLASS FunctionValidator
     ParseNode* fn() const             { return fn_; }
 
     bool init() {
         return locals_.init() &&
                breakLabels_.init() &&
                continueLabels_.init();
     }
 
-    bool finish(uint32_t funcIndex, unsigned line) {
+    void define(ModuleValidator::Func* func, unsigned line) {
         MOZ_ASSERT(!blockDepth_);
         MOZ_ASSERT(breakableStack_.empty());
         MOZ_ASSERT(continuableStack_.empty());
         MOZ_ASSERT(breakLabels_.empty());
         MOZ_ASSERT(continueLabels_.empty());
-        return m_.mg().compileFuncDef(funcIndex, line, Move(bytes_), Move(callSiteLineNums_));
+        func->define(fn_, line, Move(bytes_), Move(callSiteLineNums_));
     }
 
     bool fail(ParseNode* pn, const char* str) {
         return m_.fail(pn, str);
     }
 
     bool failf(ParseNode* pn, const char* fmt, ...) MOZ_FORMAT_PRINTF(3, 4) {
         va_list ap;
@@ -3975,17 +4074,17 @@ CheckVarRef(FunctionValidator& f, ParseN
             *type = global->varOrConstType();
             return f.encoder().writeOp(Op::GetGlobal) &&
                    f.encoder().writeVarU32(global->varOrConstIndex());
           }
           case ModuleValidator::Global::Function:
           case ModuleValidator::Global::FFI:
           case ModuleValidator::Global::MathBuiltinFunction:
           case ModuleValidator::Global::AtomicsBuiltinFunction:
-          case ModuleValidator::Global::FuncPtrTable:
+          case ModuleValidator::Global::Table:
           case ModuleValidator::Global::ArrayView:
           case ModuleValidator::Global::ArrayViewCtor:
           case ModuleValidator::Global::SimdCtor:
           case ModuleValidator::Global::SimdOp:
             break;
         }
         return f.failName(varRef, "'%s' may not be accessed by ordinary expressions", name);
     }
@@ -4727,24 +4826,26 @@ CheckSignatureAgainstExisting(ModuleVali
     MOZ_ASSERT(sig == existing);
     return true;
 }
 
 static bool
 CheckFunctionSignature(ModuleValidator& m, ParseNode* usepn, Sig&& sig, PropertyName* name,
                        ModuleValidator::Func** func)
 {
-    ModuleValidator::Func* existing = m.lookupFunction(name);
+    ModuleValidator::Func* existing = m.lookupFuncDef(name);
     if (!existing) {
         if (!CheckModuleLevelName(m, usepn, name))
             return false;
-        return m.addFunction(name, usepn->pn_pos.begin, Move(sig), func);
-    }
-
-    if (!CheckSignatureAgainstExisting(m, usepn, sig, m.mg().funcSig(existing->index())))
+        return m.addFuncDef(name, usepn->pn_pos.begin, Move(sig), func);
+    }
+
+    const SigWithId& existingSig = m.env().sigs[existing->sigIndex()];
+
+    if (!CheckSignatureAgainstExisting(m, usepn, sig, existingSig))
         return false;
 
     *func = existing;
     return true;
 }
 
 static bool
 CheckIsArgType(FunctionValidator& f, ParseNode* argNode, Type type)
@@ -4768,49 +4869,49 @@ CheckInternalCall(FunctionValidator& f, 
         return false;
 
     Sig sig(Move(args), ret.canonicalToExprType());
 
     ModuleValidator::Func* callee;
     if (!CheckFunctionSignature(f.m(), callNode, Move(sig), calleeName, &callee))
         return false;
 
-    if (!f.writeCall(callNode, Op::Call))
-        return false;
-
-    if (!f.encoder().writeVarU32(callee->index()))
+    if (!f.writeCall(callNode, MozOp::OldCallDirect))
+        return false;
+
+    if (!f.encoder().writeVarU32(callee->funcDefIndex()))
         return false;
 
     *type = Type::ret(ret);
     return true;
 }
 
 static bool
 CheckFuncPtrTableAgainstExisting(ModuleValidator& m, ParseNode* usepn, PropertyName* name,
-                                 Sig&& sig, unsigned mask, uint32_t* funcPtrTableIndex)
+                                 Sig&& sig, unsigned mask, uint32_t* tableIndex)
 {
     if (const ModuleValidator::Global* existing = m.lookupGlobal(name)) {
-        if (existing->which() != ModuleValidator::Global::FuncPtrTable)
+        if (existing->which() != ModuleValidator::Global::Table)
             return m.failName(usepn, "'%s' is not a function-pointer table", name);
 
-        ModuleValidator::FuncPtrTable& table = m.funcPtrTable(existing->funcPtrTableIndex());
+        ModuleValidator::Table& table = m.table(existing->tableIndex());
         if (mask != table.mask())
             return m.failf(usepn, "mask does not match previous value (%u)", table.mask());
 
-        if (!CheckSignatureAgainstExisting(m, usepn, sig, m.mg().sig(table.sigIndex())))
-            return false;
-
-        *funcPtrTableIndex = existing->funcPtrTableIndex();
+        if (!CheckSignatureAgainstExisting(m, usepn, sig, m.env().sigs[table.sigIndex()]))
+            return false;
+
+        *tableIndex = existing->tableIndex();
         return true;
     }
 
     if (!CheckModuleLevelName(m, usepn, name))
         return false;
 
-    if (!m.declareFuncPtrTable(Move(sig), name, usepn->pn_pos.begin, mask, funcPtrTableIndex))
+    if (!m.declareFuncPtrTable(Move(sig), name, usepn->pn_pos.begin, mask, tableIndex))
         return false;
 
     return true;
 }
 
 static bool
 CheckFuncPtrCall(FunctionValidator& f, ParseNode* callNode, Type ret, Type* type)
 {
@@ -4820,17 +4921,17 @@ CheckFuncPtrCall(FunctionValidator& f, P
     ParseNode* tableNode = ElemBase(callee);
     ParseNode* indexExpr = ElemIndex(callee);
 
     if (!tableNode->isKind(PNK_NAME))
         return f.fail(tableNode, "expecting name of function-pointer array");
 
     PropertyName* name = tableNode->name();
     if (const ModuleValidator::Global* existing = f.lookupGlobal(name)) {
-        if (existing->which() != ModuleValidator::Global::FuncPtrTable)
+        if (existing->which() != ModuleValidator::Global::Table)
             return f.failName(tableNode, "'%s' is not the name of a function-pointer array", name);
     }
 
     if (!indexExpr->isKind(PNK_BITAND))
         return f.fail(indexExpr, "function-pointer table index expression needs & mask");
 
     ParseNode* indexNode = BitwiseLeft(indexExpr);
     ParseNode* maskNode = BitwiseRight(indexExpr);
@@ -4855,17 +4956,17 @@ CheckFuncPtrCall(FunctionValidator& f, P
     uint32_t tableIndex;
     if (!CheckFuncPtrTableAgainstExisting(f.m(), tableNode, name, Move(sig), mask, &tableIndex))
         return false;
 
     if (!f.writeCall(callNode, MozOp::OldCallIndirect))
         return false;
 
     // Call signature
-    if (!f.encoder().writeVarU32(f.m().funcPtrTable(tableIndex).sigIndex()))
+    if (!f.encoder().writeVarU32(f.m().table(tableIndex).sigIndex()))
         return false;
 
     *type = Type::ret(ret);
     return true;
 }
 
 static bool
 CheckIsExternType(FunctionValidator& f, ParseNode* argNode, Type type)
@@ -4888,24 +4989,24 @@ CheckFFICall(FunctionValidator& f, Parse
         return f.fail(callNode, "FFI calls can't return SIMD values");
 
     ValTypeVector args;
     if (!CheckCallArgs<CheckIsExternType>(f, callNode, &args))
         return false;
 
     Sig sig(Move(args), ret.canonicalToExprType());
 
-    uint32_t funcIndex;
-    if (!f.m().declareImport(calleeName, Move(sig), ffiIndex, &funcIndex))
+    uint32_t importIndex;
+    if (!f.m().declareImport(calleeName, Move(sig), ffiIndex, &importIndex))
         return false;
 
     if (!f.writeCall(callNode, Op::Call))
         return false;
 
-    if (!f.encoder().writeVarU32(funcIndex))
+    if (!f.encoder().writeVarU32(importIndex))
         return false;
 
     *type = Type::ret(ret);
     return true;
 }
 
 static bool
 CheckFloatCoercionArg(FunctionValidator& f, ParseNode* inputNode, Type inputType)
@@ -5822,17 +5923,17 @@ CheckCoercedCall(FunctionValidator& f, P
             return CheckFFICall(f, call, global->ffiIndex(), ret, type);
           case ModuleValidator::Global::MathBuiltinFunction:
             return CheckCoercedMathBuiltinCall(f, call, global->mathBuiltinFunction(), ret, type);
           case ModuleValidator::Global::AtomicsBuiltinFunction:
             return CheckCoercedAtomicsBuiltinCall(f, call, global->atomicsBuiltinFunction(), ret, type);
           case ModuleValidator::Global::ConstantLiteral:
           case ModuleValidator::Global::ConstantImport:
           case ModuleValidator::Global::Variable:
-          case ModuleValidator::Global::FuncPtrTable:
+          case ModuleValidator::Global::Table:
           case ModuleValidator::Global::ArrayView:
           case ModuleValidator::Global::ArrayViewCtor:
             return f.failName(callee, "'%s' is not callable function", callee->name());
           case ModuleValidator::Global::SimdCtor:
           case ModuleValidator::Global::SimdOp:
             return CheckCoercedSimdCall(f, call, global, ret, type);
           case ModuleValidator::Global::Function:
             break;
@@ -6432,24 +6533,20 @@ CheckLoopConditionOnEntry(FunctionValida
         return true;
 
     Type condType;
     if (!CheckExpr(f, cond, &condType))
         return false;
     if (!condType.isInt())
         return f.failf(cond, "%s is not a subtype of int", condType.toChars());
 
-    // TODO change this to i32.eqz
-    // i32.eq 0 $f
-    if (!f.writeInt32Lit(0))
-        return false;
-    if (!f.encoder().writeOp(Op::I32Eq))
-        return false;
-
-    // brIf (i32.eq 0 $f) $out
+    if (!f.encoder().writeOp(Op::I32Eqz))
+        return false;
+
+    // brIf (i32.eqz $f) $out
     if (!f.writeBreakIf())
         return false;
 
     return true;
 }
 
 static bool
 CheckWhile(FunctionValidator& f, ParseNode* whileStmt, const NameVector* labels = nullptr)
@@ -7121,31 +7218,28 @@ CheckFunction(ModuleValidator& m)
 
     ModuleValidator::Func* func = nullptr;
     if (!CheckFunctionSignature(m, fn, Sig(Move(args), f.returnedType()), FunctionName(fn), &func))
         return false;
 
     if (func->defined())
         return m.failName(fn, "function '%s' already defined", FunctionName(fn));
 
-    func->define(fn);
-
-    if (!f.finish(func->index(), line))
-        return m.fail(fn, "internal compiler failure (probably out of memory)");
+    f.define(func, line);
 
     // Release the parser's lifo memory only after the last use of a parse node.
     m.parser().release(mark);
     return true;
 }
 
 static bool
 CheckAllFunctionsDefined(ModuleValidator& m)
 {
-    for (unsigned i = 0; i < m.numFunctions(); i++) {
-        ModuleValidator::Func& f = m.function(i);
+    for (unsigned i = 0; i < m.numFuncDefs(); i++) {
+        const ModuleValidator::Func& f = m.funcDef(i);
         if (!f.defined())
             return m.failNameOffset(f.firstUse(), "missing definition of function %s", f.name());
     }
 
     return true;
 }
 
 static bool
@@ -7178,48 +7272,48 @@ CheckFuncPtrTable(ModuleValidator& m, Pa
 
     unsigned length = ListLength(arrayLiteral);
 
     if (!IsPowerOfTwo(length))
         return m.failf(arrayLiteral, "function-pointer table length must be a power of 2 (is %u)", length);
 
     unsigned mask = length - 1;
 
-    Uint32Vector elemFuncIndices;
+    Uint32Vector elemFuncDefIndices;
     const Sig* sig = nullptr;
     for (ParseNode* elem = ListHead(arrayLiteral); elem; elem = NextNode(elem)) {
         if (!elem->isKind(PNK_NAME))
             return m.fail(elem, "function-pointer table's elements must be names of functions");
 
         PropertyName* funcName = elem->name();
-        const ModuleValidator::Func* func = m.lookupFunction(funcName);
+        const ModuleValidator::Func* func = m.lookupFuncDef(funcName);
         if (!func)
             return m.fail(elem, "function-pointer table's elements must be names of functions");
 
-        const Sig& funcSig = m.mg().funcSig(func->index());
+        const Sig& funcSig = m.env().sigs[func->sigIndex()];
         if (sig) {
             if (*sig != funcSig)
                 return m.fail(elem, "all functions in table must have same signature");
         } else {
             sig = &funcSig;
         }
 
-        if (!elemFuncIndices.append(func->index()))
+        if (!elemFuncDefIndices.append(func->funcDefIndex()))
             return false;
     }
 
     Sig copy;
     if (!copy.clone(*sig))
         return false;
 
     uint32_t tableIndex;
     if (!CheckFuncPtrTableAgainstExisting(m, var, var->name(), Move(copy), mask, &tableIndex))
         return false;
 
-    if (!m.defineFuncPtrTable(tableIndex, Move(elemFuncIndices)))
+    if (!m.defineFuncPtrTable(tableIndex, Move(elemFuncDefIndices)))
         return m.fail(var, "duplicate function-pointer definition");
 
     return true;
 }
 
 static bool
 CheckFuncPtrTables(ModuleValidator& m)
 {
@@ -7231,35 +7325,35 @@ CheckFuncPtrTables(ModuleValidator& m)
             break;
         for (ParseNode* var = VarListHead(varStmt); var; var = NextNode(var)) {
             if (!CheckFuncPtrTable(m, var))
                 return false;
         }
     }
 
     for (unsigned i = 0; i < m.numFuncPtrTables(); i++) {
-        ModuleValidator::FuncPtrTable& funcPtrTable = m.funcPtrTable(i);
-        if (!funcPtrTable.defined()) {
-            return m.failNameOffset(funcPtrTable.firstUse(),
+        ModuleValidator::Table& table = m.table(i);
+        if (!table.defined()) {
+            return m.failNameOffset(table.firstUse(),
                                     "function-pointer table %s wasn't defined",
-                                    funcPtrTable.name());
+                                    table.name());
         }
     }
 
     return true;
 }
 
 static bool
 CheckModuleExportFunction(ModuleValidator& m, ParseNode* pn, PropertyName* maybeFieldName = nullptr)
 {
     if (!pn->isKind(PNK_NAME))
         return m.fail(pn, "expected name of exported function");
 
     PropertyName* funcName = pn->name();
-    const ModuleValidator::Func* func = m.lookupFunction(funcName);
+    const ModuleValidator::Func* func = m.lookupFuncDef(funcName);
     if (!func)
         return m.failName(pn, "function '%s' not found", funcName);
 
     return m.addExportField(pn, *func, maybeFieldName);
 }
 
 static bool
 CheckModuleExportObject(ModuleValidator& m, ParseNode* object)
@@ -7333,29 +7427,17 @@ CheckModuleEnd(ModuleValidator &m)
 static SharedModule
 CheckModule(JSContext* cx, AsmJSParser& parser, ParseNode* stmtList, unsigned* time)
 {
     int64_t before = PRMJ_Now();
 
     ParseNode* moduleFunctionNode = parser.pc->functionBox()->functionNode;
     MOZ_ASSERT(moduleFunctionNode);
 
-    ScriptedCaller scriptedCaller;
-    if (parser.ss->filename()) {
-        scriptedCaller.line = scriptedCaller.column = 0;  // unused
-        scriptedCaller.filename = DuplicateString(parser.ss->filename());
-        if (!scriptedCaller.filename)
-            return nullptr;
-    }
-
-    MutableCompileArgs args = cx->new_<CompileArgs>();
-    if (!args || !args->initFromContext(cx, Move(scriptedCaller)))
-        return nullptr;
-
-    ModuleValidator m(cx, *args, parser, moduleFunctionNode);
+    ModuleValidator m(cx, parser, moduleFunctionNode);
     if (!m.init())
         return nullptr;
 
     if (!CheckFunctionHead(m, moduleFunctionNode))
         return nullptr;
 
     if (!CheckModuleArguments(m, moduleFunctionNode))
         return nullptr;
@@ -7370,19 +7452,16 @@ CheckModule(JSContext* cx, AsmJSParser& 
         return nullptr;
 
     if (!m.startFunctionBodies())
         return nullptr;
 
     if (!CheckFunctions(m))
         return nullptr;
 
-    if (!m.finishFunctionBodies())
-        return nullptr;
-
     if (!CheckFuncPtrTables(m))
         return nullptr;
 
     if (!CheckModuleReturn(m))
         return nullptr;
 
     if (!CheckModuleEnd(m))
         return nullptr;
--- a/js/src/wasm/WasmBinaryConstants.h
+++ b/js/src/wasm/WasmBinaryConstants.h
@@ -317,17 +317,18 @@ enum class Op
 
     AtomicPrefix                         = 0xfe,
     MozPrefix                            = 0xff,
 
     Limit                                = 0x100
 };
 
 inline bool
-IsPrefixByte(uint8_t b) {
+IsPrefixByte(uint8_t b)
+{
     return b >= uint8_t(Op::AtomicPrefix);
 }
 
 enum class MozOp
 {
     // ------------------------------------------------------------------------
     // These operators are emitted internally when compiling asm.js and are
     // rejected by wasm validation.  They are prefixed by MozPrefix.
@@ -359,16 +360,17 @@ enum class MozOp
     F64Acos,
     F64Atan,
     F64Exp,
     F64Log,
     F64Pow,
     F64Atan2,
 
     // asm.js-style call_indirect with the callee evaluated first.
+    OldCallDirect,
     OldCallIndirect,
 
     // Atomics
     I32AtomicsCompareExchange,
     I32AtomicsExchange,
     I32AtomicsLoad,
     I32AtomicsStore,
     I32AtomicsBinOp,
@@ -499,28 +501,12 @@ static const unsigned MaxStringBytes    
 static const unsigned MaxLocals              =    50000;
 static const unsigned MaxParams              =     1000;
 static const unsigned MaxBrTableElems        =  1000000;
 static const unsigned MaxMemoryInitialPages  = 16384;
 static const unsigned MaxMemoryMaximumPages  = 65536;
 static const unsigned MaxModuleBytes         = 1024 * 1024 * 1024;
 static const unsigned MaxFunctionBytes       =         128 * 1024;
 
-// To be able to assign function indices during compilation while the number of
-// imports is still unknown, asm.js sets a maximum number of imports so it can
-// immediately start handing out function indices starting at the maximum + 1.
-// this means that there is a "hole" between the last import and the first
-// definition, but that's fine.
-
-static const unsigned AsmJSMaxTypes          =   4 * 1024;
-static const unsigned AsmJSMaxFuncs          = 512 * 1024;
-static const unsigned AsmJSMaxImports        =   4 * 1024;
-static const unsigned AsmJSMaxTables         =   4 * 1024;
-static const unsigned AsmJSFirstDefFuncIndex = AsmJSMaxImports + 1;
-
-static_assert(AsmJSMaxTypes <= MaxTypes, "conservative");
-static_assert(AsmJSMaxImports <= MaxImports, "conservative");
-static_assert(AsmJSFirstDefFuncIndex < MaxFuncs, "conservative");
-
 } // namespace wasm
 } // namespace js
 
 #endif // wasm_binary_h
--- a/js/src/wasm/WasmBinaryIterator.cpp
+++ b/js/src/wasm/WasmBinaryIterator.cpp
@@ -385,16 +385,18 @@ wasm::Classify(OpBytes op)
             case MozOp::I32x4store1:
             case MozOp::I32x4store2:
             case MozOp::I32x4store3:
             case MozOp::F32x4store:
             case MozOp::F32x4store1:
             case MozOp::F32x4store2:
             case MozOp::F32x4store3:
               return OpKind::TeeStore;
+            case MozOp::OldCallDirect:
+              return OpKind::OldCallDirect;
             case MozOp::OldCallIndirect:
               return OpKind::OldCallIndirect;
             case MozOp::I32AtomicsLoad:
               return OpKind::AtomicLoad;
             case MozOp::I32AtomicsStore:
               return OpKind::AtomicStore;
             case MozOp::I32AtomicsBinOp:
               return OpKind::AtomicBinOp;
--- a/js/src/wasm/WasmBinaryIterator.h
+++ b/js/src/wasm/WasmBinaryIterator.h
@@ -128,16 +128,17 @@ enum class OpKind {
     GetLocal,
     SetLocal,
     TeeLocal,
     GetGlobal,
     SetGlobal,
     TeeGlobal,
     Call,
     CallIndirect,
+    OldCallDirect,
     OldCallIndirect,
     Return,
     If,
     Else,
     End,
     AtomicLoad,
     AtomicStore,
     AtomicBinOp,
@@ -524,16 +525,18 @@ class MOZ_STACK_CLASS OpIter : private P
     MOZ_MUST_USE bool readI16x8Const(I16x8* i16x8);
     MOZ_MUST_USE bool readI32x4Const(I32x4* i32x4);
     MOZ_MUST_USE bool readF32x4Const(F32x4* f32x4);
     MOZ_MUST_USE bool readB8x16Const(I8x16* i8x16);
     MOZ_MUST_USE bool readB16x8Const(I16x8* i16x8);
     MOZ_MUST_USE bool readB32x4Const(I32x4* i32x4);
     MOZ_MUST_USE bool readCall(uint32_t* calleeIndex, ValueVector* argValues);
     MOZ_MUST_USE bool readCallIndirect(uint32_t* sigIndex, Value* callee, ValueVector* argValues);
+    MOZ_MUST_USE bool readOldCallDirect(uint32_t numFuncImports, uint32_t* funcIndex,
+                                        ValueVector* argValues);
     MOZ_MUST_USE bool readOldCallIndirect(uint32_t* sigIndex, Value* callee, ValueVector* argValues);
     MOZ_MUST_USE bool readAtomicLoad(LinearMemoryAddress<Value>* addr,
                                      Scalar::Type* viewType);
     MOZ_MUST_USE bool readAtomicStore(LinearMemoryAddress<Value>* addr,
                                       Scalar::Type* viewType,
                                       Value* value);
     MOZ_MUST_USE bool readAtomicBinOp(LinearMemoryAddress<Value>* addr,
                                       Scalar::Type* viewType,
@@ -1619,16 +1622,43 @@ OpIter<Policy>::readCallIndirect(uint32_
     if (!popCallArgs(sig.args(), argValues))
         return false;
 
     return push(sig.ret());
 }
 
 template <typename Policy>
 inline bool
+OpIter<Policy>::readOldCallDirect(uint32_t numFuncImports, uint32_t* funcIndex,
+                                  ValueVector* argValues)
+{
+    MOZ_ASSERT(Classify(op_) == OpKind::OldCallDirect);
+
+    uint32_t funcDefIndex;
+    if (!readVarU32(&funcDefIndex))
+        return fail("unable to read call function index");
+
+    if (UINT32_MAX - funcDefIndex < numFuncImports)
+        return fail("callee index out of range");
+
+    *funcIndex = numFuncImports + funcDefIndex;
+
+    if (*funcIndex >= env_.funcSigs.length())
+        return fail("callee index out of range");
+
+    const Sig& sig = *env_.funcSigs[*funcIndex];
+
+    if (!popCallArgs(sig.args(), argValues))
+        return false;
+
+    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);
 
     if (!readVarU32(sigIndex))
         return fail("unable to read call_indirect signature index");
 
     if (*sigIndex >= env_.numSigs())
--- a/js/src/wasm/WasmCode.h
+++ b/js/src/wasm/WasmCode.h
@@ -354,16 +354,17 @@ struct MetadataTier
 
     WASM_DECLARE_SERIALIZABLE(MetadataTier);
 };
 
 typedef UniquePtr<MetadataTier> UniqueMetadataTier;
 
 class Metadata : public ShareableBase<Metadata>, public MetadataCacheablePod
 {
+  protected:
     UniqueMetadataTier         metadata1_;
     mutable UniqueMetadataTier metadata2_;  // Access only when hasTier2() is true
     mutable Atomic<bool>       hasTier2_;
 
   public:
     explicit Metadata(UniqueMetadataTier tier, ModuleKind kind = ModuleKind::Wasm)
       : MetadataCacheablePod(kind),
         metadata1_(Move(tier)),
--- a/js/src/wasm/WasmGenerator.cpp
+++ b/js/src/wasm/WasmGenerator.cpp
@@ -67,19 +67,16 @@ ModuleGenerator::ModuleGenerator(const C
                                  Atomic<bool>* cancelled, UniqueChars* error)
   : compileArgs_(&args),
     error_(error),
     cancelled_(cancelled),
     env_(env),
     linkDataTier_(nullptr),
     metadataTier_(nullptr),
     taskState_(mutexid::WasmCompileTaskState),
-    numFuncDefs_(0),
-    numSigs_(0),
-    numTables_(0),
     lifo_(GENERATOR_LIFO_DEFAULT_CHUNK_SIZE),
     masmAlloc_(&lifo_),
     masm_(MacroAssembler::WasmToken(), masmAlloc_),
     trapCodeOffsets_(),
     debugTrapCodeOffset_(),
     lastPatchedCallSite_(0),
     startOfUnpatchedCallsites_(0),
     parallel_(false),
@@ -98,19 +95,19 @@ ModuleGenerator::~ModuleGenerator()
     MOZ_ASSERT_IF(finishedFuncDefs_, !batchedBytecode_);
     MOZ_ASSERT_IF(finishedFuncDefs_, !currentTask_);
 
     if (parallel_) {
         if (outstanding_) {
             // Remove any pending compilation tasks from the worklist.
             {
                 AutoLockHelperThreadState lock;
-                CompileTaskPtrVector& worklist = HelperThreadState().wasmWorklist(lock, mode());
+                CompileTaskPtrFifo& worklist = HelperThreadState().wasmWorklist(lock, mode());
                 auto pred = [this](CompileTask* task) { return &task->state == &taskState_; };
-                size_t removed = EraseIf(worklist, pred);
+                size_t removed = worklist.eraseIf(pred);
                 MOZ_ASSERT(outstanding_ >= removed);
                 outstanding_ -= removed;
             }
 
             // Wait until all active compilation tasks have finished.
             {
                 auto taskState = taskState_.lock();
                 while (true) {
@@ -134,170 +131,189 @@ ModuleGenerator::~ModuleGenerator()
     }
 
     // Propagate error state.
     if (error_ && !*error_)
         *error_ = Move(taskState_.lock()->errorMessage);
 }
 
 bool
-ModuleGenerator::initAsmJS(Metadata* asmJSMetadata)
+ModuleGenerator::allocateGlobalBytes(uint32_t bytes, uint32_t align, uint32_t* globalDataOffset)
 {
-    MOZ_ASSERT(env_->isAsmJS());
+    MOZ_ASSERT(!startedFuncDefs_);
 
-    if (!linkData_.initTier1(Tier::Ion, *asmJSMetadata))
-        return false;
-    linkDataTier_ = &linkData_.linkData(Tier::Ion);
+    CheckedInt<uint32_t> newGlobalDataLength(metadata_->globalDataLength);
 
-    metadataTier_ = &asmJSMetadata->metadata(Tier::Ion);
-    metadata_ = asmJSMetadata;
-    MOZ_ASSERT(isAsmJS());
+    newGlobalDataLength += ComputeByteAlignment(newGlobalDataLength.value(), align);
+    if (!newGlobalDataLength.isValid())
+        return false;
 
-    // For asm.js, the Vectors in ModuleEnvironment are max-sized reservations
-    // and will be initialized in a linear order via init* functions as the
-    // module is generated.
+    *globalDataOffset = newGlobalDataLength.value();
+    newGlobalDataLength += bytes;
 
-    MOZ_ASSERT(env_->sigs.length() == AsmJSMaxTypes);
-    MOZ_ASSERT(env_->tables.length() == AsmJSMaxTables);
-    MOZ_ASSERT(env_->asmJSSigToTableIndex.length() == AsmJSMaxTypes);
+    if (!newGlobalDataLength.isValid())
+        return false;
 
+    metadata_->globalDataLength = newGlobalDataLength.value();
     return true;
 }
 
 bool
-ModuleGenerator::initWasm(size_t codeSectionSize)
+ModuleGenerator::init(size_t codeSectionSize, Metadata* maybeAsmJSMetadata)
 {
-    MOZ_ASSERT(!env_->isAsmJS());
+    // Perform fallible metadata, linkdata, assumption allocations.
 
-    auto metadataTier = js::MakeUnique<MetadataTier>(tier());
-    if (!metadataTier)
-        return false;
+    if (maybeAsmJSMetadata) {
+        MOZ_ASSERT(isAsmJS());
+        metadataTier_ = &maybeAsmJSMetadata->metadata(tier());
+        metadata_ = maybeAsmJSMetadata;
+    } else {
+        MOZ_ASSERT(!isAsmJS());
+        auto metadataTier = js::MakeUnique<MetadataTier>(tier());
+        if (!metadataTier)
+            return false;
+        metadataTier_ = metadataTier.get();
+        metadata_ = js_new<Metadata>(Move(metadataTier));
+        if (!metadata_)
+            return false;
+    }
 
-    metadata_ = js_new<Metadata>(Move(metadataTier));
-    if (!metadata_)
-        return false;
-
-    metadataTier_ = &metadata_->metadata(tier());
+    if (compileArgs_->scriptedCaller.filename) {
+        metadata_->filename = DuplicateString(compileArgs_->scriptedCaller.filename.get());
+        if (!metadata_->filename)
+            return false;
+    }
 
     if (!linkData_.initTier1(tier(), *metadata_))
         return false;
+
     linkDataTier_ = &linkData_.linkData(tier());
 
-    MOZ_ASSERT(!isAsmJS());
+    if (!assumptions_.clone(compileArgs_->assumptions))
+        return false;
 
-    // For wasm, the amount of code, functions, signatures, imports, exports,
-    // etc are known a priori.
+    // The funcToCodeRange_ maps function indices to code-range indices and all
+    // elements will be initialized by the time module generation is finished.
 
-    numSigs_ = env_->sigs.length();
-    numTables_ = env_->tables.length();
+    if (!funcToCodeRange_.appendN(BAD_CODE_RANGE, env_->funcSigs.length()))
+        return false;
 
-    // When estimating the MacroAssembler buffer size, be extra conservative
-    // since the price is low and the cost of an extra resize is high.
+    // Pre-reserve space for large Vectors to avoid the significant cost of the
+    // final reallocs. In particular, the MacroAssembler can be enormous, so be
+    // extra conservative. Note, podResizeToFit calls at the end will trim off
+    // unneeded capacity.
 
     if (!masm_.reserve(size_t(1.2 * EstimateCompiledCodeSize(tier(), codeSectionSize))))
         return false;
 
-    // Although we could compute it more precisely (only the number of far jumps
-    // is unknown), 2x number of functions is a good conservative estimate and
-    // podResizeToFit will remove waste at the end.
-
     if (!metadataTier_->codeRanges.reserve(2 * env_->numFuncDefs()))
         return false;
 
-    // Code can vary a lot, so use a conservative estimate of 1 load/store/call/trap
-    // per 10 bytes of bytecode and rely on podResizeToFit() to remove waste.
-
     const size_t CallSitesPerByteCode = 10;
     if (!metadataTier_->callSites.reserve(codeSectionSize / CallSitesPerByteCode))
         return false;
 
     const size_t MemoryAccessesPerByteCode = 10;
     if (!metadataTier_->memoryAccesses.reserve(codeSectionSize / MemoryAccessesPerByteCode))
         return false;
 
     // Allocate space in TlsData for declarations that need it.
 
+    MOZ_ASSERT(metadata_->globalDataLength == 0);
+
     for (size_t i = 0; i < env_->funcImportGlobalDataOffsets.length(); i++) {
-        env_->funcImportGlobalDataOffsets[i] = metadata_->globalDataLength;
-        metadata_->globalDataLength += sizeof(FuncImportTls);
-        if (!addFuncImport(*env_->funcSigs[i], env_->funcImportGlobalDataOffsets[i]))
+        uint32_t globalDataOffset;
+        if (!allocateGlobalBytes(sizeof(FuncImportTls), sizeof(void*), &globalDataOffset))
+            return false;
+
+        env_->funcImportGlobalDataOffsets[i] = globalDataOffset;
+
+        Sig copy;
+        if (!copy.clone(*env_->funcSigs[i]))
+            return false;
+        if (!metadataTier_->funcImports.emplaceBack(Move(copy), globalDataOffset))
             return false;
     }
 
     for (TableDesc& table : env_->tables) {
         if (!allocateGlobalBytes(sizeof(TableTls), sizeof(void*), &table.globalDataOffset))
             return false;
     }
 
-    for (uint32_t i = 0; i < numSigs_; i++) {
-        SigWithId& sig = env_->sigs[i];
-        if (SigIdDesc::isGlobal(sig)) {
-            uint32_t globalDataOffset;
-            if (!allocateGlobalBytes(sizeof(void*), sizeof(void*), &globalDataOffset))
-                return false;
+    if (!isAsmJS()) {
+        for (SigWithId& sig : env_->sigs) {
+            if (SigIdDesc::isGlobal(sig)) {
+                uint32_t globalDataOffset;
+                if (!allocateGlobalBytes(sizeof(void*), sizeof(void*), &globalDataOffset))
+                    return false;
 
-            sig.id = SigIdDesc::global(sig, globalDataOffset);
+                sig.id = SigIdDesc::global(sig, globalDataOffset);
 
-            Sig copy;
-            if (!copy.clone(sig))
-                return false;
+                Sig copy;
+                if (!copy.clone(sig))
+                    return false;
 
-            if (!metadata_->sigIds.emplaceBack(Move(copy), sig.id))
-                return false;
-        } else {
-            sig.id = SigIdDesc::immediate(sig);
+                if (!metadata_->sigIds.emplaceBack(Move(copy), sig.id))
+                    return false;
+            } else {
+                sig.id = SigIdDesc::immediate(sig);
+            }
         }
     }
 
     for (GlobalDesc& global : env_->globals) {
         if (global.isConstant())
             continue;
-        if (!allocateGlobal(&global))
+
+        uint32_t width = SizeOf(global.type());
+
+        uint32_t globalDataOffset;
+        if (!allocateGlobalBytes(width, width, &globalDataOffset))
             return false;
+
+        global.setOffset(globalDataOffset);
     }
 
-    // Build a HashSet of all exported functions, whether by explicit export of
-    // the function, or implicitly by being an element of an external (imported
-    // or exported) table, or being the start function.
+    // Accumulate all exported functions, whether by explicit export or
+    // implicitly by being an element of an external (imported or exported)
+    // table or by being the start function. The FuncExportVector stored in
+    // Metadata needs to be sorted (to allow O(log(n)) lookup at runtime) and
+    // deduplicated, so use an intermediate vector to sort and de-duplicate.
+
+    Uint32Vector exportedFuncs;
 
     for (const Export& exp : env_->exports) {
         if (exp.kind() == DefinitionKind::Function) {
-            if (!exportedFuncs_.put(exp.funcIndex()))
+            if (!exportedFuncs.append(exp.funcIndex()))
                 return false;
         }
     }
 
-    if (env_->startFuncIndex) {
-        metadata_->startFuncIndex.emplace(*env_->startFuncIndex);
-        if (!exportedFuncs_.put(*env_->startFuncIndex))
-            return false;
+    for (ElemSegment& elems : env_->elemSegments) {
+        if (env_->tables[elems.tableIndex].external) {
+            if (!exportedFuncs.appendAll(elems.elemFuncIndices))
+                return false;
+        }
     }
 
-    return true;
-}
-
-bool
-ModuleGenerator::init(size_t codeSectionSize, Metadata* maybeAsmJSMetadata)
-{
-    if (!funcToCodeRange_.appendN(BAD_CODE_RANGE, env_->funcSigs.length()))
+    if (env_->startFuncIndex && !exportedFuncs.append(*env_->startFuncIndex))
         return false;
 
-    if (!assumptions_.clone(compileArgs_->assumptions))
-        return false;
+    std::sort(exportedFuncs.begin(), exportedFuncs.end());
+    auto* newEnd = std::unique(exportedFuncs.begin(), exportedFuncs.end());
+    exportedFuncs.erase(newEnd, exportedFuncs.end());
 
-    if (!exportedFuncs_.init())
+    if (!metadataTier_->funcExports.reserve(exportedFuncs.length()))
         return false;
 
-    if (env_->isAsmJS() ? !initAsmJS(maybeAsmJSMetadata) : !initWasm(codeSectionSize))
-        return false;
-
-    if (compileArgs_->scriptedCaller.filename) {
-        metadata_->filename = DuplicateString(compileArgs_->scriptedCaller.filename.get());
-        if (!metadata_->filename)
+    for (uint32_t funcIndex : exportedFuncs) {
+        Sig sig;
+        if (!sig.clone(*env_->funcSigs[funcIndex]))
             return false;
+        metadataTier_->funcExports.infallibleEmplaceBack(Move(sig), funcIndex);
     }
 
     return true;
 }
 
 bool
 ModuleGenerator::funcIsCompiled(uint32_t funcIndex) const
 {
@@ -554,241 +570,16 @@ ModuleGenerator::linkCompiledCode(const 
         if (!linkDataTier_->internalLinks.append(link))
             return false;
     }
 
     return true;
 }
 
 bool
-ModuleGenerator::finishTask(CompileTask* task)
-{
-    masm_.haltingAlign(CodeAlignment);
-
-    // Before merging in the new function's code, if calls in a prior code range
-    // might go out of range, insert far jumps to extend the range.
-    if (!InRange(startOfUnpatchedCallsites_, masm_.size() + task->output.bytes.length())) {
-        startOfUnpatchedCallsites_ = masm_.size();
-        if (!linkCallSites())
-            return false;
-    }
-
-    if (!linkCompiledCode(task->output))
-        return false;
-
-    task->output.clear();
-
-    MOZ_ASSERT(task->inputs.empty());
-    MOZ_ASSERT(task->output.empty());
-    MOZ_ASSERT(task->lifo.isEmpty());
-    freeTasks_.infallibleAppend(task);
-    return true;
-}
-
-bool
-ModuleGenerator::finishFuncExports()
-{
-    // In addition to all the functions that were explicitly exported, any
-    // element of an exported table is also exported.
-
-    for (ElemSegment& elems : env_->elemSegments) {
-        if (env_->tables[elems.tableIndex].external) {
-            for (uint32_t funcIndex : elems.elemFuncIndices) {
-                if (!exportedFuncs_.put(funcIndex))
-                    return false;
-            }
-        }
-    }
-
-    // ModuleGenerator::exportedFuncs_ is an unordered HashSet. The
-    // FuncExportVector stored in Metadata needs to be stored sorted by
-    // function index to allow O(log(n)) lookup at runtime.
-
-    Uint32Vector sorted;
-    if (!sorted.reserve(exportedFuncs_.count()))
-        return false;
-
-    for (Uint32Set::Range r = exportedFuncs_.all(); !r.empty(); r.popFront())
-        sorted.infallibleAppend(r.front());
-
-    std::sort(sorted.begin(), sorted.end());
-
-    MOZ_ASSERT(metadataTier_->funcExports.empty());
-    if (!metadataTier_->funcExports.reserve(sorted.length()))
-        return false;
-
-    for (uint32_t funcIndex : sorted) {
-        Sig sig;
-        if (!sig.clone(funcSig(funcIndex)))
-            return false;
-
-        metadataTier_->funcExports.infallibleEmplaceBack(Move(sig), funcIndex);
-    }
-
-    return true;
-}
-
-bool
-ModuleGenerator::addFuncImport(const Sig& sig, uint32_t globalDataOffset)
-{
-    MOZ_ASSERT(!finishedFuncDefs_);
-
-    Sig copy;
-    if (!copy.clone(sig))
-        return false;
-
-    return metadataTier_->funcImports.emplaceBack(Move(copy), globalDataOffset);
-}
-
-bool
-ModuleGenerator::allocateGlobalBytes(uint32_t bytes, uint32_t align, uint32_t* globalDataOffset)
-{
-    CheckedInt<uint32_t> newGlobalDataLength(metadata_->globalDataLength);
-
-    newGlobalDataLength += ComputeByteAlignment(newGlobalDataLength.value(), align);
-    if (!newGlobalDataLength.isValid())
-        return false;
-
-    *globalDataOffset = newGlobalDataLength.value();
-    newGlobalDataLength += bytes;
-
-    if (!newGlobalDataLength.isValid())
-        return false;
-
-    metadata_->globalDataLength = newGlobalDataLength.value();
-    return true;
-}
-
-bool
-ModuleGenerator::allocateGlobal(GlobalDesc* global)
-{
-    MOZ_ASSERT(!startedFuncDefs_);
-    unsigned width = 0;
-    switch (global->type()) {
-      case ValType::I32:
-      case ValType::F32:
-        width = 4;
-        break;
-      case ValType::I64:
-      case ValType::F64:
-        width = 8;
-        break;
-      case ValType::I8x16:
-      case ValType::I16x8:
-      case ValType::I32x4:
-      case ValType::F32x4:
-      case ValType::B8x16:
-      case ValType::B16x8:
-      case ValType::B32x4:
-        width = 16;
-        break;
-    }
-
-    uint32_t offset;
-    if (!allocateGlobalBytes(width, width, &offset))
-        return false;
-
-    global->setOffset(offset);
-    return true;
-}
-
-bool
-ModuleGenerator::addGlobal(ValType type, bool isConst, uint32_t* index)
-{
-    MOZ_ASSERT(isAsmJS());
-    MOZ_ASSERT(!startedFuncDefs_);
-
-    *index = env_->globals.length();
-    GlobalDesc global(type, !isConst, *index);
-    if (!allocateGlobal(&global))
-        return false;
-
-    return env_->globals.append(global);
-}
-
-bool
-ModuleGenerator::addExport(CacheableChars&& fieldName, uint32_t funcIndex)
-{
-    MOZ_ASSERT(isAsmJS());
-    return env_->exports.emplaceBack(Move(fieldName), funcIndex, DefinitionKind::Function) &&
-           exportedFuncs_.put(funcIndex);
-}
-
-void
-ModuleGenerator::initSig(uint32_t sigIndex, Sig&& sig)
-{
-    MOZ_ASSERT(isAsmJS());
-    MOZ_ASSERT(sigIndex == numSigs_);
-    numSigs_++;
-
-    MOZ_ASSERT(env_->sigs[sigIndex] == Sig());
-    env_->sigs[sigIndex] = Move(sig);
-}
-
-const SigWithId&
-ModuleGenerator::sig(uint32_t index) const
-{
-    MOZ_ASSERT(index < numSigs_);
-    return env_->sigs[index];
-}
-
-void
-ModuleGenerator::initFuncSig(uint32_t funcIndex, uint32_t sigIndex)
-{
-    MOZ_ASSERT(isAsmJS());
-    MOZ_ASSERT(!env_->funcSigs[funcIndex]);
-
-    env_->funcSigs[funcIndex] = &env_->sigs[sigIndex];
-}
-
-void
-ModuleGenerator::initMemoryUsage(MemoryUsage memoryUsage)
-{
-    MOZ_ASSERT(isAsmJS());
-    MOZ_ASSERT(env_->memoryUsage == MemoryUsage::None);
-
-    env_->memoryUsage = memoryUsage;
-}
-
-void
-ModuleGenerator::bumpMinMemoryLength(uint32_t newMinMemoryLength)
-{
-    MOZ_ASSERT(isAsmJS());
-    MOZ_ASSERT(newMinMemoryLength >= env_->minMemoryLength);
-
-    env_->minMemoryLength = newMinMemoryLength;
-}
-
-bool
-ModuleGenerator::initImport(uint32_t funcIndex, uint32_t sigIndex)
-{
-    MOZ_ASSERT(isAsmJS());
-
-    MOZ_ASSERT(!env_->funcSigs[funcIndex]);
-    env_->funcSigs[funcIndex] = &env_->sigs[sigIndex];
-
-    uint32_t globalDataOffset;
-    if (!allocateGlobalBytes(sizeof(FuncImportTls), sizeof(void*), &globalDataOffset))
-        return false;
-
-    MOZ_ASSERT(!env_->funcImportGlobalDataOffsets[funcIndex]);
-    env_->funcImportGlobalDataOffsets[funcIndex] = globalDataOffset;
-
-    MOZ_ASSERT(funcIndex == metadataTier_->funcImports.length());
-    return addFuncImport(sig(sigIndex), globalDataOffset);
-}
-
-const SigWithId&
-ModuleGenerator::funcSig(uint32_t funcIndex) const
-{
-    MOZ_ASSERT(env_->funcSigs[funcIndex]);
-    return *env_->funcSigs[funcIndex];
-}
-
-bool
 ModuleGenerator::startFuncDefs()
 {
     MOZ_ASSERT(!startedFuncDefs_);
     MOZ_ASSERT(!finishedFuncDefs_);
 
     GlobalHelperThreadState& threads = HelperThreadState();
     MOZ_ASSERT(threads.threadCount > 1);
 
@@ -805,16 +596,31 @@ ModuleGenerator::startFuncDefs()
     for (size_t i = 0; i < numTasks; i++)
         tasks_.infallibleEmplaceBack(*env_, taskState_, COMPILATION_LIFO_DEFAULT_CHUNK_SIZE);
 
     if (!freeTasks_.reserve(numTasks))
         return false;
     for (size_t i = 0; i < numTasks; i++)
         freeTasks_.infallibleAppend(&tasks_[i]);
 
+    // Fill in function stubs for each import so that imported functions can be
+    // used in all the places that normal function definitions can (table
+    // elements, export calls, etc).
+
+    CompiledCode& importCode = tasks_[0].output;
+    MOZ_ASSERT(importCode.empty());
+
+    if (!GenerateImportFunctions(*env_, metadataTier_->funcImports, &importCode))
+        return false;
+
+    if (!linkCompiledCode(importCode))
+        return false;
+
+    importCode.clear();
+
     startedFuncDefs_ = true;
     MOZ_ASSERT(!finishedFuncDefs_);
     return true;
 }
 
 static bool
 ExecuteCompileTask(CompileTask* task, UniqueChars* error)
 {
@@ -854,16 +660,41 @@ wasm::ExecuteCompileTaskFromHelperThread
         if (!taskState->errorMessage)
             taskState->errorMessage = Move(error);
     }
 
     taskState->failedOrFinished.notify_one();
 }
 
 bool
+ModuleGenerator::finishTask(CompileTask* task)
+{
+    masm_.haltingAlign(CodeAlignment);
+
+    // Before merging in the new function's code, if calls in a prior code range
+    // might go out of range, insert far jumps to extend the range.
+    if (!InRange(startOfUnpatchedCallsites_, masm_.size() + task->output.bytes.length())) {
+        startOfUnpatchedCallsites_ = masm_.size();
+        if (!linkCallSites())
+            return false;
+    }
+
+    if (!linkCompiledCode(task->output))
+        return false;
+
+    task->output.clear();
+
+    MOZ_ASSERT(task->inputs.empty());
+    MOZ_ASSERT(task->output.empty());
+    MOZ_ASSERT(task->lifo.isEmpty());
+    freeTasks_.infallibleAppend(task);
+    return true;
+}
+
+bool
 ModuleGenerator::launchBatchCompile()
 {
     MOZ_ASSERT(currentTask_);
 
     if (cancelled_ && *cancelled_)
         return false;
 
     if (parallel_) {
@@ -907,133 +738,77 @@ ModuleGenerator::finishOutstandingTask()
     }
 
     // Call outside of the compilation lock.
     return finishTask(task);
 }
 
 bool
 ModuleGenerator::compileFuncDef(uint32_t funcIndex, uint32_t lineOrBytecode,
-                                Bytes&& bytes, const uint8_t* begin, const uint8_t* end,
+                                const uint8_t* begin, const uint8_t* end,
                                 Uint32Vector&& lineNums)
 {
     MOZ_ASSERT(startedFuncDefs_);
     MOZ_ASSERT(!finishedFuncDefs_);
     MOZ_ASSERT_IF(mode() == CompileMode::Tier1, funcIndex < env_->numFuncs());
 
-    numFuncDefs_++;
-
     if (!currentTask_) {
         if (freeTasks_.empty() && !finishOutstandingTask())
             return false;
         currentTask_ = freeTasks_.popCopy();
     }
 
     uint32_t funcBytecodeLength = end - begin;
 
     FuncCompileInputVector& inputs = currentTask_->inputs;
-    if (!inputs.emplaceBack(funcIndex, lineOrBytecode, Move(bytes), begin, end, Move(lineNums)))
+    if (!inputs.emplaceBack(funcIndex, lineOrBytecode, begin, end, Move(lineNums)))
         return false;
 
     uint32_t threshold;
     switch (tier()) {
       case Tier::Baseline: threshold = JitOptions.wasmBatchBaselineThreshold; break;
       case Tier::Ion:      threshold = JitOptions.wasmBatchIonThreshold;      break;
       default:             MOZ_CRASH("Invalid tier value");                   break;
     }
 
     batchedBytecode_ += funcBytecodeLength;
     MOZ_ASSERT(batchedBytecode_ <= MaxModuleBytes);
     return batchedBytecode_ <= threshold || launchBatchCompile();
 }
 
 bool
-ModuleGenerator::compileFuncDef(uint32_t funcIndex, uint32_t lineOrBytecode,
-                                const uint8_t* begin, const uint8_t* end)
-{
-    return compileFuncDef(funcIndex, lineOrBytecode, Bytes(), begin, end, Uint32Vector());
-}
-
-bool
-ModuleGenerator::compileFuncDef(uint32_t funcIndex, uint32_t lineOrBytecode,
-                                Bytes&& bytes, Uint32Vector&& lineNums)
-{
-    return compileFuncDef(funcIndex, lineOrBytecode, Move(bytes), bytes.begin(), bytes.end(), Move(lineNums));
-}
-
-bool
 ModuleGenerator::finishFuncDefs()
 {
     MOZ_ASSERT(startedFuncDefs_);
     MOZ_ASSERT(!finishedFuncDefs_);
 
     if (currentTask_ && !launchBatchCompile())
         return false;
 
     while (outstanding_ > 0) {
         if (!finishOutstandingTask())
             return false;
     }
 
-    MOZ_ASSERT_IF(!isAsmJS(), numFuncDefs_ == env_->numFuncDefs());
     finishedFuncDefs_ = true;
     return true;
 }
 
 bool
-ModuleGenerator::initSigTableLength(uint32_t sigIndex, uint32_t length)
-{
-    MOZ_ASSERT(isAsmJS());
-    MOZ_ASSERT(length != 0);
-    MOZ_ASSERT(length <= MaxTableInitialLength);
-
-    MOZ_ASSERT(env_->asmJSSigToTableIndex[sigIndex] == 0);
-    env_->asmJSSigToTableIndex[sigIndex] = numTables_;
-
-    TableDesc& table = env_->tables[numTables_++];
-    table.kind = TableKind::TypedFunction;
-    table.limits.initial = length;
-    table.limits.maximum = Some(length);
-    return allocateGlobalBytes(sizeof(TableTls), sizeof(void*), &table.globalDataOffset);
-}
-
-bool
-ModuleGenerator::initSigTableElems(uint32_t sigIndex, Uint32Vector&& elemFuncIndices)
-{
-    MOZ_ASSERT(isAsmJS());
-    MOZ_ASSERT(finishedFuncDefs_);
-
-    uint32_t tableIndex = env_->asmJSSigToTableIndex[sigIndex];
-    MOZ_ASSERT(env_->tables[tableIndex].limits.initial == elemFuncIndices.length());
-
-    InitExpr offset(Val(uint32_t(0)));
-    return env_->elemSegments.emplaceBack(tableIndex, offset, Move(elemFuncIndices));
-}
-
-bool
 ModuleGenerator::finishLinking()
 {
     // All functions and traps CodeRanges should have been processed.
 
 #ifdef DEBUG
-    if (isAsmJS()) {
-        for (uint32_t i = 0; i < AsmJSFirstDefFuncIndex; i++)
-            MOZ_ASSERT(funcToCodeRange_[i] == BAD_CODE_RANGE);
-        for (uint32_t i = AsmJSFirstDefFuncIndex; i < AsmJSFirstDefFuncIndex + numFuncDefs_; i++)
-            MOZ_ASSERT(funcToCodeRange_[i] != BAD_CODE_RANGE);
-        for (uint32_t i = AsmJSFirstDefFuncIndex + numFuncDefs_; i < funcToCodeRange_.length(); i++)
-            MOZ_ASSERT(funcToCodeRange_[i] == BAD_CODE_RANGE);
-    } else {
-        for (uint32_t codeRangeIndex : funcToCodeRange_)
-            MOZ_ASSERT(codeRangeIndex != BAD_CODE_RANGE);
-    }
+    for (uint32_t codeRangeIndex : funcToCodeRange_)
+        MOZ_ASSERT(codeRangeIndex != BAD_CODE_RANGE);
 #endif
 
     // Now that all functions and stubs are generated and their CodeRanges
-    // known, patch all calls (which can emit far jumps) and far jumps. 
+    // known, patch all calls (which can emit far jumps) and far jumps.
 
     if (!linkCallSites())
         return false;
 
     for (CallFarJump far : callFarJumps_)
         masm_.patchFarJump(far.jump, funcCodeRange(far.funcIndex).funcNormalEntry());
 
     for (TrapFarJump far : trapFarJumps_)
@@ -1076,16 +851,17 @@ ModuleGenerator::finishMetadata(const Sh
     }
 #endif
 
     // Copy over data from the ModuleEnvironment.
 
     metadata_->memoryUsage = env_->memoryUsage;
     metadata_->minMemoryLength = env_->minMemoryLength;
     metadata_->maxMemoryLength = env_->maxMemoryLength;
+    metadata_->startFuncIndex = env_->startFuncIndex;
     metadata_->tables = Move(env_->tables);
     metadata_->globals = Move(env_->globals);
     metadata_->funcNames = Move(env_->funcNames);
     metadata_->customSections = Move(env_->customSections);
 
     // Inflate the global bytes up to page size so that the total bytes are a
     // page size (as required by the allocator functions).
 
@@ -1095,22 +871,16 @@ ModuleGenerator::finishMetadata(const Sh
     // so realloc them down to size.
 
     metadataTier_->memoryAccesses.podResizeToFit();
     metadataTier_->codeRanges.podResizeToFit();
     metadataTier_->callSites.podResizeToFit();
     metadataTier_->debugTrapFarJumpOffsets.podResizeToFit();
     metadataTier_->debugFuncToCodeRange.podResizeToFit();
 
-    // For asm.js, the tables vector is over-allocated (to avoid resize during
-    // parallel copilation). Shrink it back down to fit.
-
-    if (isAsmJS() && !metadata_->tables.resize(numTables_))
-        return false;
-
     // Complete function exports and element segments with code range indices,
     // now that every function has a code range.
 
     for (FuncExport& fe : metadataTier_->funcExports)
         fe.initCodeRangeIndex(funcToCodeRange_[fe.funcIndex()]);
 
     for (ElemSegment& elems : env_->elemSegments) {
         Uint32Vector& codeRangeIndices = elems.elemCodeRangeIndices(tier());
@@ -1150,23 +920,16 @@ ModuleGenerator::finishMetadata(const Sh
     return true;
 }
 
 UniqueConstCodeSegment
 ModuleGenerator::finishCodeSegment(const ShareableBytes& bytecode)
 {
     MOZ_ASSERT(finishedFuncDefs_);
 
-    // Because of asm.js, we can only generate the FuncExportVector at the end
-    // of module generation (after we've seen the end of the exports object at
-    // the end of the asm.js module).
-
-    if (!finishFuncExports())
-        return nullptr;
-
     // Now that all imports/exports are known, we can generate a special
     // CompiledCode containing stubs.
 
     CompiledCode& stubCode = tasks_[0].output;
     MOZ_ASSERT(stubCode.empty());
 
     if (!GenerateStubs(*env_, metadataTier_->funcImports, metadataTier_->funcExports, &stubCode))
         return nullptr;
--- a/js/src/wasm/WasmGenerator.h
+++ b/js/src/wasm/WasmGenerator.h
@@ -22,35 +22,35 @@
 #include "jit/MacroAssembler.h"
 #include "wasm/WasmCompile.h"
 #include "wasm/WasmModule.h"
 #include "wasm/WasmValidate.h"
 
 namespace js {
 namespace wasm {
 
+struct CompileTask;
+typedef Vector<CompileTask*, 0, SystemAllocPolicy> CompileTaskPtrVector;
+
 // FuncCompileInput contains the input for compiling a single function.
 
 struct FuncCompileInput
 {
-    Bytes          bytesToDelete;
     const uint8_t* begin;
     const uint8_t* end;
     uint32_t       index;
     uint32_t       lineOrBytecode;
     Uint32Vector   callSiteLineNums;
 
     FuncCompileInput(uint32_t index,
                      uint32_t lineOrBytecode,
-                     Bytes&& bytesToDelete,
                      const uint8_t* begin,
                      const uint8_t* end,
                      Uint32Vector&& callSiteLineNums)
-      : bytesToDelete(Move(bytesToDelete)),
-        begin(begin),
+      : begin(begin),
         end(end),
         index(index),
         lineOrBytecode(lineOrBytecode),
         callSiteLineNums(Move(callSiteLineNums))
     {}
 };
 
 typedef Vector<FuncCompileInput, 8, SystemAllocPolicy> FuncCompileInputVector;
@@ -140,19 +140,17 @@ struct CompileTask
 // A ModuleGenerator encapsulates the creation of a wasm module. During the
 // lifetime of a ModuleGenerator, a sequence of FunctionGenerators are created
 // and destroyed to compile the individual function bodies. After generating all
 // functions, ModuleGenerator::finish() must be called to complete the
 // compilation and extract the resulting wasm module.
 
 class MOZ_STACK_CLASS ModuleGenerator
 {
-    typedef HashSet<uint32_t, DefaultHasher<uint32_t>, SystemAllocPolicy> Uint32Set;
     typedef Vector<CompileTask, 0, SystemAllocPolicy> CompileTaskVector;
-    typedef Vector<CompileTask*, 0, SystemAllocPolicy> CompileTaskPtrVector;
     typedef EnumeratedArray<Trap, Trap::Limit, uint32_t> Uint32TrapArray;
     typedef Vector<jit::CodeOffset, 0, SystemAllocPolicy> CodeOffsetVector;
 
     // Constant parameters
     SharedCompileArgs const         compileArgs_;
     UniqueChars* const              error_;
     Atomic<bool>* const             cancelled_;
     ModuleEnvironment* const        env_;
@@ -162,112 +160,83 @@ class MOZ_STACK_CLASS ModuleGenerator
     LinkDataTier*                   linkDataTier_; // Owned by linkData_
     LinkData                        linkData_;
     MetadataTier*                   metadataTier_; // Owned by metadata_
     MutableMetadata                 metadata_;
     UniqueJumpTable                 jumpTable_;
 
     // Data scoped to the ModuleGenerator's lifetime
     ExclusiveCompileTaskState       taskState_;
-    uint32_t                        numFuncDefs_;
-    uint32_t                        numSigs_;
-    uint32_t                        numTables_;
     LifoAlloc                       lifo_;
     jit::JitContext                 jcx_;
     jit::TempAllocator              masmAlloc_;
     jit::MacroAssembler             masm_;
     Uint32Vector                    funcToCodeRange_;
     Uint32TrapArray                 trapCodeOffsets_;
     uint32_t                        debugTrapCodeOffset_;
     TrapFarJumpVector               trapFarJumps_;
     CallFarJumpVector               callFarJumps_;
     CallSiteTargetVector            callSiteTargets_;
-    Uint32Set                       exportedFuncs_;
     uint32_t                        lastPatchedCallSite_;
     uint32_t                        startOfUnpatchedCallsites_;
     CodeOffsetVector                debugTrapFarJumps_;
 
     // Parallel compilation
     bool                            parallel_;
     uint32_t                        outstanding_;
     CompileTaskVector               tasks_;
     CompileTaskPtrVector            freeTasks_;
     CompileTask*                    currentTask_;
     uint32_t                        batchedBytecode_;
 
     // Assertions
     DebugOnly<bool>                 startedFuncDefs_;
     DebugOnly<bool>                 finishedFuncDefs_;
 
+    bool allocateGlobalBytes(uint32_t bytes, uint32_t align, uint32_t* globalDataOff);
+
     bool funcIsCompiled(uint32_t funcIndex) const;
     const CodeRange& funcCodeRange(uint32_t funcIndex) const;
 
     bool linkCallSites();
     void noteCodeRange(uint32_t codeRangeIndex, const CodeRange& codeRange);
     bool linkCompiledCode(const CompiledCode& code);
     bool finishTask(CompileTask* task);
+    bool launchBatchCompile();
     bool finishOutstandingTask();
-    bool finishFuncExports();
+
     bool finishLinking();
     bool finishMetadata(const ShareableBytes& bytecode);
     UniqueConstCodeSegment finishCodeSegment(const ShareableBytes& bytecode);
     UniqueJumpTable createJumpTable(const CodeSegment& codeSegment);
-    bool addFuncImport(const Sig& sig, uint32_t globalDataOffset);
-    bool allocateGlobalBytes(uint32_t bytes, uint32_t align, uint32_t* globalDataOff);
-    bool allocateGlobal(GlobalDesc* global);
-
-    bool launchBatchCompile();
-    bool compileFuncDef(uint32_t funcIndex, uint32_t lineOrBytecode,
-                        Bytes&& bytes, const uint8_t* begin, const uint8_t* end,
-                        Uint32Vector&& lineNums);
-
-    bool initAsmJS(Metadata* asmJSMetadata);
-    bool initWasm(size_t codeLength);
 
     bool isAsmJS() const { return env_->isAsmJS(); }
     Tier tier() const { return env_->tier(); }
     CompileMode mode() const { return env_->mode(); }
     bool debugEnabled() const { return env_->debugEnabled(); }
 
   public:
     ModuleGenerator(const CompileArgs& args, ModuleEnvironment* env,
                     Atomic<bool>* cancelled, UniqueChars* error);
     ~ModuleGenerator();
-
     MOZ_MUST_USE bool init(size_t codeSectionSize, Metadata* maybeAsmJSMetadata = nullptr);
 
-    // Function definitions:
+    // After initialization, startFuncDefs() shall be called before one call to
+    // compileFuncDef() for each funcIndex in the range [0, env->numFuncDefs),
+    // followed by finishFuncDefs().
+
     MOZ_MUST_USE bool startFuncDefs();
     MOZ_MUST_USE bool compileFuncDef(uint32_t funcIndex, uint32_t lineOrBytecode,
-                                     const uint8_t* begin, const uint8_t* end);
-    MOZ_MUST_USE bool compileFuncDef(uint32_t funcIndex, uint32_t lineOrBytecode,
-                                     Bytes&& bytes, Uint32Vector&& callSiteLineNums);
+                                     const uint8_t* begin, const uint8_t* end,
+                                     Uint32Vector&& callSiteLineNums = Uint32Vector());
     MOZ_MUST_USE bool finishFuncDefs();
 
-    // asm.js accessors:
-    uint32_t minMemoryLength() const { return env_->minMemoryLength; }
-    uint32_t numSigs() const { return numSigs_; }
-    const SigWithId& sig(uint32_t sigIndex) const;
-    const SigWithId& funcSig(uint32_t funcIndex) const;
+    // After finishFuncDefs(), one of the following is called, depending on the
+    // CompileMode: finishModule for Once or Tier1, finishTier2 for Tier2.
 
-    // asm.js lazy initialization:
-    void initSig(uint32_t sigIndex, Sig&& sig);
-    void initFuncSig(uint32_t funcIndex, uint32_t sigIndex);
-    MOZ_MUST_USE bool initImport(uint32_t funcIndex, uint32_t sigIndex);
-    MOZ_MUST_USE bool initSigTableLength(uint32_t sigIndex, uint32_t length);
-    MOZ_MUST_USE bool initSigTableElems(uint32_t sigIndex, Uint32Vector&& elemFuncIndices);
-    void initMemoryUsage(MemoryUsage memoryUsage);
-    void bumpMinMemoryLength(uint32_t newMinMemoryLength);
-    MOZ_MUST_USE bool addGlobal(ValType type, bool isConst, uint32_t* index);
-    MOZ_MUST_USE bool addExport(CacheableChars&& fieldChars, uint32_t funcIndex);
-
-    // Finish compilation of the given bytecode.
     SharedModule finishModule(const ShareableBytes& bytecode);
-
-    // Finish compilation of the given bytecode, installing tier-variant parts
-    // for Tier 2 into module.
     MOZ_MUST_USE bool finishTier2(Module& module);
 };
 
 } // namespace wasm
 } // namespace js
 
 #endif // wasm_generator_h
--- a/js/src/wasm/WasmIonCompile.cpp
+++ b/js/src/wasm/WasmIonCompile.cpp
@@ -2032,24 +2032,29 @@ EmitCallArgs(FunctionCompiler& f, const 
         if (!f.passArg(args[i], sig.args()[i], call))
             return false;
     }
 
     return f.finishCall(call);
 }
 
 static bool
-EmitCall(FunctionCompiler& f)
+EmitCall(FunctionCompiler& f, bool asmJSFuncDef)
 {
     uint32_t lineOrBytecode = f.readCallSiteLineOrBytecode();
 
     uint32_t funcIndex;
     DefVector args;
-    if (!f.iter().readCall(&funcIndex, &args))
-        return false;
+    if (asmJSFuncDef) {
+        if (!f.iter().readOldCallDirect(f.env().numFuncImports(), &funcIndex, &args))
+            return false;
+    } else {
+        if (!f.iter().readCall(&funcIndex, &args))
+            return false;
+    }
 
     if (f.inDeadCode())
         return true;
 
     const Sig& sig = *f.env().funcSigs[funcIndex];
 
     CallCompileState call(f, lineOrBytecode);
     if (!EmitCallArgs(f, sig, args, &call))
@@ -3322,17 +3327,17 @@ EmitBodyExprs(FunctionCompiler& f)
             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));
+            CHECK(EmitCall(f, /* asmJSFuncDef = */ false));
           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));
@@ -3718,16 +3723,18 @@ EmitBodyExprs(FunctionCompiler& f)
               case uint16_t(MozOp::F64Exp):
                 CHECK_ASMJS(EmitUnaryMathBuiltinCall(f, SymbolicAddress::ExpD, ValType::F64));
               case uint16_t(MozOp::F64Log):
                 CHECK_ASMJS(EmitUnaryMathBuiltinCall(f, SymbolicAddress::LogD, ValType::F64));
               case uint16_t(MozOp::F64Pow):
                 CHECK_ASMJS(EmitBinaryMathBuiltinCall(f, SymbolicAddress::PowD, ValType::F64));
               case uint16_t(MozOp::F64Atan2):
                 CHECK_ASMJS(EmitBinaryMathBuiltinCall(f, SymbolicAddress::ATan2D, ValType::F64));
+              case uint16_t(MozOp::OldCallDirect):
+                CHECK_ASMJS(EmitCall(f, /* asmJSFuncDef = */ true));
               case uint16_t(MozOp::OldCallIndirect):
                 CHECK_ASMJS(EmitCallIndirect(f, /* oldStyle = */ true));
 
                 // Atomics
               case uint16_t(MozOp::I32AtomicsLoad):
                 CHECK_ASMJS(EmitAtomicsLoad(f));
               case uint16_t(MozOp::I32AtomicsStore):
                 CHECK_ASMJS(EmitAtomicsStore(f));
--- a/js/src/wasm/WasmStubs.cpp
+++ b/js/src/wasm/WasmStubs.cpp
@@ -537,16 +537,43 @@ GenerateImportFunction(jit::MacroAssembl
 
     GenerateFunctionEpilogue(masm, framePushed, offsets);
 
     masm.wasmEmitTrapOutOfLineCode();
 
     return FinishOffsets(masm, offsets);
 }
 
+static const unsigned STUBS_LIFO_DEFAULT_CHUNK_SIZE = 4 * 1024;
+
+bool
+wasm::GenerateImportFunctions(const ModuleEnvironment& env, const FuncImportVector& imports,
+                              CompiledCode* code)
+{
+    LifoAlloc lifo(STUBS_LIFO_DEFAULT_CHUNK_SIZE);
+    TempAllocator alloc(&lifo);
+    MacroAssembler masm(MacroAssembler::WasmToken(), alloc);
+
+    for (uint32_t funcIndex = 0; funcIndex < imports.length(); funcIndex++) {
+        const FuncImport& fi = imports[funcIndex];
+
+        FuncOffsets offsets;
+        if (!GenerateImportFunction(masm, fi, env.funcSigs[funcIndex]->id, &offsets))
+            return false;
+        if (!code->codeRanges.emplaceBack(funcIndex, /* bytecodeOffset = */ 0, offsets))
+            return false;
+    }
+
+    masm.finish();
+    if (masm.oom())
+        return false;
+
+    return code->swap(masm);
+}
+
 // Generate a stub that is called via the internal ABI derived from the
 // signature of the import and calls into an appropriate callImport C++
 // function, having boxed all the ABI arguments into a homogeneous Value array.
 static bool
 GenerateImportInterpExit(MacroAssembler& masm, const FuncImport& fi, uint32_t funcImportIndex,
                          Label* throwLabel, CallableOffsets* offsets)
 {
     masm.setFramePushed(0);
@@ -1308,18 +1335,16 @@ GenerateDebugTrapStub(MacroAssembler& ma
     masm.setFramePushed(framePushed);
     masm.PopRegsInMask(AllAllocatableRegs);
 
     GenerateExitEpilogue(masm, 0, ExitReason::Fixed::DebugTrap, offsets);
 
     return FinishOffsets(masm, offsets);
 }
 
-static const unsigned STUBS_LIFO_DEFAULT_CHUNK_SIZE = 4 * 1024;
-
 bool
 wasm::GenerateStubs(const ModuleEnvironment& env, const FuncImportVector& imports,
                     const FuncExportVector& exports, CompiledCode* code)
 {
     LifoAlloc lifo(STUBS_LIFO_DEFAULT_CHUNK_SIZE);
     TempAllocator alloc(&lifo);
     MacroAssembler masm(MacroAssembler::WasmToken(), alloc);
 
@@ -1338,24 +1363,16 @@ wasm::GenerateStubs(const ModuleEnvironm
             return false;
         if (!code->codeRanges.emplaceBack(CodeRange::ImportInterpExit, funcIndex, offsets))
             return false;
 
         if (!GenerateImportJitExit(masm, fi, &throwLabel, &offsets))
             return false;
         if (!code->codeRanges.emplaceBack(CodeRange::ImportJitExit, funcIndex, offsets))
             return false;
-
-        if (!env.isAsmJS()) {
-            FuncOffsets offsets;
-            if (!GenerateImportFunction(masm, fi, env.funcSigs[funcIndex]->id, &offsets))
-                return false;
-            if (!code->codeRanges.emplaceBack(funcIndex, /* bytecodeOffset = */ 0, offsets))
-                return false;
-        }
     }
 
     for (const FuncExport& fe : exports) {
         Offsets offsets;
         if (!GenerateEntry(masm, fe, &offsets))
             return false;
         if (!code->codeRanges.emplaceBack(CodeRange::Entry, fe.funcIndex(), offsets))
             return false;
--- a/js/src/wasm/WasmStubs.h
+++ b/js/src/wasm/WasmStubs.h
@@ -24,15 +24,19 @@
 namespace js {
 namespace wasm {
 
 extern bool
 GenerateBuiltinThunk(jit::MacroAssembler& masm, jit::ABIFunctionType abiType, ExitReason exitReason,
                      void* funcPtr, CallableOffsets* offsets);
 
 extern bool
+GenerateImportFunctions(const ModuleEnvironment& env, const FuncImportVector& imports,
+                        CompiledCode* code);
+
+extern bool
 GenerateStubs(const ModuleEnvironment& env, const FuncImportVector& imports,
               const FuncExportVector& exports, CompiledCode* code);
 
 } // namespace wasm
 } // namespace js
 
 #endif // wasm_stubs_h
--- a/js/src/wasm/WasmTextToBinary.cpp
+++ b/js/src/wasm/WasmTextToBinary.cpp
@@ -2799,18 +2799,17 @@ ParseLimits(WasmParseContext& c, Limits*
     if (!c.ts.match(WasmToken::Index, &initial, c.error))
         return false;
 
     Maybe<uint32_t> maximum;
     WasmToken token;
     if (c.ts.getIf(WasmToken::Index, &token))
         maximum.emplace(token.index());
 
-    Limits r = { initial.index(), maximum };
-    *limits = r;
+    *limits = Limits(initial.index(), maximum);
     return true;
 }
 
 static bool
 ParseMemory(WasmParseContext& c, WasmToken token, AstModule* module)
 {
     AstName name = c.ts.getIfName();
 
@@ -2867,18 +2866,17 @@ ParseMemory(WasmParseContext& c, WasmTok
             if (!segment || !module->append(segment))
                 return false;
 
             pages = AlignBytes<size_t>(totalLength, PageSize) / PageSize;
             if (pages != uint32_t(pages))
                 return false;
         }
 
-        Limits memory = { uint32_t(pages), Some(uint32_t(pages)) };
-        if (!module->addMemory(name, memory))
+        if (!module->addMemory(name, Limits(pages, Some(pages))))
             return false;
 
         if (!c.ts.match(WasmToken::CloseParen, c.error))
             return false;
 
         return true;
     }
 
@@ -3160,18 +3158,17 @@ ParseTable(WasmParseContext& c, WasmToke
 
     if (!c.ts.match(WasmToken::CloseParen, c.error))
         return false;
 
     uint32_t numElements = uint32_t(elems.length());
     if (numElements != elems.length())
         return false;
 
-    Limits r = { numElements, Some(numElements) };
-    if (!module->addTable(name, r))
+    if (!module->addTable(name, Limits(numElements, Some(numElements))))
         return false;
 
     auto* zero = new(c.lifo) AstConst(Val(uint32_t(0)));
     if (!zero)
         return false;
 
     AstElemSegment* segment = new(c.lifo) AstElemSegment(zero, Move(elems));
     return segment && module->append(segment);
--- a/js/src/wasm/WasmTypes.h
+++ b/js/src/wasm/WasmTypes.h
@@ -159,16 +159,39 @@ struct ShareableBase : AtomicRefCounted<
         bool ok = seen->add(p, self);
         (void)ok;  // oh well
         return mallocSizeOf(self) + self->sizeOfExcludingThis(mallocSizeOf);
     }
 };
 
 // ValType utilities
 
+static inline unsigned
+SizeOf(ValType vt)
+{
+    switch (vt) {
+      case ValType::I32:
+      case ValType::F32:
+        return 4;
+      case ValType::I64:
+      case ValType::F64:
+        return 8;
+      case ValType::I8x16:
+      case ValType::I16x8:
+      case ValType::I32x4:
+      case ValType::F32x4:
+      case ValType::B8x16:
+      case ValType::B16x8:
+      case ValType::B32x4:
+        return 16;
+      default:
+        MOZ_CRASH("Invalid ValType");
+    }
+}
+
 static inline bool
 IsSimdType(ValType vt)
 {
     switch (vt) {
       case ValType::I8x16:
       case ValType::I16x8:
       case ValType::I32x4:
       case ValType::F32x4:
@@ -850,17 +873,18 @@ class SigIdDesc
 // that compares this signature's id or, at instantiation what signature ids to
 // allocate in the global hash and where to put them.
 
 struct SigWithId : Sig
 {
     SigIdDesc id;
 
     SigWithId() = default;
-    explicit SigWithId(Sig&& sig, SigIdDesc id) : Sig(Move(sig)), id(id) {}
+    explicit SigWithId(Sig&& sig) : Sig(Move(sig)), id() {}
+    SigWithId(Sig&& sig, SigIdDesc id) : Sig(Move(sig)), id(id) {}
     void operator=(Sig&& rhs) { Sig::operator=(Move(rhs)); }
 
     WASM_DECLARE_SERIALIZABLE(SigWithId)
 };
 
 typedef Vector<SigWithId, 0, SystemAllocPolicy> SigWithIdVector;
 typedef Vector<const SigWithId*, 0, SystemAllocPolicy> SigWithIdPtrVector;
 
@@ -1323,16 +1347,21 @@ enum ModuleKind
 };
 
 // Represents the resizable limits of memories and tables.
 
 struct Limits
 {
     uint32_t initial;
     Maybe<uint32_t> maximum;
+
+    Limits() = default;
+    explicit Limits(uint32_t initial, const Maybe<uint32_t>& maximum = Nothing())
+      : initial(initial), maximum(maximum)
+    {}
 };
 
 // TableDesc describes a table as well as the offset of the table's base pointer
 // in global memory. Currently, wasm only has "any function" and asm.js only
 // "typed function".
 
 enum class TableKind
 {
--- a/js/src/wasm/WasmValidate.h
+++ b/js/src/wasm/WasmValidate.h
@@ -39,29 +39,32 @@ struct ModuleEnvironment
     const DebugEnabled        debug;
     const ModuleKind          kind;
 
     // Constant parameters determined no later than at the start of the code
     // section:
     CompileMode               mode_;
     Tier                      tier_;
 
-    // Module fields filled out incrementally during decoding:
+    // Module fields decoded from the module environment (or initialized while
+    // validating an asm.js module) and immutable during compilation:
     MemoryUsage               memoryUsage;
-    Atomic<uint32_t>          minMemoryLength;
+    uint32_t                  minMemoryLength;
     Maybe<uint32_t>           maxMemoryLength;
     SigWithIdVector           sigs;
     SigWithIdPtrVector        funcSigs;
     Uint32Vector              funcImportGlobalDataOffsets;
     GlobalDescVector          globals;
     TableDescVector           tables;
     Uint32Vector              asmJSSigToTableIndex;
     ImportVector              imports;
     ExportVector              exports;
     Maybe<uint32_t>           startFuncIndex;
+
+    // Fields decoded as part of the wasm module tail:
     ElemSegmentVector         elemSegments;
     DataSegmentVector         dataSegments;
     NameInBytecodeVector      funcNames;
     CustomSectionVector       customSections;
 
     static const CompileMode UnknownMode = (CompileMode)-1;
     static const Tier        UnknownTier = (Tier)-1;
 
@@ -93,34 +96,24 @@ struct ModuleEnvironment
     }
     size_t numTables() const {
         return tables.length();
     }
     size_t numSigs() const {
         return sigs.length();
     }
     size_t numFuncs() const {
-        // asm.js pre-reserves a bunch of function index space which is
-        // incrementally filled in during function-body validation. Thus, there
-        // are a few possible interpretations of numFuncs() (total index space
-        // size vs.  exact number of imports/definitions encountered so far) and
-        // to simplify things we simply only define this quantity for wasm.
-        MOZ_ASSERT(!isAsmJS());
         return funcSigs.length();
     }
+    size_t numFuncImports() const {
+        return funcImportGlobalDataOffsets.length();
+    }
     size_t numFuncDefs() const {
-        // asm.js overallocates the length of funcSigs and in general does not
-        // know the number of function definitions until it's done compiling.
-        MOZ_ASSERT(!isAsmJS());
         return funcSigs.length() - funcImportGlobalDataOffsets.length();
     }
-    size_t numFuncImports() const {
-        MOZ_ASSERT(!isAsmJS());
-        return funcImportGlobalDataOffsets.length();
-    }
     bool usesMemory() const {
         return UsesMemory(memoryUsage);
     }
     bool isAsmJS() const {
         return kind == ModuleKind::AsmJS;
     }
     bool debugEnabled() const {
         return debug == DebugEnabled::True;
--- a/mfbt/FastBernoulliTrial.h
+++ b/mfbt/FastBernoulliTrial.h
@@ -350,17 +350,17 @@ class FastBernoulliTrial {
      *
      *    if (skipCount > SIZE_MAX)
      *       skipCount = SIZE_MAX;
      *
      * that leads to undefined behavior 64-bit machines: SIZE_MAX coerced to
      * double is 2^64, not 2^64-1, so this doesn't actually set skipCount to a
      * value that can be safely assigned to mSkipCount.
      *
-     * Jakub Oleson cleverly suggested flipping the sense of the comparison: if
+     * Jakob Olesen cleverly suggested flipping the sense of the comparison: if
      * we require that skipCount < SIZE_MAX, then because of the gaps (2048)
      * between doubles at that magnitude, the highest double less than 2^64 is
      * 2^64 - 2048, which is fine to store in a size_t.
      *
      * (On 32-bit machines, all size_t values can be represented exactly in
      * double, so all is well.)
      */
     double skipCount = std::floor(std::log(mGenerator.nextDouble())
--- a/toolkit/mozapps/update/tests/data/xpcshellUtilsAUS.js
+++ b/toolkit/mozapps/update/tests/data/xpcshellUtilsAUS.js
@@ -81,18 +81,16 @@ const FILE_MAINTENANCE_SERVICE_BIN = "ma
 const FILE_MAINTENANCE_SERVICE_INSTALLER_BIN = "maintenanceservice_installer.exe";
 const FILE_OLD_VERSION_MAR = "old_version.mar";
 const FILE_PARTIAL_EXE = "partial.exe";
 const FILE_UPDATER_BIN = "updater" + BIN_SUFFIX;
 const FILE_WRONG_CHANNEL_MAR = "wrong_product_channel.mar";
 
 const PERFORMING_STAGED_UPDATE = "Performing a staged update";
 const CALL_QUIT = "calling QuitProgressUI";
-const REMOVE_OLD_DIST_DIR = "removing old distribution directory";
-const MOVE_OLD_DIST_DIR = "Moving old distribution directory to new location";
 const ERR_UPDATE_IN_PROGRESS = "Update already in progress! Exiting";
 const ERR_RENAME_FILE = "rename_file: failed to rename file";
 const ERR_ENSURE_COPY = "ensure_copy: failed to copy the file";
 const ERR_UNABLE_OPEN_DEST = "unable to open destination file";
 const ERR_BACKUP_DISCARD = "backup_discard: unable to remove";
 const ERR_MOVE_DESTDIR_7 = "Moving destDir to tmpDir failed, err: 7";
 const ERR_BACKUP_CREATE_7 = "backup_create failed: 7";
 const ERR_LOADSOURCEFILE_FAILED = "LoadSourceFile failed";
@@ -3005,22 +3003,16 @@ function checkUpdateLogContents(aCompare
   // These could be changed to relative paths using <test_dir_path> and
   // <update_dir_path>
   updateLogContents = updateLogContents.replace(/PATCH DIRECTORY.*/g, "");
   updateLogContents = updateLogContents.replace(/INSTALLATION DIRECTORY.*/g, "");
   updateLogContents = updateLogContents.replace(/WORKING DIRECTORY.*/g, "");
   // Skip lines that log failed attempts to open the callback executable.
   updateLogContents = updateLogContents.replace(/NS_main: callback app file .*/g, "");
 
-  if (IS_MACOSX) {
-    // Skip lines that log moving the distribution directory for Mac v2 signing.
-    updateLogContents = updateLogContents.replace(/Moving old [^\n]*\nrename_file: .*/g, "");
-    updateLogContents = updateLogContents.replace(/New distribution directory .*/g, "");
-  }
-
   if (IS_WIN) {
     // The FindFile results when enumerating the filesystem on Windows is not
     // determistic so the results matching the following need to be fixed.
     let re = new RegExp("([^\n]* 7\/7text1[^\n]*)\n" +
                         "([^\n]* 7\/7text0[^\n]*)\n", "g");
     updateLogContents = updateLogContents.replace(re, "$2\n$1\n");
   }
 
--- a/toolkit/mozapps/update/tests/unit_base_updater/marStageSuccessComplete.js
+++ b/toolkit/mozapps/update/tests/unit_base_updater/marStageSuccessComplete.js
@@ -11,17 +11,16 @@ function run_test() {
   if (!setupTestCommon()) {
     return;
   }
   gTestFiles = gTestFilesCompleteSuccess;
   gTestFiles[gTestFiles.length - 1].originalContents = null;
   gTestFiles[gTestFiles.length - 1].compareContents = "FromComplete\n";
   gTestFiles[gTestFiles.length - 1].comparePerms = 0o644;
   gTestDirs = gTestDirsCompleteSuccess;
-  setupDistributionDir();
   setupSymLinks();
   setupUpdaterTest(FILE_COMPLETE_MAR, false);
 }
 
 /**
  * Called after the call to setupUpdaterTest finishes.
  */
 function setupUpdaterTestFinished() {
@@ -51,56 +50,28 @@ function runUpdateFinished() {
  */
 function checkPostUpdateAppLogFinished() {
   checkAppBundleModTime();
   checkSymLinks();
   standardInit();
   checkPostUpdateRunningFile(true);
   checkFilesAfterUpdateSuccess(getApplyDirFile, false, true);
   checkUpdateLogContents(LOG_REPLACE_SUCCESS, false, true);
-  checkDistributionDir();
   do_execute_soon(waitForUpdateXMLFiles);
 }
 
 /**
  * Called after the call to waitForUpdateXMLFiles finishes.
  */
 function waitForUpdateXMLFilesFinished() {
   checkUpdateManager(STATE_NONE, false, STATE_SUCCEEDED, 0, 1);
   checkCallbackLog();
 }
 
 /**
- * Setup the state of the distribution directory for the test.
- */
-function setupDistributionDir() {
-  if (IS_MACOSX) {
-    // Create files in the old distribution directory location to verify that
-    // the directory and its contents are removed when there is a distribution
-    // directory in the new location.
-    let testFile = getApplyDirFile(DIR_MACOS + "distribution/testFile", true);
-    writeFile(testFile, "test\n");
-    testFile = getApplyDirFile(DIR_MACOS + "distribution/test1/testFile", true);
-    writeFile(testFile, "test\n");
-  }
-}
-
-/**
- * Checks the state of the distribution directory for the test.
- */
-function checkDistributionDir() {
-  if (IS_MACOSX) {
-    let distributionDir = getApplyDirFile(DIR_MACOS + "distribution", true);
-    Assert.ok(!distributionDir.exists(),
-              MSG_SHOULD_NOT_EXIST + getMsgPath(distributionDir.path));
-    checkUpdateLogContains(REMOVE_OLD_DIST_DIR);
-  }
-}
-
-/**
  * Setup symlinks for the test.
  */
 function setupSymLinks() {
   // Don't test symlinks on Mac OS X in this test since it tends to timeout.
   // It is tested on Mac OS X in marAppInUseStageSuccessComplete_unix.js
   if (IS_UNIX && !IS_MACOSX) {
     removeSymlink();
     createSymlink();
--- a/toolkit/mozapps/update/tests/unit_base_updater/marStageSuccessPartial.js
+++ b/toolkit/mozapps/update/tests/unit_base_updater/marStageSuccessPartial.js
@@ -12,17 +12,16 @@ function run_test() {
     return;
   }
   gTestFiles = gTestFilesPartialSuccess;
   gTestFiles[gTestFiles.length - 2].originalContents = null;
   gTestFiles[gTestFiles.length - 2].compareContents = "FromPartial\n";
   gTestFiles[gTestFiles.length - 2].comparePerms = 0o644;
   gTestDirs = gTestDirsPartialSuccess;
   preventDistributionFiles();
-  setupDistributionDir();
   setupUpdaterTest(FILE_PARTIAL_MAR, true);
 }
 
 /**
  * Called after the call to setupUpdaterTest finishes.
  */
 function setupUpdaterTestFinished() {
   stageUpdate(true);
@@ -50,63 +49,18 @@ function runUpdateFinished() {
  * Called after the call to checkPostUpdateAppLog finishes.
  */
 function checkPostUpdateAppLogFinished() {
   checkAppBundleModTime();
   standardInit();
   checkPostUpdateRunningFile(true);
   checkFilesAfterUpdateSuccess(getApplyDirFile, false, true);
   checkUpdateLogContents(LOG_REPLACE_SUCCESS, false, true, true);
-  checkDistributionDir();
   do_execute_soon(waitForUpdateXMLFiles);
 }
 
 /**
  * Called after the call to waitForUpdateXMLFiles finishes.
  */
 function waitForUpdateXMLFilesFinished() {
   checkUpdateManager(STATE_NONE, false, STATE_SUCCEEDED, 0, 1);
   checkCallbackLog();
 }
-
-/**
- * Setup the state of the distribution directory for the test.
- */
-function setupDistributionDir() {
-  if (IS_MACOSX) {
-    // Create files in the old distribution directory location to verify that
-    // the directory and its contents are moved to the new location on update.
-    let testFile = getApplyDirFile(DIR_MACOS + "distribution/testFile", true);
-    writeFile(testFile, "test\n");
-    testFile = getApplyDirFile(DIR_MACOS + "distribution/test/testFile", true);
-    writeFile(testFile, "test\n");
-  }
-}
-
-/**
- * Checks the state of the distribution directory.
- */
-function checkDistributionDir() {
-  let distributionDir = getApplyDirFile(DIR_RESOURCES + "distribution", true);
-  if (IS_MACOSX) {
-    Assert.ok(distributionDir.exists(),
-              MSG_SHOULD_EXIST + getMsgPath(distributionDir.path));
-
-    let testFile = getApplyDirFile(DIR_RESOURCES + "distribution/testFile", true);
-    Assert.ok(testFile.exists(),
-              MSG_SHOULD_EXIST + getMsgPath(testFile.path));
-
-    testFile = getApplyDirFile(DIR_RESOURCES + "distribution/test/testFile", true);
-    Assert.ok(testFile.exists(),
-              MSG_SHOULD_EXIST + getMsgPath(testFile.path));
-
-    distributionDir = getApplyDirFile(DIR_MACOS + "distribution", true);
-    Assert.ok(!distributionDir.exists(),
-              MSG_SHOULD_NOT_EXIST + getMsgPath(distributionDir.path));
-
-    checkUpdateLogContains(MOVE_OLD_DIST_DIR);
-  } else {
-    debugDump("testing that files aren't added with an add-if instruction " +
-              "when the file's destination directory doesn't exist");
-    Assert.ok(!distributionDir.exists(),
-              MSG_SHOULD_NOT_EXIST + getMsgPath(distributionDir.path));
-  }
-}
--- a/toolkit/mozapps/update/tests/unit_base_updater/marSuccessComplete.js
+++ b/toolkit/mozapps/update/tests/unit_base_updater/marSuccessComplete.js
@@ -7,17 +7,16 @@
 
 function run_test() {
   if (!setupTestCommon()) {
     return;
   }
   gTestFiles = gTestFilesCompleteSuccess;
   gTestDirs = gTestDirsCompleteSuccess;
   preventDistributionFiles();
-  setupDistributionDir();
   setupUpdaterTest(FILE_COMPLETE_MAR, true);
 }
 
 /**
  * Called after the call to setupUpdaterTest finishes.
  */
 function setupUpdaterTestFinished() {
   runUpdate(STATE_SUCCEEDED, false, 0, true);
@@ -34,63 +33,18 @@ function runUpdateFinished() {
  * Called after the call to checkPostUpdateAppLog finishes.
  */
 function checkPostUpdateAppLogFinished() {
   checkAppBundleModTime();
   standardInit();
   checkPostUpdateRunningFile(true);
   checkFilesAfterUpdateSuccess(getApplyDirFile);
   checkUpdateLogContents(LOG_COMPLETE_SUCCESS, false, false, true);
-  checkDistributionDir();
   do_execute_soon(waitForUpdateXMLFiles);
 }
 
 /**
  * Called after the call to waitForUpdateXMLFiles finishes.
  */
 function waitForUpdateXMLFilesFinished() {
   checkUpdateManager(STATE_NONE, false, STATE_SUCCEEDED, 0, 1);
   checkCallbackLog();
 }
-
-/**
- * Setup the state of the distribution directory for the test.
- */
-function setupDistributionDir() {
-  if (IS_MACOSX) {
-    // Create files in the old distribution directory location to verify that
-    // the directory and its contents are moved to the new location on update.
-    let testFile = getApplyDirFile(DIR_MACOS + "distribution/testFile", true);
-    writeFile(testFile, "test\n");
-    testFile = getApplyDirFile(DIR_MACOS + "distribution/test/testFile", true);
-    writeFile(testFile, "test\n");
-  }
-}
-
-/**
- * Checks the state of the distribution directory.
- */
-function checkDistributionDir() {
-  let distributionDir = getApplyDirFile(DIR_RESOURCES + "distribution", true);
-  if (IS_MACOSX) {
-    Assert.ok(distributionDir.exists(),
-              MSG_SHOULD_EXIST + getMsgPath(distributionDir.path));
-
-    let testFile = getApplyDirFile(DIR_RESOURCES + "distribution/testFile", true);
-    Assert.ok(testFile.exists(),
-              MSG_SHOULD_EXIST + getMsgPath(testFile.path));
-
-    testFile = getApplyDirFile(DIR_RESOURCES + "distribution/test/testFile", true);
-    Assert.ok(testFile.exists(),
-              MSG_SHOULD_EXIST + getMsgPath(testFile.path));
-
-    distributionDir = getApplyDirFile(DIR_MACOS + "distribution", true);
-    Assert.ok(!distributionDir.exists(),
-              MSG_SHOULD_NOT_EXIST + getMsgPath(distributionDir.path));
-
-    checkUpdateLogContains(MOVE_OLD_DIST_DIR);
-  } else {
-    debugDump("testing that files aren't added with an add-if instruction " +
-              "when the file's destination directory doesn't exist");
-    Assert.ok(!distributionDir.exists(),
-              MSG_SHOULD_NOT_EXIST + getMsgPath(distributionDir.path));
-  }
-}
--- a/toolkit/mozapps/update/tests/unit_base_updater/marSuccessPartial.js
+++ b/toolkit/mozapps/update/tests/unit_base_updater/marSuccessPartial.js
@@ -12,17 +12,16 @@ function run_test() {
   gTestFiles = gTestFilesPartialSuccess;
   gTestFiles[gTestFiles.length - 1].originalContents = null;
   gTestFiles[gTestFiles.length - 1].compareContents = "FromPartial\n";
   gTestFiles[gTestFiles.length - 1].comparePerms = 0o644;
   gTestFiles[gTestFiles.length - 2].originalContents = null;
   gTestFiles[gTestFiles.length - 2].compareContents = "FromPartial\n";
   gTestFiles[gTestFiles.length - 2].comparePerms = 0o644;
   gTestDirs = gTestDirsPartialSuccess;
-  setupDistributionDir();
   // The third parameter will test that a relative path that contains a
   // directory traversal to the post update binary doesn't execute.
   setupUpdaterTest(FILE_PARTIAL_MAR, false, "test/../");
 }
 
 /**
  * Called after the call to setupUpdaterTest finishes.
  */
@@ -34,46 +33,18 @@ function setupUpdaterTestFinished() {
  * Called after the call to runUpdate finishes.
  */
 function runUpdateFinished() {
   checkAppBundleModTime();
   standardInit();
   checkPostUpdateRunningFile(false);
   checkFilesAfterUpdateSuccess(getApplyDirFile);
   checkUpdateLogContents(LOG_PARTIAL_SUCCESS);
-  checkDistributionDir();
   do_execute_soon(waitForUpdateXMLFiles);
 }
 
 /**
  * Called after the call to waitForUpdateXMLFiles finishes.
  */
 function waitForUpdateXMLFilesFinished() {
   checkUpdateManager(STATE_NONE, false, STATE_SUCCEEDED, 0, 1);
   checkCallbackLog();
 }
-
-/**
- * Setup the state of the distribution directory for the test.
- */
-function setupDistributionDir() {
-  if (IS_MACOSX) {
-    // Create files in the old distribution directory location to verify that
-    // the directory and its contents are removed when there is a distribution
-    // directory in the new location.
-    let testFile = getApplyDirFile(DIR_MACOS + "distribution/testFile", true);
-    writeFile(testFile, "test\n");
-    testFile = getApplyDirFile(DIR_MACOS + "distribution/test/testFile", true);
-    writeFile(testFile, "test\n");
-  }
-}
-
-/**
- * Checks the state of the distribution directory.
- */
-function checkDistributionDir() {
-  if (IS_MACOSX) {
-    let distributionDir = getApplyDirFile(DIR_MACOS + "distribution", true);
-    Assert.ok(!distributionDir.exists(),
-              MSG_SHOULD_NOT_EXIST + getMsgPath(distributionDir.path));
-    checkUpdateLogContains(REMOVE_OLD_DIST_DIR);
-  }
-}
--- a/toolkit/mozapps/update/tests/unit_service_updater/marStageSuccessCompleteSvc.js
+++ b/toolkit/mozapps/update/tests/unit_service_updater/marStageSuccessCompleteSvc.js
@@ -11,17 +11,16 @@ function run_test() {
   if (!setupTestCommon()) {
     return;
   }
   gTestFiles = gTestFilesCompleteSuccess;
   gTestFiles[gTestFiles.length - 1].originalContents = null;
   gTestFiles[gTestFiles.length - 1].compareContents = "FromComplete\n";
   gTestFiles[gTestFiles.length - 1].comparePerms = 0o644;
   gTestDirs = gTestDirsCompleteSuccess;
-  setupDistributionDir();
   setupSymLinks();
   setupUpdaterTest(FILE_COMPLETE_MAR, false);
 }
 
 /**
  * Called after the call to setupUpdaterTest finishes.
  */
 function setupUpdaterTestFinished() {
@@ -51,56 +50,28 @@ function runUpdateFinished() {
  */
 function checkPostUpdateAppLogFinished() {
   checkAppBundleModTime();
   checkSymLinks();
   standardInit();
   checkPostUpdateRunningFile(true);
   checkFilesAfterUpdateSuccess(getApplyDirFile, false, true);
   checkUpdateLogContents(LOG_REPLACE_SUCCESS, false, true);
-  checkDistributionDir();
   do_execute_soon(waitForUpdateXMLFiles);
 }
 
 /**
  * Called after the call to waitForUpdateXMLFiles finishes.
  */
 function waitForUpdateXMLFilesFinished() {
   checkUpdateManager(STATE_NONE, false, STATE_SUCCEEDED, 0, 1);
   checkCallbackLog();
 }
 
 /**
- * Setup the state of the distribution directory for the test.
- */
-function setupDistributionDir() {
-  if (IS_MACOSX) {
-    // Create files in the old distribution directory location to verify that
-    // the directory and its contents are removed when there is a distribution
-    // directory in the new location.
-    let testFile = getApplyDirFile(DIR_MACOS + "distribution/testFile", true);
-    writeFile(testFile, "test\n");
-    testFile = getApplyDirFile(DIR_MACOS + "distribution/test1/testFile", true);
-    writeFile(testFile, "test\n");
-  }
-}
-
-/**
- * Checks the state of the distribution directory for the test.
- */
-function checkDistributionDir() {
-  if (IS_MACOSX) {
-    let distributionDir = getApplyDirFile(DIR_MACOS + "distribution", true);
-    Assert.ok(!distributionDir.exists(),
-              MSG_SHOULD_NOT_EXIST + getMsgPath(distributionDir.path));
-    checkUpdateLogContains(REMOVE_OLD_DIST_DIR);
-  }
-}
-
-/**
  * Setup symlinks for the test.
  */
 function setupSymLinks() {
   // Don't test symlinks on Mac OS X in this test since it tends to timeout.
   // It is tested on Mac OS X in marAppInUseStageSuccessComplete_unix.js
   if (IS_UNIX && !IS_MACOSX) {
     removeSymlink();
     createSymlink();
--- a/toolkit/mozapps/update/tests/unit_service_updater/marStageSuccessPartialSvc.js
+++ b/toolkit/mozapps/update/tests/unit_service_updater/marStageSuccessPartialSvc.js
@@ -12,17 +12,16 @@ function run_test() {
     return;
   }
   gTestFiles = gTestFilesPartialSuccess;
   gTestFiles[gTestFiles.length - 2].originalContents = null;
   gTestFiles[gTestFiles.length - 2].compareContents = "FromPartial\n";
   gTestFiles[gTestFiles.length - 2].comparePerms = 0o644;
   gTestDirs = gTestDirsPartialSuccess;
   preventDistributionFiles();
-  setupDistributionDir();
   setupUpdaterTest(FILE_PARTIAL_MAR, true);
 }
 
 /**
  * Called after the call to setupUpdaterTest finishes.
  */
 function setupUpdaterTestFinished() {
   stageUpdate(true);
@@ -50,63 +49,18 @@ function runUpdateFinished() {
  * Called after the call to checkPostUpdateAppLog finishes.
  */
 function checkPostUpdateAppLogFinished() {
   checkAppBundleModTime();
   standardInit();
   checkPostUpdateRunningFile(true);
   checkFilesAfterUpdateSuccess(getApplyDirFile, false, true);
   checkUpdateLogContents(LOG_REPLACE_SUCCESS, false, true, true);
-  checkDistributionDir();
   do_execute_soon(waitForUpdateXMLFiles);
 }
 
 /**
  * Called after the call to waitForUpdateXMLFiles finishes.
  */
 function waitForUpdateXMLFilesFinished() {
   checkUpdateManager(STATE_NONE, false, STATE_SUCCEEDED, 0, 1);
   checkCallbackLog();
 }
-
-/**
- * Setup the state of the distribution directory for the test.
- */
-function setupDistributionDir() {
-  if (IS_MACOSX) {
-    // Create files in the old distribution directory location to verify that
-    // the directory and its contents are moved to the new location on update.
-    let testFile = getApplyDirFile(DIR_MACOS + "distribution/testFile", true);
-    writeFile(testFile, "test\n");
-    testFile = getApplyDirFile(DIR_MACOS + "distribution/test/testFile", true);
-    writeFile(testFile, "test\n");
-  }
-}
-
-/**
- * Checks the state of the distribution directory.
- */
-function checkDistributionDir() {
-  let distributionDir = getApplyDirFile(DIR_RESOURCES + "distribution", true);
-  if (IS_MACOSX) {
-    Assert.ok(distributionDir.exists(),
-              MSG_SHOULD_EXIST + getMsgPath(distributionDir.path));
-
-    let testFile = getApplyDirFile(DIR_RESOURCES + "distribution/testFile", true);
-    Assert.ok(testFile.exists(),
-              MSG_SHOULD_EXIST + getMsgPath(testFile.path));
-
-    testFile = getApplyDirFile(DIR_RESOURCES + "distribution/test/testFile", true);
-    Assert.ok(testFile.exists(),
-              MSG_SHOULD_EXIST + getMsgPath(testFile.path));
-
-    distributionDir = getApplyDirFile(DIR_MACOS + "distribution", true);
-    Assert.ok(!distributionDir.exists(),
-              MSG_SHOULD_NOT_EXIST + getMsgPath(distributionDir.path));
-
-    checkUpdateLogContains(MOVE_OLD_DIST_DIR);
-  } else {
-    debugDump("testing that files aren't added with an add-if instruction " +
-              "when the file's destination directory doesn't exist");
-    Assert.ok(!distributionDir.exists(),
-              MSG_SHOULD_NOT_EXIST + getMsgPath(distributionDir.path));
-  }
-}
--- a/toolkit/mozapps/update/tests/unit_service_updater/marSuccessCompleteSvc.js
+++ b/toolkit/mozapps/update/tests/unit_service_updater/marSuccessCompleteSvc.js
@@ -7,17 +7,16 @@
 
 function run_test() {
   if (!setupTestCommon()) {
     return;
   }
   gTestFiles = gTestFilesCompleteSuccess;
   gTestDirs = gTestDirsCompleteSuccess;
   preventDistributionFiles();
-  setupDistributionDir();
   setupUpdaterTest(FILE_COMPLETE_MAR, true);
 }
 
 /**
  * Called after the call to setupUpdaterTest finishes.
  */
 function setupUpdaterTestFinished() {
   runUpdate(STATE_SUCCEEDED, false, 0, true);
@@ -34,63 +33,18 @@ function runUpdateFinished() {
  * Called after the call to checkPostUpdateAppLog finishes.
  */
 function checkPostUpdateAppLogFinished() {
   checkAppBundleModTime();
   standardInit();
   checkPostUpdateRunningFile(true);
   checkFilesAfterUpdateSuccess(getApplyDirFile);
   checkUpdateLogContents(LOG_COMPLETE_SUCCESS, false, false, true);
-  checkDistributionDir();
   do_execute_soon(waitForUpdateXMLFiles);
 }
 
 /**
  * Called after the call to waitForUpdateXMLFiles finishes.
  */
 function waitForUpdateXMLFilesFinished() {
   checkUpdateManager(STATE_NONE, false, STATE_SUCCEEDED, 0, 1);
   checkCallbackLog();
 }
-
-/**
- * Setup the state of the distribution directory for the test.
- */
-function setupDistributionDir() {
-  if (IS_MACOSX) {
-    // Create files in the old distribution directory location to verify that
-    // the directory and its contents are moved to the new location on update.
-    let testFile = getApplyDirFile(DIR_MACOS + "distribution/testFile", true);
-    writeFile(testFile, "test\n");
-    testFile = getApplyDirFile(DIR_MACOS + "distribution/test/testFile", true);
-    writeFile(testFile, "test\n");
-  }
-}
-
-/**
- * Checks the state of the distribution directory.
- */
-function checkDistributionDir() {
-  let distributionDir = getApplyDirFile(DIR_RESOURCES + "distribution", true);
-  if (IS_MACOSX) {
-    Assert.ok(distributionDir.exists(),
-              MSG_SHOULD_EXIST + getMsgPath(distributionDir.path));
-
-    let testFile = getApplyDirFile(DIR_RESOURCES + "distribution/testFile", true);
-    Assert.ok(testFile.exists(),
-              MSG_SHOULD_EXIST + getMsgPath(testFile.path));
-
-    testFile = getApplyDirFile(DIR_RESOURCES + "distribution/test/testFile", true);
-    Assert.ok(testFile.exists(),
-              MSG_SHOULD_EXIST + getMsgPath(testFile.path));
-
-    distributionDir = getApplyDirFile(DIR_MACOS + "distribution", true);
-    Assert.ok(!distributionDir.exists(),
-              MSG_SHOULD_NOT_EXIST + getMsgPath(distributionDir.path));
-
-    checkUpdateLogContains(MOVE_OLD_DIST_DIR);
-  } else {
-    debugDump("testing that files aren't added with an add-if instruction " +
-              "when the file's destination directory doesn't exist");
-    Assert.ok(!distributionDir.exists(),
-              MSG_SHOULD_NOT_EXIST + getMsgPath(distributionDir.path));
-  }
-}
--- a/toolkit/mozapps/update/tests/unit_service_updater/marSuccessPartialSvc.js
+++ b/toolkit/mozapps/update/tests/unit_service_updater/marSuccessPartialSvc.js
@@ -12,17 +12,16 @@ function run_test() {
   gTestFiles = gTestFilesPartialSuccess;
   gTestFiles[gTestFiles.length - 1].originalContents = null;
   gTestFiles[gTestFiles.length - 1].compareContents = "FromPartial\n";
   gTestFiles[gTestFiles.length - 1].comparePerms = 0o644;
   gTestFiles[gTestFiles.length - 2].originalContents = null;
   gTestFiles[gTestFiles.length - 2].compareContents = "FromPartial\n";
   gTestFiles[gTestFiles.length - 2].comparePerms = 0o644;
   gTestDirs = gTestDirsPartialSuccess;
-  setupDistributionDir();
   // The third parameter will test that a relative path that contains a
   // directory traversal to the post update binary doesn't execute.
   setupUpdaterTest(FILE_PARTIAL_MAR, false, "test/../");
 }
 
 /**
  * Called after the call to setupUpdaterTest finishes.
  */
@@ -34,46 +33,18 @@ function setupUpdaterTestFinished() {
  * Called after the call to runUpdate finishes.
  */
 function runUpdateFinished() {
   checkAppBundleModTime();
   standardInit();
   checkPostUpdateRunningFile(false);
   checkFilesAfterUpdateSuccess(getApplyDirFile);
   checkUpdateLogContents(LOG_PARTIAL_SUCCESS);
-  checkDistributionDir();
   do_execute_soon(waitForUpdateXMLFiles);
 }
 
 /**
  * Called after the call to waitForUpdateXMLFiles finishes.
  */
 function waitForUpdateXMLFilesFinished() {
   checkUpdateManager(STATE_NONE, false, STATE_SUCCEEDED, 0, 1);
   checkCallbackLog();
 }
-
-/**
- * Setup the state of the distribution directory for the test.
- */
-function setupDistributionDir() {
-  if (IS_MACOSX) {
-    // Create files in the old distribution directory location to verify that
-    // the directory and its contents are removed when there is a distribution
-    // directory in the new location.
-    let testFile = getApplyDirFile(DIR_MACOS + "distribution/testFile", true);
-    writeFile(testFile, "test\n");
-    testFile = getApplyDirFile(DIR_MACOS + "distribution/test/testFile", true);
-    writeFile(testFile, "test\n");
-  }
-}
-
-/**
- * Checks the state of the distribution directory.
- */
-function checkDistributionDir() {
-  if (IS_MACOSX) {
-    let distributionDir = getApplyDirFile(DIR_MACOS + "distribution", true);
-    Assert.ok(!distributionDir.exists(),
-              MSG_SHOULD_NOT_EXIST + getMsgPath(distributionDir.path));
-    checkUpdateLogContains(REMOVE_OLD_DIST_DIR);
-  }
-}
--- a/toolkit/mozapps/update/updater/updater.cpp
+++ b/toolkit/mozapps/update/updater/updater.cpp
@@ -8,32 +8,28 @@
  *
  *  contents = 1*( line )
  *  line     = method LWS *( param LWS ) CRLF
  *  CRLF     = "\r\n"
  *  LWS      = 1*( " " | "\t" )
  *
  *  Available methods for the manifest file:
  *
- *  updatev2.manifest
- *  -----------------
- *  method   = "add" | "add-if" | "patch" | "patch-if" | "remove" |
- *             "rmdir" | "rmrfdir" | type
- *
- *  'type' is the update type (e.g. complete or partial) and when present MUST
- *  be the first entry in the update manifest. The type is used to support
- *  downgrades by causing the actions defined in precomplete to be performed.
- *
  *  updatev3.manifest
  *  -----------------
  *  method   = "add" | "add-if" | "add-if-not" | "patch" | "patch-if" |
  *             "remove" | "rmdir" | "rmrfdir" | type
  *
  *  'add-if-not' adds a file if it doesn't exist.
  *
+ *  'type' is the update type (e.g. complete or partial) and when present MUST
+ *  be the first entry in the update manifest. The type is used to support
+ *  removing files that no longer exist when when applying a complete update by
+ *  causing the actions defined in the precomplete file to be performed.
+ *
  *  precomplete
  *  -----------
  *  method   = "remove" | "rmdir"
  */
 #include "bspatch.h"
 #include "progressui.h"
 #include "archivereader.h"
 #include "readstrings.h"
@@ -2486,52 +2482,17 @@ UpdateThreadFunc(void *param)
     NS_tchar dataFile[MAXPATHLEN];
     rv = GetUpdateFileName(dataFile, sizeof(dataFile)/sizeof(dataFile[0]));
     if (rv == OK) {
       rv = gArchiveReader.Open(dataFile);
     }
 
 #ifdef MOZ_VERIFY_MAR_SIGNATURE
     if (rv == OK) {
-#ifdef XP_WIN
-      HKEY baseKey = nullptr;
-      wchar_t valueName[] = L"Image Path";
-      wchar_t rasenh[] = L"rsaenh.dll";
-      bool reset = false;
-      if (RegOpenKeyExW(HKEY_LOCAL_MACHINE,
-                        L"SOFTWARE\\Microsoft\\Cryptography\\Defaults\\Provider\\Microsoft Enhanced Cryptographic Provider v1.0",
-                        0, KEY_READ | KEY_WRITE,
-                        &baseKey) == ERROR_SUCCESS) {
-        wchar_t path[MAX_PATH + 1];
-        DWORD size = sizeof(path);
-        DWORD type;
-        if (RegQueryValueExW(baseKey, valueName, 0, &type,
-                             (LPBYTE)path, &size) == ERROR_SUCCESS) {
-          if (type == REG_SZ && wcscmp(path, rasenh) == 0) {
-            wchar_t rasenhFullPath[] = L"%SystemRoot%\\System32\\rsaenh.dll";
-            if (RegSetValueExW(baseKey, valueName, 0, REG_SZ,
-                               (const BYTE*)rasenhFullPath,
-                               sizeof(rasenhFullPath)) == ERROR_SUCCESS) {
-              reset = true;
-            }
-          }
-        }
-      }
-#endif
       rv = gArchiveReader.VerifySignature();
-#ifdef XP_WIN
-      if (baseKey) {
-        if (reset) {
-          RegSetValueExW(baseKey, valueName, 0, REG_SZ,
-                         (const BYTE*)rasenh,
-                         sizeof(rasenh));
-        }
-        RegCloseKey(baseKey);
-      }
-#endif
     }
 
     if (rv == OK) {
       if (rv == OK) {
         NS_tchar updateSettingsPath[MAX_TEXT_LEN];
         NS_tsnprintf(updateSettingsPath,
                      sizeof(updateSettingsPath) / sizeof(updateSettingsPath[0]),
 #ifdef XP_MACOSX
@@ -3633,54 +3594,16 @@ int NS_main(int argc, NS_tchar **argv)
     } else {
       LOG(("NS_main: failed to schedule OS reboot removal of " \
            "directory: " LOG_S, DELETE_DIR));
     }
   }
 #endif /* XP_WIN */
 
 #ifdef XP_MACOSX
-  // When the update is successful remove the precomplete file in the root of
-  // the application bundle and move the distribution directory from
-  // Contents/MacOS to Contents/Resources and if both exist delete the
-  // directory under Contents/MacOS (see Bug 1068439).
-  if (gSucceeded && !sStagedUpdate) {
-    NS_tchar oldPrecomplete[MAXPATHLEN];
-    NS_tsnprintf(oldPrecomplete, sizeof(oldPrecomplete)/sizeof(oldPrecomplete[0]),
-                 NS_T("%s/precomplete"), gInstallDirPath);
-    NS_tremove(oldPrecomplete);
-
-    NS_tchar oldDistDir[MAXPATHLEN];
-    NS_tsnprintf(oldDistDir, sizeof(oldDistDir)/sizeof(oldDistDir[0]),
-                 NS_T("%s/Contents/MacOS/distribution"), gInstallDirPath);
-    int rv = NS_taccess(oldDistDir, F_OK);
-    if (!rv) {
-      NS_tchar newDistDir[MAXPATHLEN];
-      NS_tsnprintf(newDistDir, sizeof(newDistDir)/sizeof(newDistDir[0]),
-                   NS_T("%s/Contents/Resources/distribution"), gInstallDirPath);
-      rv = NS_taccess(newDistDir, F_OK);
-      if (!rv) {
-        LOG(("New distribution directory already exists... removing old " \
-             "distribution directory: " LOG_S, oldDistDir));
-        rv = ensure_remove_recursive(oldDistDir);
-        if (rv) {
-          LOG(("Removing old distribution directory failed - err: %d", rv));
-        }
-      } else {
-        LOG(("Moving old distribution directory to new location. src: " LOG_S \
-             ", dst:" LOG_S, oldDistDir, newDistDir));
-        rv = rename_file(oldDistDir, newDistDir, true);
-        if (rv) {
-          LOG(("Moving old distribution directory to new location failed - " \
-               "err: %d", rv));
-        }
-      }
-    }
-  }
-
   if (isElevated) {
     SetGroupOwnershipAndPermissions(gInstallDirPath);
     freeArguments(argc, argv);
     CleanupElevatedMacUpdate(false);
   } else if (IsOwnedByGroupAdmin(gInstallDirPath)) {
     // If the group ownership of the Firefox .app bundle was set to the "admin"
     // group during a previous elevated update, we need to ensure that all files
     // in the bundle have group ownership of "admin" as well as write permission
@@ -4211,21 +4134,18 @@ int DoUpdate()
   NS_tchar manifest[MAXPATHLEN];
   NS_tsnprintf(manifest, sizeof(manifest)/sizeof(manifest[0]),
                NS_T("%s/updating/update.manifest"), gWorkingDirPath);
   ensure_parent_dir(manifest);
 
   // extract the manifest
   int rv = gArchiveReader.ExtractFile("updatev3.manifest", manifest);
   if (rv) {
-    rv = gArchiveReader.ExtractFile("updatev2.manifest", manifest);
-    if (rv) {
-      LOG(("DoUpdate: error extracting manifest file"));
-      return rv;
-    }
+    LOG(("DoUpdate: error extracting manifest file"));
+    return rv;
   }
 
   NS_tchar *rb = GetManifestContents(manifest);
   NS_tremove(manifest);
   if (rb == nullptr) {
     LOG(("DoUpdate: error opening manifest file: " LOG_S, manifest));
     return READ_ERROR;
   }