Bug 1277562 - Part 9: Add Wasm Tier 2 compilation tasks. r=luke
authorLars T Hansen <lhansen@mozilla.com>
Thu, 09 Feb 2017 15:15:17 +0100
changeset 425127 0f4d52995594cc5c2d302c11b34088e5e5174fb2
parent 425126 21129f558137d87de170fbed432ab4b6206b9ff3
child 425128 b8b7771cce0d66e6fbfec285a84ef1b7e2e3ff0d
push id7761
push userjlund@mozilla.com
push dateFri, 15 Sep 2017 00:19:52 +0000
treeherdermozilla-beta@c38455951db4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersluke
bugs1277562
milestone57.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1277562 - Part 9: Add Wasm Tier 2 compilation tasks. r=luke
js/public/Utility.h
js/src/vm/HelperThreads.cpp
js/src/vm/HelperThreads.h
js/src/vm/MutexIDs.h
js/src/wasm/AsmJS.cpp
js/src/wasm/WasmCode.h
js/src/wasm/WasmCompile.cpp
js/src/wasm/WasmCompile.h
js/src/wasm/WasmGenerator.cpp
js/src/wasm/WasmGenerator.h
js/src/wasm/WasmJS.cpp
js/src/wasm/WasmModule.cpp
js/src/wasm/WasmModule.h
--- a/js/public/Utility.h
+++ b/js/public/Utility.h
@@ -61,16 +61,17 @@ enum ThreadType {
     THREAD_TYPE_WASM,           // 2
     THREAD_TYPE_ION,            // 3
     THREAD_TYPE_PARSE,          // 4
     THREAD_TYPE_COMPRESS,       // 5
     THREAD_TYPE_GCHELPER,       // 6
     THREAD_TYPE_GCPARALLEL,     // 7
     THREAD_TYPE_PROMISE_TASK,   // 8
     THREAD_TYPE_ION_FREE,       // 9
+    THREAD_TYPE_WASM_TIER2,     // 10
     THREAD_TYPE_MAX             // Used to check shell function arguments
 };
 
 namespace oom {
 
 /*
  * Theads are tagged only in certain debug contexts.  Notably, to make testing
  * OOM in certain helper threads more effective, we allow restricting the OOM
--- a/js/src/vm/HelperThreads.cpp
+++ b/js/src/vm/HelperThreads.cpp
@@ -97,16 +97,34 @@ js::StartOffThreadWasmCompile(wasm::Comp
     if (!HelperThreadState().wasmWorklist(lock, mode).append(task))
         return false;
 
     HelperThreadState().notifyOne(GlobalHelperThreadState::PRODUCER, lock);
     return true;
 }
 
 bool
+js::StartOffThreadWasmTier2Generator(wasm::Tier2GeneratorTask* task)
+{
+    AutoLockHelperThreadState lock;
+
+    if (!HelperThreadState().wasmTier2GeneratorWorklist(lock).append(task))
+        return false;
+
+    HelperThreadState().notifyOne(GlobalHelperThreadState::PRODUCER, lock);
+    return true;
+}
+
+void
+js::CancelOffThreadWasmTier2Generator()
+{
+    // TODO: Implement this
+}
+
+bool
 js::StartOffThreadIonCompile(JSContext* cx, jit::IonBuilder* builder)
 {
     AutoLockHelperThreadState lock;
 
     if (!HelperThreadState().ionWorklist(lock).append(builder))
         return false;
 
     HelperThreadState().notifyOne(GlobalHelperThreadState::PRODUCER, lock);
@@ -875,16 +893,17 @@ GlobalHelperThreadState::GlobalHelperThr
     threadCount = ThreadCountForCPUCount(cpuCount);
 
     MOZ_ASSERT(cpuCount > 0, "GetCPUCount() seems broken");
 }
 
 void
 GlobalHelperThreadState::finish()
 {
+    CancelOffThreadWasmTier2Generator();
     finishThreads();
 
     // Make sure there are no Ion free tasks left. We check this here because,
     // unlike the other tasks, we don't explicitly block on this when
     // destroying a runtime.
     AutoLockHelperThreadState lock;
     auto& freeList = ionFreeList(lock);
     while (!freeList.empty())
@@ -955,16 +974,17 @@ GlobalHelperThreadState::hasActiveThread
 
     return false;
 }
 
 void
 GlobalHelperThreadState::waitForAllThreads()
 {
     CancelOffThreadIonCompile();
+    CancelOffThreadWasmTier2Generator();
 
     AutoLockHelperThreadState lock;
     while (hasActiveThreads(lock))
         wait(lock, CONSUMER);
 }
 
 // A task can be a "master" task, ie, it will block waiting for other worker
 // threads that perform work on its behalf.  If so it must not take the last
@@ -1057,16 +1077,22 @@ size_t
 GlobalHelperThreadState::maxWasmCompilationThreads() const
 {
     if (IsHelperThreadSimulatingOOM(js::THREAD_TYPE_WASM))
         return 1;
     return cpuCount;
 }
 
 size_t
+GlobalHelperThreadState::maxWasmTier2GeneratorThreads() const
+{
+    return MaxTier2GeneratorTasks;
+}
+
+size_t
 GlobalHelperThreadState::maxParseThreads() const
 {
     if (IsHelperThreadSimulatingOOM(js::THREAD_TYPE_PARSE))
         return 1;
 
     // Don't allow simultaneous off thread parses, to reduce contention on the
     // atoms table. Note that wasm compilation depends on this to avoid
     // stalling the helper thread, as off thread parse tasks can trigger and
@@ -1104,25 +1130,57 @@ GlobalHelperThreadState::maxGCParallelTh
 bool
 GlobalHelperThreadState::canStartWasmCompile(const AutoLockHelperThreadState& lock,
                                              wasm::CompileMode mode)
 {
     // Don't execute a wasm job if an earlier one failed.
     if (wasmWorklist(lock, mode).empty() || wasmFailed(lock, mode))
         return false;
 
-    // Honor the maximum allowed threads to compile wasm jobs at once,
-    // to avoid oversaturating the machine.
-    if (!checkTaskThreadLimit<wasm::CompileTask*>(maxWasmCompilationThreads()))
+    // For Tier1 and Once compilation, honor the maximum allowed threads to
+    // compile wasm jobs at once, to avoid oversaturating the machine.
+    //
+    // For Tier2 compilation we need to allow other things to happen too, so for
+    // now we only allow one thread.
+    //
+    // TODO: We should investigate more intelligent strategies, see bug 1380033.
+    //
+    // If Tier2 is very backlogged we must give priority to it, since the Tier2
+    // queue holds onto Tier1 tasks.  Indeed if Tier2 is backlogged we will
+    // devote more resources to Tier2 and not start any Tier1 work at all.
+
+    bool tier2oversubscribed = wasmTier2GeneratorWorklist(lock).length() > 20;
+
+    size_t threads;
+    if (mode == wasm::CompileMode::Tier2) {
+        if (tier2oversubscribed)
+            threads = maxWasmCompilationThreads();
+        else
+            threads = 1;
+    } else {
+        if (tier2oversubscribed)
+            threads = 0;
+        else
+            threads = maxWasmCompilationThreads();
+    }
+
+    if (!threads || !checkTaskThreadLimit<wasm::CompileTask*>(threads))
         return false;
 
     return true;
 }
 
 bool
+GlobalHelperThreadState::canStartWasmTier2Generator(const AutoLockHelperThreadState& lock)
+{
+    return !wasmTier2GeneratorWorklist(lock).empty() &&
+           checkTaskThreadLimit<wasm::Tier2GeneratorTask*>(maxWasmTier2GeneratorThreads());
+}
+
+bool
 GlobalHelperThreadState::canStartPromiseHelperTask(const AutoLockHelperThreadState& lock)
 {
     return !promiseHelperTasks(lock).empty();
 }
 
 static bool
 IonBuilderHasHigherPriority(jit::IonBuilder* first, jit::IonBuilder* second)
 {
@@ -1754,16 +1812,44 @@ HelperThread::handleWasmWorkload(AutoLoc
     }
 
     // Notify the active thread in case it's waiting.
     HelperThreadState().notifyAll(GlobalHelperThreadState::CONSUMER, locked);
     currentTask.reset();
 }
 
 void
+HelperThread::handleWasmTier2GeneratorWorkload(AutoLockHelperThreadState& locked)
+{
+    MOZ_ASSERT(HelperThreadState().canStartWasmTier2Generator(locked));
+    MOZ_ASSERT(idle());
+
+    currentTask.emplace(HelperThreadState().wasmTier2GeneratorWorklist(locked).popCopy());
+    bool success = false;
+
+    wasm::Tier2GeneratorTask* task = wasmTier2GeneratorTask();
+    {
+        AutoUnlockHelperThreadState unlock(locked);
+        success = wasm::GenerateTier2(task);
+    }
+
+    // We silently ignore failures.  Such failures must be resource exhaustion,
+    // because all error checking was performed by the initial compilation.
+    mozilla::Unused << success;
+
+    // During shutdown the main thread will wait for any ongoing (cancelled)
+    // tier-2 generation to shut down normally.  To do so, it waits on the
+    // CONSUMER condition for the count of finished generators to rise.
+    HelperThreadState().notifyAll(GlobalHelperThreadState::CONSUMER, locked);
+
+    wasm::DeleteTier2GeneratorTask(task);
+    currentTask.reset();
+}
+
+void
 HelperThread::handlePromiseHelperTaskWorkload(AutoLockHelperThreadState& locked)
 {
     MOZ_ASSERT(HelperThreadState().canStartPromiseHelperTask(locked));
     MOZ_ASSERT(idle());
 
     PromiseHelperTask* task = HelperThreadState().promiseHelperTasks(locked).popCopy();
     currentTask.emplace(task);
 
@@ -2200,16 +2286,18 @@ HelperThread::threadLoop()
                 task = js::THREAD_TYPE_PARSE;
             } else if (HelperThreadState().canStartCompressionTask(lock)) {
                 task = js::THREAD_TYPE_COMPRESS;
             } else if (HelperThreadState().canStartIonFreeTask(lock)) {
                 task = js::THREAD_TYPE_ION_FREE;
             } else if (HelperThreadState().canStartWasmCompile(lock, wasm::CompileMode::Tier2)) {
                 task = js::THREAD_TYPE_WASM;
                 tier = wasm::CompileMode::Tier2;
+            } else if (HelperThreadState().canStartWasmTier2Generator(lock)) {
+                task = js::THREAD_TYPE_WASM_TIER2;
             } else {
                 task = js::THREAD_TYPE_NONE;
             }
 
             if (task != js::THREAD_TYPE_NONE)
                 break;
 
             HelperThreadState().wait(lock, GlobalHelperThreadState::PRODUCER);
@@ -2236,14 +2324,17 @@ HelperThread::threadLoop()
             handleParseWorkload(lock);
             break;
           case js::THREAD_TYPE_COMPRESS:
             handleCompressionWorkload(lock);
             break;
           case js::THREAD_TYPE_ION_FREE:
             handleIonFreeWorkload(lock);
             break;
+          case js::THREAD_TYPE_WASM_TIER2:
+            handleWasmTier2GeneratorWorkload(lock);
+            break;
           default:
             MOZ_CRASH("No task to perform");
         }
         js::oom::SetThreadType(js::THREAD_TYPE_NONE);
     }
 }
--- a/js/src/vm/HelperThreads.h
+++ b/js/src/vm/HelperThreads.h
@@ -41,17 +41,19 @@ struct ParseTask;
 struct PromiseHelperTask;
 namespace jit {
   class IonBuilder;
 } // namespace jit
 namespace wasm {
   class FuncIR;
   class FunctionCompileResults;
   class CompileTask;
+  struct Tier2GeneratorTask;
   typedef Vector<CompileTask*, 0, SystemAllocPolicy> CompileTaskPtrVector;
+  typedef Vector<Tier2GeneratorTask*, 0, SystemAllocPolicy> Tier2GeneratorTaskPtrVector;
 } // namespace wasm
 
 enum class ParseTaskKind
 {
     Script,
     Module,
     ScriptDecode,
     MultiScriptsDecode
@@ -59,16 +61,20 @@ enum class ParseTaskKind
 
 // Per-process state for off thread work items.
 class GlobalHelperThreadState
 {
     friend class AutoLockHelperThreadState;
     friend class AutoUnlockHelperThreadState;
 
   public:
+    // A single tier-2 ModuleGenerator job spawns many compilation jobs, and we
+    // do not want to allow more than one such ModuleGenerator to run at a time.
+    static const size_t MaxTier2GeneratorTasks = 1;
+
     // Number of CPUs to treat this machine as having when creating threads.
     // May be accessed without locking.
     size_t cpuCount;
 
     // Number of threads to create. May be accessed without locking.
     size_t threadCount;
 
     typedef Vector<jit::IonBuilder*, 0, SystemAllocPolicy> IonBuilderVector;
@@ -86,16 +92,17 @@ class GlobalHelperThreadState
     // The lists below are all protected by |lock|.
 
     // Ion compilation worklist and finished jobs.
     IonBuilderVector ionWorklist_, ionFinishedList_, ionFreeList_;
 
     // wasm worklist and finished jobs.
     wasm::CompileTaskPtrVector wasmWorklist_tier1_, wasmFinishedList_tier1_;
     wasm::CompileTaskPtrVector wasmWorklist_tier2_, wasmFinishedList_tier2_;
+    wasm::Tier2GeneratorTaskPtrVector wasmTier2GeneratorWorklist_;
 
     // For now, only allow a single parallel wasm compilation at each tier to
     // happen at a time.  This avoids race conditions on
     // wasmWorklist/wasmFinishedList/etc, for which there is one for each tier.
     //
     // TODO: remove this restriction by making the work and finish lists
     // per-compilation state, not part of the global state.
 
@@ -142,16 +149,17 @@ class GlobalHelperThreadState
     GCParallelTaskVector gcParallelWorklist_;
 
     ParseTask* removeFinishedParseTask(ParseTaskKind kind, void* token);
 
   public:
     size_t maxIonCompilationThreads() const;
     size_t maxUnpausedIonCompilationThreads() const;
     size_t maxWasmCompilationThreads() const;
+    size_t maxWasmTier2GeneratorThreads() const;
     size_t maxParseThreads() const;
     size_t maxCompressionThreads() const;
     size_t maxGCHelperThreads() const;
     size_t maxGCParallelThreads() const;
 
     GlobalHelperThreadState();
 
     bool ensureInitialized();
@@ -224,16 +232,20 @@ class GlobalHelperThreadState
             return wasmFinishedList_tier1_;
           case wasm::CompileMode::Tier2:
             return wasmFinishedList_tier2_;
           default:
             MOZ_CRASH();
         }
     }
 
+    wasm::Tier2GeneratorTaskPtrVector& wasmTier2GeneratorWorklist(const AutoLockHelperThreadState&) {
+        return wasmTier2GeneratorWorklist_;
+    }
+
     PromiseHelperTaskVector& promiseHelperTasks(const AutoLockHelperThreadState&) {
         return promiseHelperTasks_;
     }
 
     ParseTaskVector& parseWorklist(const AutoLockHelperThreadState&) {
         return parseWorklist_;
     }
     ParseTaskVector& parseFinishedList(const AutoLockHelperThreadState&) {
@@ -259,16 +271,17 @@ class GlobalHelperThreadState
         return gcHelperWorklist_;
     }
 
     GCParallelTaskVector& gcParallelWorklist(const AutoLockHelperThreadState&) {
         return gcParallelWorklist_;
     }
 
     bool canStartWasmCompile(const AutoLockHelperThreadState& lock, wasm::CompileMode mode);
+    bool canStartWasmTier2Generator(const AutoLockHelperThreadState& lock);
     bool canStartPromiseHelperTask(const AutoLockHelperThreadState& lock);
     bool canStartIonCompile(const AutoLockHelperThreadState& lock);
     bool canStartIonFreeTask(const AutoLockHelperThreadState& lock);
     bool canStartParseTask(const AutoLockHelperThreadState& lock);
     bool canStartCompressionTask(const AutoLockHelperThreadState& lock);
     bool canStartGCHelperTask(const AutoLockHelperThreadState& lock);
     bool canStartGCParallelTask(const AutoLockHelperThreadState& lock);
 
@@ -435,16 +448,17 @@ HelperThreadState()
     extern GlobalHelperThreadState* gHelperThreadState;
 
     MOZ_ASSERT(gHelperThreadState);
     return *gHelperThreadState;
 }
 
 typedef mozilla::Variant<jit::IonBuilder*,
                          wasm::CompileTask*,
+                         wasm::Tier2GeneratorTask*,
                          PromiseHelperTask*,
                          ParseTask*,
                          SourceCompressionTask*,
                          GCHelperState*,
                          GCParallelTask*> HelperTaskUnion;
 
 /* Individual helper thread, one allocated per core. */
 struct HelperThread
@@ -476,16 +490,20 @@ struct HelperThread
         return maybeCurrentTaskAs<jit::IonBuilder*>();
     }
 
     /* Any wasm data currently being optimized on this thread. */
     wasm::CompileTask* wasmTask() {
         return maybeCurrentTaskAs<wasm::CompileTask*>();
     }
 
+    wasm::Tier2GeneratorTask* wasmTier2GeneratorTask() {
+        return maybeCurrentTaskAs<wasm::Tier2GeneratorTask*>();
+    }
+
     /* Any source being parsed/emitted on this thread. */
     ParseTask* parseTask() {
         return maybeCurrentTaskAs<ParseTask*>();
     }
 
     /* Any source being compressed on this thread. */
     SourceCompressionTask* compressionTask() {
         return maybeCurrentTaskAs<SourceCompressionTask*>();
@@ -511,16 +529,17 @@ struct HelperThread
     T maybeCurrentTaskAs() {
         if (currentTask.isSome() && currentTask->is<T>())
             return currentTask->as<T>();
 
         return nullptr;
     }
 
     void handleWasmWorkload(AutoLockHelperThreadState& locked, wasm::CompileMode mode);
+    void handleWasmTier2GeneratorWorkload(AutoLockHelperThreadState& locked);
     void handlePromiseHelperTaskWorkload(AutoLockHelperThreadState& locked);
     void handleIonWorkload(AutoLockHelperThreadState& locked);
     void handleIonFreeWorkload(AutoLockHelperThreadState& locked);
     void handleParseWorkload(AutoLockHelperThreadState& locked);
     void handleCompressionWorkload(AutoLockHelperThreadState& locked);
     void handleGCHelperWorkload(AutoLockHelperThreadState& locked);
     void handleGCParallelWorkload(AutoLockHelperThreadState& locked);
 };
@@ -551,22 +570,35 @@ CurrentHelperThread();
 // Pause the current thread until it's pause flag is unset.
 void
 PauseCurrentHelperThread();
 
 // Enqueues a wasm compilation task.
 bool
 StartOffThreadWasmCompile(wasm::CompileTask* task, wasm::CompileMode mode);
 
+// Enqueues a wasm compilation task.
+bool
+StartOffThreadWasmTier2Generator(wasm::Tier2GeneratorTask* task);
+
+// Cancel all background Wasm Tier-2 compilations.
+void
+CancelOffThreadWasmTier2Generator();
+
 namespace wasm {
 
 // Performs MIR optimization and LIR generation on one or several functions.
 MOZ_MUST_USE bool
 CompileFunction(CompileTask* task, UniqueChars* error);
 
+MOZ_MUST_USE bool
+GenerateTier2(Tier2GeneratorTask* task);
+
+void
+DeleteTier2GeneratorTask(Tier2GeneratorTask* task);
 }
 
 /*
  * If helper threads are available, call execute() then dispatchResolve() on the
  * given task in a helper thread. If no helper threads are available, the given
  * task is executed and resolved synchronously.
  */
 bool
--- a/js/src/vm/MutexIDs.h
+++ b/js/src/vm/MutexIDs.h
@@ -38,16 +38,17 @@
   _(PerfSpewer,                  500) \
   _(CacheIRSpewer,               500) \
   _(TraceLoggerThreadState,      500) \
   _(DateTimeInfoMutex,           500) \
   _(IcuTimeZoneStateMutex,       500) \
   _(ProcessExecutableRegion,     500) \
   _(WasmCodeProfilingLabels,     500) \
   _(OffThreadPromiseState,       500) \
+  _(WasmTier2GeneratorComplete,  500) \
                                       \
   _(TraceLoggerGraphState,       600) \
   _(VTuneLock,                   600)
 
 namespace js {
 namespace mutexid {
 
 #define DEFINE_MUTEX_ID(name, order)  \
--- a/js/src/wasm/AsmJS.cpp
+++ b/js/src/wasm/AsmJS.cpp
@@ -1858,34 +1858,34 @@ class MOZ_STACK_CLASS ModuleValidator
         ScriptedCaller scriptedCaller;
         if (parser_.ss->filename()) {
             scriptedCaller.line = scriptedCaller.column = 0;  // unused
             scriptedCaller.filename = DuplicateString(parser_.ss->filename());
             if (!scriptedCaller.filename)
                 return false;
         }
 
-        CompileArgs args;
-        if (!args.initFromContext(cx_, Move(scriptedCaller)))
+        MutableCompileArgs args = cx_->new_<CompileArgs>();
+        if (!args || !args->initFromContext(cx_, Move(scriptedCaller)))
             return false;
 
         auto env = MakeUnique<ModuleEnvironment>(ModuleKind::AsmJS);
         if (!env ||
             !env->sigs.resize(AsmJSMaxTypes) ||
             !env->funcSigs.resize(AsmJSMaxFuncs) ||
             !env->funcImportGlobalDataOffsets.resize(AsmJSMaxImports) ||
             !env->tables.resize(AsmJSMaxTables) ||
             !env->asmJSSigToTableIndex.resize(AsmJSMaxTypes))
         {
             return false;
         }
 
         env->minMemoryLength = RoundUpToNextValidAsmJSHeapLength(0);
 
-        if (!mg_.init(Move(env), args, CompileMode::Once, asmJSMetadata_.get()))
+        if (!mg_.init(Move(env), *args, CompileMode::Once, asmJSMetadata_.get()))
             return false;
 
         return true;
     }
 
     JSContext* cx() const                    { return cx_; }
     PropertyName* moduleFunctionName() const { return moduleFunctionName_; }
     PropertyName* globalArgumentName() const { return globalArgumentName_; }
@@ -2397,21 +2397,21 @@ class MOZ_STACK_CLASS ModuleValidator
 
         TokenPos pos;
         JS_ALWAYS_TRUE(tokenStream().peekTokenPos(&pos, TokenStream::Operand));
         uint32_t endAfterCurly = pos.end;
         asmJSMetadata_->srcLengthWithRightBrace = endAfterCurly - asmJSMetadata_->srcStart;
 
         // asm.js does not have any wasm bytecode to save; view-source is
         // provided through the ScriptSource.
-        SharedBytes bytes = js_new<ShareableBytes>();
+        SharedBytes bytes = cx_->new_<ShareableBytes>();
         if (!bytes)
             return nullptr;
 
-        return mg_.finish(*bytes);
+        return mg_.finishModule(*bytes);
     }
 };
 
 /*****************************************************************************/
 // Numeric literal utilities
 
 static bool
 IsNumericNonFloatLiteral(ParseNode* pn)
--- a/js/src/wasm/WasmCode.h
+++ b/js/src/wasm/WasmCode.h
@@ -388,16 +388,22 @@ class Metadata : public ShareableBase<Me
     void commitTier2() const;
     bool hasTier2() const { return hasTier2_; }
     void setTier2(UniqueMetadataTier metadata) const;
     Tiers tiers() const;
 
     const MetadataTier& metadata(Tier t) const;
     MetadataTier& metadata(Tier t);
 
+    UniquePtr<MetadataTier> takeMetadata(Tier tier) {
+        MOZ_ASSERT(!hasTier2());
+        MOZ_ASSERT(metadata1_->tier == tier);
+        return Move(metadata1_);
+    }
+
     SigWithIdVector       sigIds;
     GlobalDescVector      globals;
     TableDescVector       tables;
     NameInBytecodeVector  funcNames;
     CustomSectionVector   customSections;
     CacheableChars        filename;
     ModuleHash            hash;
 
--- a/js/src/wasm/WasmCompile.cpp
+++ b/js/src/wasm/WasmCompile.cpp
@@ -13,16 +13,19 @@
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 #include "wasm/WasmCompile.h"
 
+#include "mozilla/Maybe.h"
+#include "mozilla/Unused.h"
+
 #include "jsprf.h"
 
 #include "wasm/WasmBaselineCompile.h"
 #include "wasm/WasmBinaryIterator.h"
 #include "wasm/WasmGenerator.h"
 #include "wasm/WasmSignalHandlers.h"
 #include "wasm/WasmValidate.h"
 
@@ -120,32 +123,38 @@ CompilerAvailability(ModuleKind kind, co
     // that don't have Ion at all, so this can happen if the user has disabled
     // both compilers or if she has disabled Ion but baseline can't compile the
     // code.
 
     if (!(*baselineEnabled || *ionEnabled))
         *ionEnabled = true;
 }
 
+static bool
+BackgroundWorkPossible()
+{
+    return CanUseExtraThreads() && HelperThreadState().cpuCount > 1;
+}
+
 bool
 wasm::GetDebugEnabled(const CompileArgs& args, ModuleKind kind)
 {
     bool baselineEnabled, debugEnabled, ionEnabled;
     CompilerAvailability(kind, args, &baselineEnabled, &debugEnabled, &ionEnabled);
 
     return debugEnabled;
 }
 
 wasm::CompileMode
 wasm::GetInitialCompileMode(const CompileArgs& args, ModuleKind kind)
 {
     bool baselineEnabled, debugEnabled, ionEnabled;
     CompilerAvailability(kind, args, &baselineEnabled, &debugEnabled, &ionEnabled);
 
-    return (baselineEnabled && ionEnabled && !debugEnabled)
+    return BackgroundWorkPossible() && baselineEnabled && ionEnabled && !debugEnabled
            ? CompileMode::Tier1
            : CompileMode::Once;
 }
 
 wasm::Tier
 wasm::GetTier(const CompileArgs& args, CompileMode compileMode, ModuleKind kind)
 {
     bool baselineEnabled, debugEnabled, ionEnabled;
@@ -163,38 +172,111 @@ wasm::GetTier(const CompileArgs& args, C
       case CompileMode::Once:
         return (debugEnabled || !ionEnabled) ? Tier::Baseline : Tier::Ion;
 
       default:
         MOZ_CRASH("Bad mode");
     }
 }
 
+namespace js {
+namespace wasm {
+
+struct Tier2GeneratorTask
+{
+    // The module that wants the results of the compilation
+    SharedModule            module;
+
+    // The arguments for the compilation
+    SharedCompileArgs       compileArgs;
+
+    Tier2GeneratorTask(Module& module, const CompileArgs& compileArgs)
+      : module(&module),
+        compileArgs(&compileArgs)
+    {}
+};
+
+}
+}
+
+static bool
+Compile(ModuleGenerator& mg, const ShareableBytes& bytecode, const CompileArgs& args,
+        UniqueChars* error, CompileMode compileMode)
+{
+    auto env = js::MakeUnique<ModuleEnvironment>();
+    if (!env)
+        return false;
+
+    Decoder d(bytecode.bytes, error);
+    if (!DecodeModuleEnvironment(d, env.get()))
+        return false;
+
+    if (!mg.init(Move(env), args, compileMode))
+        return false;
+
+    if (!DecodeCodeSection(d, mg))
+        return false;
+
+    if (!DecodeModuleTail(d, &mg.mutableEnv()))
+        return false;
+
+    return true;
+}
+
+
 SharedModule
 wasm::Compile(const ShareableBytes& bytecode, const CompileArgs& args, UniqueChars* error)
 {
     MOZ_RELEASE_ASSERT(wasm::HaveSignalHandlers());
 
-    Decoder d(bytecode.bytes, error);
-
-    auto env = js::MakeUnique<ModuleEnvironment>();
-    if (!env)
-        return nullptr;
-
-    if (!DecodeModuleEnvironment(d, env.get()))
-        return nullptr;
+    ModuleGenerator mg(error);
 
-    CompileMode compileMode = GetInitialCompileMode(args);
-
-    ModuleGenerator mg(error);
-    if (!mg.init(Move(env), args, compileMode))
-        return nullptr;
-
-    if (!DecodeCodeSection(d, mg))
-        return nullptr;
-
-    if (!DecodeModuleTail(d, &mg.mutableEnv()))
+    CompileMode mode = GetInitialCompileMode(args);
+    if (!::Compile(mg, bytecode, args, error, mode))
         return nullptr;
 
     MOZ_ASSERT(!*error, "unreported error in decoding");
 
-    return mg.finish(bytecode);
+    SharedModule module = mg.finishModule(bytecode);
+    if (!module)
+        return nullptr;
+
+    if (mode == CompileMode::Tier1) {
+        MOZ_ASSERT(BackgroundWorkPossible());
+
+        auto task = js::MakeUnique<Tier2GeneratorTask>(*module, args);
+        if (!task) {
+            module->unblockOnTier2GeneratorFinished(CompileMode::Once);
+            return nullptr;
+        }
+
+        if (!StartOffThreadWasmTier2Generator(&*task)) {
+            module->unblockOnTier2GeneratorFinished(CompileMode::Once);
+            return nullptr;
+        }
+
+        mozilla::Unused << task.release();
+    }
+
+    return module;
 }
+
+// This runs on a helper thread.
+bool
+wasm::GenerateTier2(Tier2GeneratorTask* task)
+{
+    UniqueChars     error;
+    ModuleGenerator mg(&error);
+
+    bool res =
+        ::Compile(mg, task->module->bytecode(), *task->compileArgs, &error, CompileMode::Tier2) &&
+        mg.finishTier2(task->module->bytecode(), task->module);
+
+    task->module->unblockOnTier2GeneratorFinished(res ? CompileMode::Tier2 : CompileMode::Once);
+
+    return res;
+}
+
+void
+wasm::DeleteTier2GeneratorTask(Tier2GeneratorTask* task)
+{
+    js_delete(task);
+}
--- a/js/src/wasm/WasmCompile.h
+++ b/js/src/wasm/WasmCompile.h
@@ -30,17 +30,17 @@ struct ScriptedCaller
 {
     UniqueChars filename;
     unsigned line;
     unsigned column;
 };
 
 // Describes all the parameters that control wasm compilation.
 
-struct CompileArgs
+struct CompileArgs : ShareableBase<CompileArgs>
 {
     Assumptions assumptions;
     ScriptedCaller scriptedCaller;
     bool baselineEnabled;
     bool debugEnabled;
     bool ionEnabled;
 
     CompileArgs(Assumptions&& assumptions, ScriptedCaller&& scriptedCaller)
@@ -52,16 +52,19 @@ struct CompileArgs
     {}
 
     // If CompileArgs is constructed without arguments, initFromContext() must
     // be called to complete initialization.
     CompileArgs() = default;
     bool initFromContext(JSContext* cx, ScriptedCaller&& scriptedCaller);
 };
 
+typedef RefPtr<CompileArgs> MutableCompileArgs;
+typedef RefPtr<const CompileArgs> SharedCompileArgs;
+
 // Compile the given WebAssembly bytecode with the given arguments into a
 // wasm::Module. On success, the Module is returned. On failure, the returned
 // SharedModule pointer is null and either:
 //  - *error points to a string description of the error
 //  - *error is null and the caller should report out-of-memory.
 
 SharedModule
 Compile(const ShareableBytes& bytecode, const CompileArgs& args, UniqueChars* error);
@@ -70,20 +73,21 @@ Compile(const ShareableBytes& bytecode, 
 // configuration options, and the nature of the module.  Note debugging can be
 // unavailable even if selected, if Rabaldr is unavailable or the module is not
 // compilable by Rabaldr.
 
 bool
 GetDebugEnabled(const CompileArgs& args, ModuleKind kind = ModuleKind::Wasm);
 
 // Select the mode for the initial compilation of a module.  The mode is "Tier1"
-// precisely if both compilers are available and we're not debugging, and in
-// that case, we'll compile twice, with the mode set to "Tier2" during the
-// second compilation.  Otherwise, the tier is "Once" and we'll compile once,
-// with the appropriate compiler.
+// precisely if both compilers are available, we're not debugging, and it is
+// possible to compile in the background, and in that case, we'll compile twice,
+// with the mode set to "Tier2" during the second (background) compilation.
+// Otherwise, the tier is "Once" and we'll compile once, with the appropriate
+// compiler.
 
 CompileMode
 GetInitialCompileMode(const CompileArgs& args, ModuleKind kind = ModuleKind::Wasm);
 
 // Select the tier for a compilation.  The tier is Tier::Baseline if we're
 // debugging, if Baldr is not available, or if both compilers are are available
 // and the compileMode is Tier1; otherwise the tier is Tier::Ion.
 
--- a/js/src/wasm/WasmGenerator.cpp
+++ b/js/src/wasm/WasmGenerator.cpp
@@ -1128,32 +1128,23 @@ ModuleGenerator::generateBytecodeHash(co
 {
     mozilla::SHA1Sum::Hash hash;
     mozilla::SHA1Sum sha1Sum;
     sha1Sum.update(bytecode.begin(), bytecode.length());
     sha1Sum.finish(hash);
     memcpy(metadata_->hash, hash, sizeof(ModuleHash));
 }
 
-SharedModule
-ModuleGenerator::finish(const ShareableBytes& bytecode)
+bool
+ModuleGenerator::finishMetadata(const ShareableBytes& bytecode)
 {
-    MOZ_ASSERT(!activeFuncDef_);
-    MOZ_ASSERT(finishedFuncDefs_);
-
-    if (!finishFuncExports())
-        return nullptr;
-
-    if (!finishCodegen())
-        return nullptr;
-
     // Convert the CallSiteAndTargetVector (needed during generation) to a
     // CallSiteVector (what is stored in the Module).
     if (!metadataTier_->callSites.appendAll(masm_.callSites()))
-        return nullptr;
+        return false;
 
     // The MacroAssembler has accumulated all the memory accesses during codegen.
     metadataTier_->memoryAccesses = masm_.extractMemoryAccesses();
 
     // Copy over data from the ModuleEnvironment.
     metadata_->memoryUsage = env_->memoryUsage;
     metadata_->minMemoryLength = env_->minMemoryLength;
     metadata_->maxMemoryLength = env_->maxMemoryLength;
@@ -1174,17 +1165,37 @@ ModuleGenerator::finish(const ShareableB
     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 nullptr;
+        return false;
+
+    generateBytecodeHash(bytecode);
+
+    return true;
+}
+
+bool
+ModuleGenerator::finishCommon(const ShareableBytes& bytecode)
+{
+    MOZ_ASSERT(!activeFuncDef_);
+    MOZ_ASSERT(finishedFuncDefs_);
+
+    if (!finishFuncExports())
+        return false;
+
+    if (!finishCodegen())
+        return false;
+
+    if (!finishMetadata(bytecode))
+        return false;
 
     // Assert CodeRanges are sorted.
 #ifdef DEBUG
     uint32_t lastEnd = 0;
     for (const CodeRange& codeRange : metadataTier_->codeRanges) {
         MOZ_ASSERT(codeRange.begin() >= lastEnd);
         lastEnd = codeRange.end();
     }
@@ -1195,19 +1206,28 @@ ModuleGenerator::finish(const ShareableB
     uint32_t lastOffset = 0;
     for (uint32_t debugTrapFarJumpOffset : metadataTier_->debugTrapFarJumpOffsets) {
         MOZ_ASSERT(debugTrapFarJumpOffset >= lastOffset);
         lastOffset = debugTrapFarJumpOffset;
     }
 #endif
 
     if (!finishLinkData())
-        return nullptr;
+        return false;
+
+    return true;
+}
 
-    generateBytecodeHash(bytecode);
+SharedModule
+ModuleGenerator::finishModule(const ShareableBytes& bytecode)
+{
+    MOZ_ASSERT(compileMode_ == CompileMode::Once || compileMode_ == CompileMode::Tier1);
+
+    if (!finishCommon(bytecode))
+        return nullptr;
 
     UniqueConstCodeSegment codeSegment = CodeSegment::create(tier_,
                                                              masm_,
                                                              bytecode,
                                                              *linkDataTier_,
                                                              *metadata_);
     if (!codeSegment)
         return nullptr;
@@ -1222,28 +1242,54 @@ ModuleGenerator::finish(const ShareableB
         if (!maybeDebuggingBytes)
             return nullptr;
     }
 
     SharedCode code = js_new<Code>(Move(codeSegment), *metadata_);
     if (!code)
         return nullptr;
 
-    return SharedModule(js_new<Module>(Move(assumptions_),
+    return SharedModule(js_new<Module>(compileMode_,
+                                       Move(assumptions_),
                                        *code,
                                        Move(maybeDebuggingBytes),
                                        Move(linkData_),
                                        Move(env_->imports),
                                        Move(env_->exports),
                                        Move(env_->dataSegments),
                                        Move(env_->elemSegments),
                                        bytecode));
 }
 
 bool
+ModuleGenerator::finishTier2(const ShareableBytes& bytecode, SharedModule module)
+{
+    MOZ_ASSERT(compileMode_ == CompileMode::Tier2);
+
+    if (!finishCommon(bytecode))
+        return false;
+
+    UniqueConstCodeSegment codeSegment = CodeSegment::create(tier_,
+                                                             masm_,
+                                                             bytecode,
+                                                             *linkDataTier_,
+                                                             *metadata_);
+    if (!codeSegment)
+        return false;
+
+    MOZ_ASSERT(!metadata_->debugEnabled);
+
+    module->finishTier2Generator(linkData_.takeLinkData(tier_),
+                                 metadata_->takeMetadata(tier_),
+                                 Move(codeSegment),
+                                 Move(env_));
+    return true;
+}
+
+bool
 wasm::CompileFunction(CompileTask* task, UniqueChars* error)
 {
     TraceLoggerThread* logger = TraceLoggerForCurrentThread();
     AutoTraceLog logCompile(logger, TraceLogger_WasmCompilation);
 
     switch (task->tier()) {
       case Tier::Ion:
         for (FuncCompileUnit& unit : task->units()) {
--- a/js/src/wasm/WasmGenerator.h
+++ b/js/src/wasm/WasmGenerator.h
@@ -196,16 +196,18 @@ class CompileTask
         alloc_.reset();
         lifo_.releaseAll();
 
         init();
         return true;
     }
 };
 
+struct Tier2GeneratorTask;
+
 // 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
 {
@@ -263,16 +265,18 @@ class MOZ_STACK_CLASS ModuleGenerator
     MOZ_MUST_USE bool patchCallSites();
     MOZ_MUST_USE bool patchFarJumps(const TrapExitOffsetArray& trapExits, const Offsets& debugTrapStub);
     MOZ_MUST_USE bool finishTask(CompileTask* task);
     MOZ_MUST_USE bool finishOutstandingTask();
     MOZ_MUST_USE bool finishFuncExports();
     MOZ_MUST_USE bool finishCodegen();
     MOZ_MUST_USE bool finishLinkData();
     void generateBytecodeHash(const ShareableBytes& bytecode);
+    MOZ_MUST_USE bool finishMetadata(const ShareableBytes& bytecode);
+    MOZ_MUST_USE bool finishCommon(const ShareableBytes& bytecode);
     MOZ_MUST_USE bool addFuncImport(const Sig& sig, uint32_t globalDataOffset);
     MOZ_MUST_USE bool allocateGlobalBytes(uint32_t bytes, uint32_t align, uint32_t* globalDataOff);
     MOZ_MUST_USE bool allocateGlobal(GlobalDesc* global);
 
     MOZ_MUST_USE bool launchBatchCompile();
 
     MOZ_MUST_USE bool initAsmJS(Metadata* asmJSMetadata);
     MOZ_MUST_USE bool initWasm(const CompileArgs& args);
@@ -323,17 +327,21 @@ class MOZ_STACK_CLASS ModuleGenerator
     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 finish(const ShareableBytes& 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(const ShareableBytes& bytecode, SharedModule module);
 };
 
 // A FunctionGenerator encapsulates the generation of a single function body.
 // ModuleGenerator::startFuncDef must be called after construction and before
 // doing anything else.
 //
 // After the body is complete, ModuleGenerator::finishFuncDef must be called
 // before the FunctionGenerator is destroyed and the next function is started.
--- a/js/src/wasm/WasmJS.cpp
+++ b/js/src/wasm/WasmJS.cpp
@@ -380,22 +380,22 @@ wasm::Eval(JSContext* cx, Handle<TypedAr
         ReportOutOfMemory(cx);
         return false;
     }
 
     ScriptedCaller scriptedCaller;
     if (!DescribeScriptedCaller(cx, &scriptedCaller))
         return false;
 
-    CompileArgs compileArgs;
-    if (!compileArgs.initFromContext(cx, Move(scriptedCaller)))
+    MutableCompileArgs compileArgs = cx->new_<CompileArgs>();
+    if (!compileArgs || !compileArgs->initFromContext(cx, Move(scriptedCaller)))
         return false;
 
     UniqueChars error;
-    SharedModule module = Compile(*bytecode, compileArgs, &error);
+    SharedModule module = Compile(*bytecode, *compileArgs, &error);
     if (!module) {
         if (error) {
             JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_WASM_COMPILE_ERROR,
                                       error.get());
             return false;
         }
         ReportOutOfMemory(cx);
         return false;
@@ -882,22 +882,22 @@ WasmModuleObject::construct(JSContext* c
         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_BUF_ARG);
         return false;
     }
 
     MutableBytes bytecode;
     if (!GetBufferSource(cx, &callArgs[0].toObject(), JSMSG_WASM_BAD_BUF_ARG, &bytecode))
         return false;
 
-    CompileArgs compileArgs;
-    if (!InitCompileArgs(cx, &compileArgs))
+    MutableCompileArgs compileArgs = cx->new_<CompileArgs>();
+    if (!compileArgs || !InitCompileArgs(cx, compileArgs.get()))
         return false;
 
     UniqueChars error;
-    SharedModule module = Compile(*bytecode, compileArgs, &error);
+    SharedModule module = Compile(*bytecode, *compileArgs, &error);
     if (!module) {
         if (error) {
             JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_WASM_COMPILE_ERROR,
                                       error.get());
             return false;
         }
         ReportOutOfMemory(cx);
         return false;
@@ -1894,46 +1894,47 @@ Reject(JSContext* cx, const CompileArgs&
     if (!errorObj)
         return false;
 
     RootedValue rejectionValue(cx, ObjectValue(*errorObj));
     return PromiseObject::reject(cx, promise, rejectionValue);
 }
 
 static bool
-ResolveCompilation(JSContext* cx, Module& module, Handle<PromiseObject*> promise)
+ResolveCompilation(JSContext* cx, Module& module, const CompileArgs& compileArgs,
+                   Handle<PromiseObject*> promise)
 {
     RootedObject proto(cx, &cx->global()->getPrototype(JSProto_WasmModule).toObject());
     RootedObject moduleObj(cx, WasmModuleObject::create(cx, module, proto));
     if (!moduleObj)
         return false;
 
     RootedValue resolutionValue(cx, ObjectValue(*moduleObj));
     return PromiseObject::resolve(cx, promise, resolutionValue);
 }
 
 struct CompilePromiseTask : PromiseHelperTask
 {
-    MutableBytes bytecode;
-    CompileArgs  compileArgs;
-    UniqueChars  error;
-    SharedModule module;
+    MutableBytes      bytecode;
+    SharedCompileArgs compileArgs;
+    UniqueChars       error;
+    SharedModule      module;
 
     CompilePromiseTask(JSContext* cx, Handle<PromiseObject*> promise)
       : PromiseHelperTask(cx, promise)
     {}
 
     void execute() override {
-        module = Compile(*bytecode, compileArgs, &error);
+        module = Compile(*bytecode, *compileArgs, &error);
     }
 
     bool resolve(JSContext* cx, Handle<PromiseObject*> promise) override {
         return module
-               ? ResolveCompilation(cx, *module, promise)
-               : Reject(cx, compileArgs, Move(error), promise);
+               ? ResolveCompilation(cx, *module, *compileArgs, promise)
+               : Reject(cx, *compileArgs, Move(error), promise);
     }
 };
 
 static bool
 RejectWithPendingException(JSContext* cx, Handle<PromiseObject*> promise)
 {
     if (!cx->isExceptionPending())
         return false;
@@ -1993,29 +1994,31 @@ WebAssembly_compile(JSContext* cx, unsig
     if (!task || !task->init(cx))
         return false;
 
     CallArgs callArgs = CallArgsFromVp(argc, vp);
 
     if (!GetBufferSource(cx, callArgs, "WebAssembly.compile", &task->bytecode))
         return RejectWithPendingException(cx, promise, callArgs);
 
-    if (!InitCompileArgs(cx, &task->compileArgs))
+    MutableCompileArgs compileArgs = cx->new_<CompileArgs>();
+    if (!compileArgs || !InitCompileArgs(cx, compileArgs))
         return false;
+    task->compileArgs = compileArgs;
 
     if (!StartOffThreadPromiseHelperTask(cx, Move(task)))
         return false;
 
     callArgs.rval().setObject(*promise);
     return true;
 }
 
 static bool
-ResolveInstantiation(JSContext* cx, Module& module, HandleObject importObj,
-                     Handle<PromiseObject*> promise)
+ResolveInstantiation(JSContext* cx, Module& module, const CompileArgs& compileArgs,
+                     HandleObject importObj, Handle<PromiseObject*> promise)
 {
     RootedObject proto(cx, &cx->global()->getPrototype(JSProto_WasmModule).toObject());
     RootedObject moduleObj(cx, WasmModuleObject::create(cx, module, proto));
     if (!moduleObj)
         return false;
 
     RootedWasmInstanceObject instanceObj(cx);
     if (!Instantiate(cx, module, importObj, &instanceObj))
@@ -2035,34 +2038,36 @@ ResolveInstantiation(JSContext* cx, Modu
 
     val = ObjectValue(*resultObj);
     return PromiseObject::resolve(cx, promise, val);
 }
 
 struct InstantiatePromiseTask : PromiseHelperTask
 {
     MutableBytes           bytecode;
-    CompileArgs            compileArgs;
+    SharedCompileArgs      compileArgs;
     UniqueChars            error;
     SharedModule           module;
     PersistentRootedObject importObj;
 
-    InstantiatePromiseTask(JSContext* cx, Handle<PromiseObject*> promise, HandleObject importObj)
+    InstantiatePromiseTask(JSContext* cx, Handle<PromiseObject*> promise,
+                           const CompileArgs& compileArgs, HandleObject importObj)
       : PromiseHelperTask(cx, promise),
+        compileArgs(&compileArgs),
         importObj(cx, importObj)
     {}
 
     void execute() override {
-        module = Compile(*bytecode, compileArgs, &error);
+        module = Compile(*bytecode, *compileArgs, &error);
     }
 
     bool resolve(JSContext* cx, Handle<PromiseObject*> promise) override {
         return module
-               ? ResolveInstantiation(cx, *module, importObj, promise)
-               : Reject(cx, compileArgs, Move(error), promise);
+               ? ResolveInstantiation(cx, *module, *compileArgs, importObj, promise)
+               : Reject(cx, *compileArgs, Move(error), promise);
     }
 };
 
 static bool
 GetInstantiateArgs(JSContext* cx, CallArgs callArgs, MutableHandleObject firstArg,
                    MutableHandleObject importObj)
 {
     if (!callArgs.requireAtLeast(cx, "WebAssembly.instantiate", 1))
@@ -2100,26 +2105,27 @@ WebAssembly_instantiate(JSContext* cx, u
         RootedWasmInstanceObject instanceObj(cx);
         if (!Instantiate(cx, *module, importObj, &instanceObj))
             return RejectWithPendingException(cx, promise, callArgs);
 
         RootedValue resolutionValue(cx, ObjectValue(*instanceObj));
         if (!PromiseObject::resolve(cx, promise, resolutionValue))
             return false;
     } else {
-        auto task = cx->make_unique<InstantiatePromiseTask>(cx, promise, importObj);
+        MutableCompileArgs compileArgs = cx->new_<CompileArgs>();
+        if (!compileArgs || !InitCompileArgs(cx, compileArgs.get()))
+            return false;
+
+        auto task = cx->make_unique<InstantiatePromiseTask>(cx, promise, *compileArgs, importObj);
         if (!task || !task->init(cx))
             return false;
 
         if (!GetBufferSource(cx, firstArg, JSMSG_WASM_BAD_BUF_MOD_ARG, &task->bytecode))
             return RejectWithPendingException(cx, promise, callArgs);
 
-        if (!InitCompileArgs(cx, &task->compileArgs))
-            return false;
-
         if (!StartOffThreadPromiseHelperTask(cx, Move(task)))
             return false;
     }
 
     callArgs.rval().setObject(*promise);
     return true;
 }
 
--- a/js/src/wasm/WasmModule.cpp
+++ b/js/src/wasm/WasmModule.cpp
@@ -16,16 +16,17 @@
  * limitations under the License.
  */
 
 #include "wasm/WasmModule.h"
 
 #include "jsnspr.h"
 
 #include "jit/JitOptions.h"
+#include "threading/LockGuard.h"
 #include "wasm/WasmCompile.h"
 #include "wasm/WasmInstance.h"
 #include "wasm/WasmJS.h"
 #include "wasm/WasmSerialize.h"
 
 #include "jsatominlines.h"
 
 #include "vm/ArrayBufferObject-inl.h"
@@ -257,16 +258,20 @@ Module::bytecodeSerialize(uint8_t* bytec
 Module::compiledSerializedSize() const
 {
     // The compiled debug code must not be saved, set compiled size to 0,
     // so Module::assumptionsMatch will return false during assumptions
     // deserialization.
     if (metadata().debugEnabled)
         return 0;
 
+    blockOnIonCompileFinished();
+    if (!code_->hasTier(Tier::Serialized))
+        return 0;
+
     return assumptions_.serializedSize() +
            linkData_.serializedSize() +
            SerializedVectorSize(imports_) +
            SerializedVectorSize(exports_) +
            SerializedPodVectorSize(dataSegments_) +
            SerializedVectorSize(elemSegments_) +
            code_->serializedSize();
 }
@@ -274,31 +279,67 @@ Module::compiledSerializedSize() const
 /* virtual */ void
 Module::compiledSerialize(uint8_t* compiledBegin, size_t compiledSize) const
 {
     if (metadata().debugEnabled) {
         MOZ_RELEASE_ASSERT(compiledSize == 0);
         return;
     }
 
-    // Assumption must be serialized at the beginning of the compiled bytes so
-    // that compiledAssumptionsMatch can detect a build-id mismatch before any
-    // other decoding occurs.
+    blockOnIonCompileFinished();
+    if (!code_->hasTier(Tier::Serialized)) {
+        MOZ_RELEASE_ASSERT(compiledSize == 0);
+        return;
+    }
 
     uint8_t* cursor = compiledBegin;
     cursor = assumptions_.serialize(cursor);
     cursor = linkData_.serialize(cursor);
     cursor = SerializeVector(cursor, imports_);
     cursor = SerializeVector(cursor, exports_);
     cursor = SerializePodVector(cursor, dataSegments_);
     cursor = SerializeVector(cursor, elemSegments_);
     cursor = code_->serialize(cursor, linkData_);
     MOZ_RELEASE_ASSERT(cursor == compiledBegin + compiledSize);
 }
 
+void
+Module::finishTier2Generator(UniqueLinkDataTier linkData2, UniqueMetadataTier metadata2,
+                             UniqueConstCodeSegment code2, UniqueModuleEnvironment env2)
+{
+    // Install the data in the data structures. They will not be visible yet.
+
+    metadata().setTier2(Move(metadata2));
+    linkData().setTier2(Move(linkData2));
+    code().setTier2(Move(code2));
+    for (uint32_t i = 0; i < elemSegments_.length(); i++)
+        elemSegments_[i].setTier2(Move(env2->elemSegments[i].elemCodeRangeIndices(Tier::Ion)));
+
+    // Set the flag atomically to make the tier 2 data visible everywhere at
+    // once.
+
+    metadata().commitTier2();
+}
+
+void
+Module::blockOnIonCompileFinished() const
+{
+    LockGuard<Mutex> l(tier2Lock_);
+    while (mode_ == CompileMode::Tier1 && !metadata().hasTier2())
+        tier2Cond_.wait(l);
+}
+
+void
+Module::unblockOnTier2GeneratorFinished(CompileMode newMode) const
+{
+    LockGuard<Mutex> l(tier2Lock_);
+    mode_ = newMode;
+    tier2Cond_.notify_all();
+}
+
 /* static */ bool
 Module::assumptionsMatch(const Assumptions& current, const uint8_t* compiledBegin, size_t remain)
 {
     Assumptions cached;
     if (!cached.deserialize(compiledBegin, remain))
         return false;
 
     return current == cached;
@@ -362,19 +403,20 @@ Module::deserialize(const uint8_t* bytec
     MutableCode code = js_new<Code>();
     cursor = code->deserialize(cursor, bytecode, linkData, *metadata);
     if (!cursor)
         return nullptr;
 
     MOZ_RELEASE_ASSERT(cursor == compiledBegin + compiledSize);
     MOZ_RELEASE_ASSERT(!!maybeMetadata == code->metadata().isAsmJS());
 
-    return js_new<Module>(Move(assumptions),
+    return js_new<Module>(CompileMode::Tier2, // Serialized code is always Tier 2
+                          Move(assumptions),
                           *code,
-                          nullptr, // Serialized code is never debuggable
+                          nullptr,            // Serialized code is never debuggable
                           Move(linkData),
                           Move(imports),
                           Move(exports),
                           Move(dataSegments),
                           Move(elemSegments),
                           *bytecode);
 }
 
@@ -458,20 +500,22 @@ wasm::DeserializeModule(PRFileDesc* byte
 
     memcpy(bytecode->bytes.begin(), bytecodeMapping.get(), bytecodeInfo.size);
 
     ScriptedCaller scriptedCaller;
     scriptedCaller.filename = Move(filename);
     scriptedCaller.line = line;
     scriptedCaller.column = column;
 
-    CompileArgs args(Assumptions(Move(buildId)), Move(scriptedCaller));
+    SharedCompileArgs args = js_new<CompileArgs>(Assumptions(Move(buildId)), Move(scriptedCaller));
+    if (!args)
+        return nullptr;
 
     UniqueChars error;
-    return Compile(*bytecode, Move(args), &error);
+    return Compile(*bytecode, *args, &error);
 }
 
 /* virtual */ void
 Module::addSizeOfMisc(MallocSizeOf mallocSizeOf,
                       Metadata::SeenSet* seenMetadata,
                       ShareableBytes::SeenSet* seenBytes,
                       Code::SeenSet* seenCode,
                       size_t* code,
--- a/js/src/wasm/WasmModule.h
+++ b/js/src/wasm/WasmModule.h
@@ -15,23 +15,28 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 #ifndef wasm_module_h
 #define wasm_module_h
 
 #include "js/TypeDecls.h"
-
+#include "threading/ConditionVariable.h"
+#include "threading/Mutex.h"
+#include "vm/MutexIDs.h"
 #include "wasm/WasmCode.h"
 #include "wasm/WasmTable.h"
+#include "wasm/WasmValidate.h"
 
 namespace js {
 namespace wasm {
 
+struct CompileArgs;
+
 // LinkData contains all the metadata necessary to patch all the locations
 // that depend on the absolute address of a CodeSegment.
 //
 // LinkData is built incrementing by ModuleGenerator and then stored immutably
 // in Module.
 
 struct LinkDataTierCacheablePod
 {
@@ -90,16 +95,22 @@ class LinkData
 
     bool hasTier2() const { return metadata_->hasTier2(); }
     void setTier2(UniqueLinkDataTier linkData) const;
     Tiers tiers() const;
 
     const LinkDataTier& linkData(Tier tier) const;
     LinkDataTier& linkData(Tier tier);
 
+    UniquePtr<LinkDataTier> takeLinkData(Tier tier) {
+        MOZ_ASSERT(!hasTier2());
+        MOZ_ASSERT(linkData1_->tier == tier);
+        return Move(linkData1_);
+    }
+
     WASM_DECLARE_SERIALIZABLE(LinkData)
 };
 
 // Module represents a compiled wasm module and primarily provides two
 // operations: instantiation and serialization. A Module can be instantiated any
 // number of times to produce new Instance objects. A Module can be serialized
 // any number of times such that the serialized bytes can be deserialized later
 // to produce a new, equivalent Module.
@@ -125,29 +136,44 @@ class Module : public JS::WasmModule
 
     // `codeIsBusy_` is set to false initially and then to true when `code_` is
     // already being used for an instance and can't be shared because it may be
     // patched by the debugger. Subsequent instances must then create copies
     // by linking the `unlinkedCodeForDebugging_`.
 
     mutable mozilla::Atomic<bool> codeIsBusy_;
 
+    // The lock guards the mode_ member, and the lock/cond pair are used to
+    // allow threads to wait for the availability of Ion code and signal the
+    // completion of tier-2 compilation; see blockOnIonCompileFinished and
+    // unblockOnTier2GeneratorFinished, below.
+
+    mutable Mutex                 tier2Lock_;
+    mutable ConditionVariable     tier2Cond_;
+
+    // Access mode_ only under the lock.  It will be changed from Tier1 to Tier2
+    // once Tier2 compilation is finished, and from Tier1 to Once if Tier2
+    // compilation is disabled (in testing modes) or cancelled.
+
+    mutable CompileMode           mode_;
+
     bool instantiateFunctions(JSContext* cx, Handle<FunctionVector> funcImports) const;
     bool instantiateMemory(JSContext* cx, MutableHandleWasmMemoryObject memory) const;
     bool instantiateTable(JSContext* cx,
                           MutableHandleWasmTableObject table,
                           SharedTableVector* tables) const;
     bool initSegments(JSContext* cx,
                       HandleWasmInstanceObject instance,
                       Handle<FunctionVector> funcImports,
                       HandleWasmMemoryObject memory,
                       const ValVector& globalImports) const;
 
   public:
-    Module(Assumptions&& assumptions,
+    Module(CompileMode mode,
+           Assumptions&& assumptions,
            const Code& code,
            UniqueConstBytes unlinkedCodeForDebugging,
            LinkData&& linkData,
            ImportVector&& imports,
            ExportVector&& exports,
            DataSegmentVector&& dataSegments,
            ElemSegmentVector&& elemSegments,
            const ShareableBytes& bytecode)
@@ -155,40 +181,68 @@ class Module : public JS::WasmModule
         code_(&code),
         unlinkedCodeForDebugging_(Move(unlinkedCodeForDebugging)),
         linkData_(Move(linkData)),
         imports_(Move(imports)),
         exports_(Move(exports)),
         dataSegments_(Move(dataSegments)),
         elemSegments_(Move(elemSegments)),
         bytecode_(&bytecode),
-        codeIsBusy_(false)
+        codeIsBusy_(false),
+        tier2Lock_(js::mutexid::WasmTier2GeneratorComplete),
+        mode_(mode)
     {
         MOZ_ASSERT_IF(metadata().debugEnabled, unlinkedCodeForDebugging_);
     }
     ~Module() override { /* Note: can be called on any thread */ }
 
     const Code& code() const { return *code_; }
+    const CodeSegment& codeSegment(Tier t) const { return code_->segment(t); }
     const Metadata& metadata() const { return code_->metadata(); }
     const MetadataTier& metadata(Tier t) const { return code_->metadata(t); }
+    const LinkData& linkData() const { return linkData_; }
+    const LinkDataTier& linkData(Tier t) const { return linkData_.linkData(t); }
     const ImportVector& imports() const { return imports_; }
     const ExportVector& exports() const { return exports_; }
-    const Bytes& bytecode() const { return bytecode_->bytes; }
+    const ShareableBytes& bytecode() const { return *bytecode_; }
     uint32_t codeLength(Tier t) const { return code_->segment(t).length(); }
 
     // Instantiate this module with the given imports:
 
     bool instantiate(JSContext* cx,
                      Handle<FunctionVector> funcImports,
                      HandleWasmTableObject tableImport,
                      HandleWasmMemoryObject memoryImport,
                      const ValVector& globalImports,
                      HandleObject instanceProto,
                      MutableHandleWasmInstanceObject instanceObj) const;
 
+    // Tiered compilation support: these run on the compilation thread.
+
+    // Will be invoked once the second-tier compilation of the module is
+    // finished; it is passed the tier-variant data for Tier 2, which it
+    // installs in the module and makes visible.
+
+    void finishTier2Generator(UniqueLinkDataTier linkData2, UniqueMetadataTier metadata2,
+                              UniqueConstCodeSegment code2, UniqueModuleEnvironment env2);
+
+    // Wait until Ion-compiled code is available, which will be true either
+    // immediately (first-level compile was Ion and is already done), not at all
+    // (first-level compile was Baseline and there's not a second level), or
+    // later (ongoing second-level compilation).  Once this returns, one can use
+    // code().hasTier() to check code availability - there is no guarantee that
+    // Ion code will be available, but if it isn't then it never will.
+
+    void blockOnIonCompileFinished() const;
+
+    // Signal all waiters that are waiting on tier-2 compilation to be done that
+    // they should wake up.  They will be waiting in blockOnIonCompileFinished.
+
+    void unblockOnTier2GeneratorFinished(CompileMode newMode) const;
+
     // Structured clone support:
 
     size_t bytecodeSerializedSize() const override;
     void bytecodeSerialize(uint8_t* bytecodeBegin, size_t bytecodeSize) const override;
     size_t compiledSerializedSize() const override;
     void compiledSerialize(uint8_t* compiledBegin, size_t compiledSize) const override;
 
     static bool assumptionsMatch(const Assumptions& current, const uint8_t* compiledBegin,