Bug 1395587 - Baldr: shrink the ModuleGenerator interface (r=lth)
authorLuke Wagner <luke@mozilla.com>
Wed, 06 Sep 2017 08:30:35 -0500
changeset 428808 7189690845fb45120bd74ebe6d9d7c10d206cd08
parent 428807 b6eeb204f318400551b4c34050b33af0a7d08508
child 428809 32df4db6c1503deeb20e50d6e76d0c8cd0d2d5ef
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)
reviewerslth
bugs1395587
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 1395587 - Baldr: shrink the ModuleGenerator interface (r=lth) MozReview-Commit-ID: DdVvMrtpEIl
js/src/wasm/AsmJS.cpp
js/src/wasm/WasmBaselineCompile.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/WasmIonCompile.cpp
js/src/wasm/WasmModule.cpp
js/src/wasm/WasmModule.h
js/src/wasm/WasmStubs.cpp
js/src/wasm/WasmTypes.h
js/src/wasm/WasmValidate.h
--- a/js/src/wasm/AsmJS.cpp
+++ b/js/src/wasm/AsmJS.cpp
@@ -1647,16 +1647,17 @@ class MOZ_STACK_CLASS ModuleValidator
     GlobalMap             globalMap_;
     SigMap                sigMap_;
     ImportMap             importMap_;
     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_;
 
@@ -1704,17 +1705,18 @@ class MOZ_STACK_CLASS ModuleValidator
             return true;
         }
 
         return newSig(Move(sig), sigIndex) &&
                sigMap_.add(p, &mg_.sig(*sigIndex), *sigIndex);
     }
 
   public:
-    ModuleValidator(JSContext* cx, AsmJSParser& parser, ParseNode* moduleFunctionNode)
+    ModuleValidator(JSContext* cx, const CompileArgs& args, AsmJSParser& parser,
+                    ParseNode* moduleFunctionNode)
       : cx_(cx),
         parser_(parser),
         moduleFunctionNode_(moduleFunctionNode),
         moduleFunctionName_(FunctionName(moduleFunctionNode)),
         globalArgumentName_(nullptr),
         importArgumentName_(nullptr),
         bufferArgumentName_(nullptr),
         standardLibraryMathNames_(cx),
@@ -1725,17 +1727,18 @@ class MOZ_STACK_CLASS ModuleValidator
         functions_(cx),
         funcPtrTables_(cx),
         globalMap_(cx),
         sigMap_(cx),
         importMap_(cx),
         arrayViews_(cx),
         atomicsPresent_(false),
         simdPresent_(false),
-        mg_(nullptr, nullptr),
+        env_(CompileMode::Once, Tier::Ion, DebugEnabled::False, ModuleKind::AsmJS),
+        mg_(args, &env_, nullptr, nullptr),
         errorString_(nullptr),
         errorOffset_(UINT32_MAX),
         errorOverRecursed_(false)
     {}
 
     ~ModuleValidator() {
         if (errorString_) {
             MOZ_ASSERT(errorOffset_ != UINT32_MAX);
@@ -1850,45 +1853,27 @@ 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;
 
-        ScriptedCaller scriptedCaller;
-        if (parser_.ss->filename()) {
-            scriptedCaller.line = scriptedCaller.column = 0;  // unused
-            scriptedCaller.filename = DuplicateString(parser_.ss->filename());
-            if (!scriptedCaller.filename)
-                return false;
-        }
-
-        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))
+        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;
         }
 
-        env->minMemoryLength = RoundUpToNextValidAsmJSHeapLength(0);
-
-        if (!mg_.init(Move(env), *args, CompileMode::Once, asmJSMetadata_.get()))
-            return false;
-
-        return true;
+        return mg_.init(asmJSMetadata_.get());
     }
 
     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_; }
@@ -2488,17 +2473,17 @@ IsSimdTuple(ModuleValidator& m, ParseNod
     if (CallArgListLength(pn) != GetSimdLanes(global->simdCtorType()))
         return false;
 
     *type = global->simdCtorType();
     return true;
 }
 
 static bool
-IsNumericLiteral(ModuleValidator& m, ParseNode* pn, bool* isSimd = nullptr);
+IsNumericLiteral(ModuleValidator& m, ParseNode* pn);
 
 static NumLit
 ExtractNumericLiteral(ModuleValidator& m, ParseNode* pn);
 
 static inline bool
 IsLiteralInt(ModuleValidator& m, ParseNode* pn, uint32_t* u32);
 
 static bool
@@ -2539,26 +2524,19 @@ IsSimdLiteral(ModuleValidator& m, ParseN
         arg = NextNode(arg);
     }
 
     MOZ_ASSERT(arg == nullptr);
     return true;
 }
 
 static bool
-IsNumericLiteral(ModuleValidator& m, ParseNode* pn, bool* isSimd)
-{
-    if (IsNumericNonFloatLiteral(pn) || IsFloatLiteral(m, pn))
-        return true;
-    if (IsSimdLiteral(m, pn)) {
-        if (isSimd)
-            *isSimd = true;
-        return true;
-    }
-    return false;
+IsNumericLiteral(ModuleValidator& m, ParseNode* pn)
+{
+    return IsNumericNonFloatLiteral(pn) || IsFloatLiteral(m, pn) || IsSimdLiteral(m, pn);
 }
 
 // The JS grammar treats -42 as -(42) (i.e., with separate grammar
 // productions) for the unary - and literal 42). However, the asm.js spec
 // recognizes -42 (modulo parens, so -(42) and -((42))) as a single literal
 // so fold the two potential parse nodes into a single double value.
 static double
 ExtractNumericNonFloatValue(ParseNode* pn, ParseNode** out = nullptr)
@@ -2958,23 +2936,16 @@ class MOZ_STACK_CLASS FunctionValidator
     }
 
     bool finish(uint32_t funcIndex) {
         MOZ_ASSERT(!blockDepth_);
         MOZ_ASSERT(breakableStack_.empty());
         MOZ_ASSERT(continuableStack_.empty());
         MOZ_ASSERT(breakLabels_.empty());
         MOZ_ASSERT(continueLabels_.empty());
-        for (auto iter = locals_.all(); !iter.empty(); iter.popFront()) {
-            if (iter.front().value().type.isSimd()) {
-                setUsesSimd();
-                break;
-            }
-        }
-
         return m_.mg().finishFuncDef(funcIndex, &fg_);
     }
 
     bool fail(ParseNode* pn, const char* str) {
         return m_.fail(pn, str);
     }
 
     bool failf(ParseNode* pn, const char* fmt, ...) MOZ_FORMAT_PRINTF(3, 4) {
@@ -2984,26 +2955,16 @@ class MOZ_STACK_CLASS FunctionValidator
         va_end(ap);
         return false;
     }
 
     bool failName(ParseNode* pn, const char* fmt, PropertyName* name) {
         return m_.failName(pn, fmt, name);
     }
 
-    /***************************************************** Attributes */
-
-    void setUsesSimd() {
-        fg_.setUsesSimd();
-    }
-
-    void setUsesAtomics() {
-        fg_.setUsesAtomics();
-    }
-
     /***************************************************** Local scope setup */
 
     bool addLocal(ParseNode* pn, PropertyName* name, Type type) {
         LocalMap::AddPtr p = locals_.lookupForAdd(name);
         if (p)
             return failName(pn, "duplicate local name '%s' not allowed", name);
         return locals_.add(p, name, Local(type, locals_.count()));
     }
@@ -3889,22 +3850,18 @@ IsLiteralOrConst(FunctionValidator& f, P
         const ModuleValidator::Global* global = f.lookupGlobal(pn->name());
         if (!global || global->which() != ModuleValidator::Global::ConstantLiteral)
             return false;
 
         *lit = global->constLiteralValue();
         return true;
     }
 
-    bool isSimd = false;
-    if (!IsNumericLiteral(f.m(), pn, &isSimd))
-        return false;
-
-    if (isSimd)
-        f.setUsesSimd();
+    if (!IsNumericLiteral(f.m(), pn))
+        return false;
 
     *lit = ExtractNumericLiteral(f.m(), pn);
     return true;
 }
 
 static bool
 CheckFinalReturn(FunctionValidator& f, ParseNode* lastNonEmptyStmt)
 {
@@ -4704,18 +4661,16 @@ CheckAtomicsExchange(FunctionValidator& 
     *type = Type::Int;
     return true;
 }
 
 static bool
 CheckAtomicsBuiltinCall(FunctionValidator& f, ParseNode* callNode, AsmJSAtomicsBuiltinFunction func,
                         Type* type)
 {
-    f.setUsesAtomics();
-
     switch (func) {
       case AsmJSAtomicsBuiltin_compareExchange:
         return CheckAtomicsCompareExchange(f, callNode, type);
       case AsmJSAtomicsBuiltin_exchange:
         return CheckAtomicsExchange(f, callNode, type);
       case AsmJSAtomicsBuiltin_load:
         return CheckAtomicsLoad(f, callNode, type);
       case AsmJSAtomicsBuiltin_store:
@@ -5618,18 +5573,16 @@ CheckSimdSplat(FunctionValidator& f, Par
     *type = opType;
     return true;
 }
 
 static bool
 CheckSimdOperationCall(FunctionValidator& f, ParseNode* call, const ModuleValidator::Global* global,
                        Type* type)
 {
-    f.setUsesSimd();
-
     MOZ_ASSERT(global->isSimdOperation());
 
     SimdType opType = global->simdOperationType();
 
     switch (SimdOperation op = global->simdOperation()) {
       case SimdOperation::Fn_check:
         return CheckSimdCheck(f, call, opType, type);
 
@@ -5714,18 +5667,16 @@ CheckSimdOperationCall(FunctionValidator
     }
     MOZ_CRASH("unexpected simd operation in CheckSimdOperationCall");
 }
 
 static bool
 CheckSimdCtorCall(FunctionValidator& f, ParseNode* call, const ModuleValidator::Global* global,
                   Type* type)
 {
-    f.setUsesSimd();
-
     MOZ_ASSERT(call->isKind(PNK_CALL));
 
     SimdType simdType = global->simdCtorType();
     unsigned length = GetSimdLanes(simdType);
     if (!CheckSimdCallArgs(f, call, length, CheckSimdScalarArgs(simdType)))
         return false;
 
     if (!f.writeSimdOp(simdType, SimdOperation::Constructor))
@@ -5853,20 +5804,17 @@ CheckCoercedAtomicsBuiltinCall(FunctionV
 static bool
 CheckCoercedCall(FunctionValidator& f, ParseNode* call, Type ret, Type* type)
 {
     MOZ_ASSERT(ret.isCanonical());
 
     if (!CheckRecursionLimitDontReport(f.cx()))
         return f.m().failOverRecursed();
 
-    bool isSimd = false;
-    if (IsNumericLiteral(f.m(), call, &isSimd)) {
-        if (isSimd)
-            f.setUsesSimd();
+    if (IsNumericLiteral(f.m(), call)) {
         NumLit lit = ExtractNumericLiteral(f.m(), call);
         if (!f.writeConstExpr(lit))
             return false;
         return CoerceResult(f, call, ret, Type::lit(lit), type);
     }
 
     ParseNode* callee = CallCallee(call);
 
@@ -6406,22 +6354,18 @@ CheckBitwise(FunctionValidator& f, Parse
 }
 
 static bool
 CheckExpr(FunctionValidator& f, ParseNode* expr, Type* type)
 {
     if (!CheckRecursionLimitDontReport(f.cx()))
         return f.m().failOverRecursed();
 
-    bool isSimd = false;
-    if (IsNumericLiteral(f.m(), expr, &isSimd)) {
-        if (isSimd)
-            f.setUsesSimd();
+    if (IsNumericLiteral(f.m(), expr))
         return CheckNumericLiteral(f, expr, type);
-    }
 
     switch (expr->getKind()) {
       case PNK_NAME:        return CheckVarRef(f, expr, type);
       case PNK_ELEM:        return CheckLoadArray(f, expr, type);
       case PNK_ASSIGN:      return CheckAssign(f, expr, type);
       case PNK_POS:         return CheckPos(f, expr, type);
       case PNK_NOT:         return CheckNot(f, expr, type);
       case PNK_NEG:         return CheckNeg(f, expr, type);
@@ -7399,17 +7343,29 @@ 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);
 
-    ModuleValidator m(cx, parser, 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);
     if (!m.init())
         return nullptr;
 
     if (!CheckFunctionHead(m, moduleFunctionNode))
         return nullptr;
 
     if (!CheckModuleArguments(m, moduleFunctionNode))
         return nullptr;
--- a/js/src/wasm/WasmBaselineCompile.cpp
+++ b/js/src/wasm/WasmBaselineCompile.cpp
@@ -359,16 +359,17 @@ class BaseCompiler
         ScratchI32(BaseCompiler& bc) {}
         operator Register() const {
             MOZ_CRASH("BaseCompiler platform hook - ScratchI32");
         }
     };
 #endif
 
     typedef Vector<NonAssertingLabel, 8, SystemAllocPolicy> LabelVector;
+    typedef Vector<MIRType, 8, SystemAllocPolicy> MIRTypeVector;
 
     // The strongly typed register wrappers have saved my bacon a few
     // times; though they are largely redundant they stay, for now.
 
     struct RegI32 : public Register
     {
         RegI32() : Register(Register::Invalid()) {}
         explicit RegI32(Register reg) : Register(reg) {}
--- a/js/src/wasm/WasmCode.h
+++ b/js/src/wasm/WasmCode.h
@@ -373,17 +373,18 @@ class Metadata : public ShareableBase<Me
 {
     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))
+        metadata1_(Move(tier)),
+        debugEnabled(false)
     {}
     virtual ~Metadata() {}
 
     MetadataCacheablePod& pod() { return *this; }
     const MetadataCacheablePod& pod() const { return *this; }
 
     void commitTier2() const;
     bool hasTier2() const { return hasTier2_; }
--- a/js/src/wasm/WasmCompile.cpp
+++ b/js/src/wasm/WasmCompile.cpp
@@ -55,41 +55,41 @@ DecodeFunctionBody(Decoder& d, ModuleGen
         return false;
 
     memcpy(fg.bytes().begin(), bodyBegin, bodySize);
 
     return mg.finishFuncDef(funcIndex, &fg);
 }
 
 static bool
-DecodeCodeSection(Decoder& d, ModuleGenerator& mg)
+DecodeCodeSection(Decoder& d, ModuleGenerator& mg, ModuleEnvironment* env)
 {
     uint32_t sectionStart, sectionSize;
-    if (!d.startSection(SectionId::Code, &mg.mutableEnv(), &sectionStart, &sectionSize, "code"))
+    if (!d.startSection(SectionId::Code, env, &sectionStart, &sectionSize, "code"))
         return false;
 
     if (!mg.startFuncDefs())
         return false;
 
     if (sectionStart == Decoder::NotStarted) {
-        if (mg.env().numFuncDefs() != 0)
+        if (env->numFuncDefs() != 0)
             return d.fail("expected function bodies");
 
         return mg.finishFuncDefs();
     }
 
     uint32_t numFuncDefs;
     if (!d.readVarU32(&numFuncDefs))
         return d.fail("expected function body count");
 
-    if (numFuncDefs != mg.env().numFuncDefs())
+    if (numFuncDefs != env->numFuncDefs())
         return d.fail("function body count does not match function signature count");
 
     for (uint32_t funcDefIndex = 0; funcDefIndex < numFuncDefs; funcDefIndex++) {
-        if (!DecodeFunctionBody(d, mg, mg.env().numFuncImports() + funcDefIndex))
+        if (!DecodeFunctionBody(d, mg, env->numFuncImports() + funcDefIndex))
             return false;
     }
 
     if (!d.finishSection(sectionStart, sectionSize, "code"))
         return false;
 
     return mg.finishFuncDefs();
 }
@@ -107,125 +107,79 @@ CompileArgs::initFromContext(JSContext* 
     // only enable it when a developer actually cares: when the debugger tab
     // is open.
     debugEnabled = cx->compartment()->debuggerObservesAsmJS();
 
     this->scriptedCaller = Move(scriptedCaller);
     return assumptions.initBuildIdFromContext(cx);
 }
 
-static void
-CompilerAvailability(ModuleKind kind, const CompileArgs& args, bool* baselineEnabled,
-                     bool* debugEnabled, bool* ionEnabled)
-{
-    bool baselinePossible = kind == ModuleKind::Wasm && BaselineCanCompile();
-    *baselineEnabled = baselinePossible && args.baselineEnabled;
-    *debugEnabled = baselinePossible && args.debugEnabled;
-    *ionEnabled = args.ionEnabled;
-
-    // Default to Ion if necessary: We will never get to this point on platforms
-    // 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 BackgroundWorkPossible() && baselineEnabled && ionEnabled && !debugEnabled
-           ? CompileMode::Tier1
-           : CompileMode::Once;
-}
-
-wasm::Tier
-wasm::GetTier(const CompileArgs& args, CompileMode compileMode, ModuleKind kind)
-{
-    bool baselineEnabled, debugEnabled, ionEnabled;
-    CompilerAvailability(kind, args, &baselineEnabled, &debugEnabled, &ionEnabled);
-
-    switch (compileMode) {
-      case CompileMode::Tier1:
-        MOZ_ASSERT(baselineEnabled);
-        return Tier::Baseline;
-
-      case CompileMode::Tier2:
-        MOZ_ASSERT(ionEnabled);
-        return Tier::Ion;
-
-      case CompileMode::Once:
-        return (debugEnabled || !ionEnabled) ? Tier::Baseline : Tier::Ion;
-
-      default:
-        MOZ_CRASH("Bad mode");
-    }
-}
-
-static bool
-Compile(ModuleGenerator& mg, const ShareableBytes& bytecode, const CompileArgs& args,
-        UniqueChars* error, CompileMode compileMode)
+SharedModule
+wasm::CompileInitialTier(const ShareableBytes& bytecode, const CompileArgs& args, UniqueChars* error)
 {
     MOZ_RELEASE_ASSERT(wasm::HaveSignalHandlers());
 
-    auto env = js::MakeUnique<ModuleEnvironment>();
-    if (!env)
-        return false;
+    bool baselineEnabled = BaselineCanCompile() && args.baselineEnabled;
+    bool debugEnabled = BaselineCanCompile() && args.debugEnabled;
+    bool ionEnabled = args.ionEnabled || !baselineEnabled;
+
+    CompileMode mode;
+    Tier tier;
+    DebugEnabled debug;
+    if (BackgroundWorkPossible() && baselineEnabled && ionEnabled && !debugEnabled) {
+        mode = CompileMode::Tier1;
+        tier = Tier::Baseline;
+        debug = DebugEnabled::False;
+    } else {
+        mode = CompileMode::Once;
+        tier = debugEnabled || !ionEnabled ? Tier::Baseline : Tier::Ion;
+        debug = debugEnabled ? DebugEnabled::True : DebugEnabled::False;
+    }
+
+    ModuleEnvironment env(mode, tier, debug);
 
     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;
+    if (!DecodeModuleEnvironment(d, &env))
+        return nullptr;
 
-    MOZ_ASSERT(!*error, "unreported error");
-    return true;
-}
+    ModuleGenerator mg(args, &env, nullptr, error);
+    if (!mg.init())
+        return nullptr;
 
-SharedModule
-wasm::CompileInitialTier(const ShareableBytes& bytecode, const CompileArgs& args, UniqueChars* error)
-{
-    ModuleGenerator mg(error, nullptr);
+    if (!DecodeCodeSection(d, mg, &env))
+        return nullptr;
 
-    CompileMode mode = GetInitialCompileMode(args);
-    if (!Compile(mg, bytecode, args, error, mode))
+    if (!DecodeModuleTail(d, &env))
         return nullptr;
 
     return mg.finishModule(bytecode);
 }
 
 bool
 wasm::CompileTier2(Module& module, const CompileArgs& args, Atomic<bool>* cancelled)
 {
+    MOZ_RELEASE_ASSERT(wasm::HaveSignalHandlers());
+
     UniqueChars error;
-    ModuleGenerator mg(&error, cancelled);
+    Decoder d(module.bytecode().bytes, &error);
+
+    ModuleEnvironment env(CompileMode::Tier2, Tier::Ion, DebugEnabled::False);
+    if (!DecodeModuleEnvironment(d, &env))
+        return false;
 
-    if (!Compile(mg, module.bytecode(), args, &error, CompileMode::Tier2))
+    ModuleGenerator mg(args, &env, cancelled, &error);
+    if (!mg.init())
+        return false;
+
+    if (!DecodeCodeSection(d, mg, &env))
+        return false;
+
+    if (!DecodeModuleTail(d, &env))
         return false;
 
     return mg.finishTier2(module);
 }
--- a/js/src/wasm/WasmCompile.h
+++ b/js/src/wasm/WasmCompile.h
@@ -70,37 +70,12 @@ SharedModule
 CompileInitialTier(const ShareableBytes& bytecode, const CompileArgs& args, UniqueChars* error);
 
 // Attempt to compile the second tier of the given wasm::Module, returning whether
 // tier-2 compilation succeeded and Module::finishTier2 was called.
 
 bool
 CompileTier2(Module& module, const CompileArgs& args, Atomic<bool>* cancelled);
 
-// Select whether debugging is available based on the available compilers, the
-// 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, 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.
-
-Tier
-GetTier(const CompileArgs& args, CompileMode compileMode, ModuleKind kind = ModuleKind::Wasm);
-
 }  // namespace wasm
 }  // namespace js
 
 #endif // namespace wasm_compile_h
--- a/js/src/wasm/WasmGenerator.cpp
+++ b/js/src/wasm/WasmGenerator.cpp
@@ -40,21 +40,22 @@ using mozilla::MakeEnumeratedRange;
 
 // ****************************************************************************
 // ModuleGenerator
 
 static const unsigned GENERATOR_LIFO_DEFAULT_CHUNK_SIZE = 4 * 1024;
 static const unsigned COMPILATION_LIFO_DEFAULT_CHUNK_SIZE = 64 * 1024;
 static const uint32_t BAD_CODE_RANGE = UINT32_MAX;
 
-ModuleGenerator::ModuleGenerator(UniqueChars* error, mozilla::Atomic<bool>* cancelled)
-  : compileMode_(CompileMode(-1)),
-    tier_(Tier(-1)),
+ModuleGenerator::ModuleGenerator(const CompileArgs& args, ModuleEnvironment* env,
+                                 Atomic<bool>* cancelled, UniqueChars* error)
+  : compileArgs_(&args),
     error_(error),
     cancelled_(cancelled),
+    env_(env),
     linkDataTier_(nullptr),
     metadataTier_(nullptr),
     numSigs_(0),
     numTables_(0),
     lifo_(GENERATOR_LIFO_DEFAULT_CHUNK_SIZE),
     masmAlloc_(&lifo_),
     masm_(MacroAssembler::WasmToken(), masmAlloc_),
     lastPatchedCallsite_(0),
@@ -73,39 +74,39 @@ ModuleGenerator::ModuleGenerator(UniqueC
 
 ModuleGenerator::~ModuleGenerator()
 {
     if (parallel_) {
         // Wait for any outstanding jobs to fail or complete.
         if (outstanding_) {
             AutoLockHelperThreadState lock;
             while (true) {
-                CompileTaskPtrVector& worklist = HelperThreadState().wasmWorklist(lock, compileMode_);
+                CompileTaskPtrVector& worklist = HelperThreadState().wasmWorklist(lock, mode());
                 MOZ_ASSERT(outstanding_ >= worklist.length());
                 outstanding_ -= worklist.length();
                 worklist.clear();
 
-                CompileTaskPtrVector& finished = HelperThreadState().wasmFinishedList(lock, compileMode_);
+                CompileTaskPtrVector& finished = HelperThreadState().wasmFinishedList(lock, mode());
                 MOZ_ASSERT(outstanding_ >= finished.length());
                 outstanding_ -= finished.length();
                 finished.clear();
 
-                uint32_t numFailed = HelperThreadState().harvestFailedWasmJobs(lock, compileMode_);
+                uint32_t numFailed = HelperThreadState().harvestFailedWasmJobs(lock, mode());
                 MOZ_ASSERT(outstanding_ >= numFailed);
                 outstanding_ -= numFailed;
 
                 if (!outstanding_)
                     break;
 
                 HelperThreadState().wait(lock, GlobalHelperThreadState::CONSUMER);
             }
         }
 
-        MOZ_ASSERT(HelperThreadState().wasmCompilationInProgress(compileMode_));
-        HelperThreadState().wasmCompilationInProgress(compileMode_) = false;
+        MOZ_ASSERT(HelperThreadState().wasmCompilationInProgress(mode()));
+        HelperThreadState().wasmCompilationInProgress(mode()) = false;
     } else {
         MOZ_ASSERT(!outstanding_);
     }
     MOZ_ASSERT_IF(finishedFuncDefs_, !batchedBytecode_);
     MOZ_ASSERT_IF(finishedFuncDefs_, !currentTask_);
 }
 
 bool
@@ -116,58 +117,48 @@ ModuleGenerator::initAsmJS(Metadata* asm
     if (!linkData_.initTier1(Tier::Ion, *asmJSMetadata))
         return false;
     linkDataTier_ = &linkData_.linkData(Tier::Ion);
 
     metadataTier_ = &asmJSMetadata->metadata(Tier::Ion);
     metadata_ = asmJSMetadata;
     MOZ_ASSERT(isAsmJS());
 
-    // Enabling debugging requires baseline and baseline is only enabled for
-    // wasm (since the baseline does not currently support Atomics or SIMD).
-
-    metadata_->debugEnabled = false;
-    tier_ = Tier::Ion;
-
     // 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.
 
     MOZ_ASSERT(env_->sigs.length() == AsmJSMaxTypes);
     MOZ_ASSERT(env_->tables.length() == AsmJSMaxTables);
     MOZ_ASSERT(env_->asmJSSigToTableIndex.length() == AsmJSMaxTypes);
 
     return true;
 }
 
 bool
-ModuleGenerator::initWasm(const CompileArgs& args)
+ModuleGenerator::initWasm()
 {
     MOZ_ASSERT(!env_->isAsmJS());
 
-    tier_ = GetTier(args, compileMode_);
-
-    auto metadataTier = js::MakeUnique<MetadataTier>(tier_);
+    auto metadataTier = js::MakeUnique<MetadataTier>(tier());
     if (!metadataTier)
         return false;
 
     metadata_ = js_new<Metadata>(Move(metadataTier));
     if (!metadata_)
         return false;
 
-    metadataTier_ = &metadata_->metadata(tier_);
+    metadataTier_ = &metadata_->metadata(tier());
 
-    if (!linkData_.initTier1(tier_, *metadata_))
+    if (!linkData_.initTier1(tier(), *metadata_))
         return false;
-    linkDataTier_ = &linkData_.linkData(tier_);
+    linkDataTier_ = &linkData_.linkData(tier());
 
     MOZ_ASSERT(!isAsmJS());
 
-    metadata_->debugEnabled = GetDebugEnabled(args);
-
     // For wasm, the Vectors are correctly-sized and already initialized.
 
     numSigs_ = env_->sigs.length();
     numTables_ = env_->tables.length();
 
     for (size_t i = 0; i < env_->funcImportGlobalDataOffsets.length(); i++) {
         env_->funcImportGlobalDataOffsets[i] = metadata_->globalDataLength;
         metadata_->globalDataLength += sizeof(FuncImportTls);
@@ -215,90 +206,65 @@ ModuleGenerator::initWasm(const CompileA
     }
 
     if (env_->startFuncIndex) {
         metadata_->startFuncIndex.emplace(*env_->startFuncIndex);
         if (!exportedFuncs_.put(*env_->startFuncIndex))
             return false;
     }
 
-    if (metadata_->debugEnabled) {
-        if (!debugFuncArgTypes_.resize(env_->funcSigs.length()))
-            return false;
-        if (!debugFuncReturnTypes_.resize(env_->funcSigs.length()))
-            return false;
-        for (size_t i = 0; i < debugFuncArgTypes_.length(); i++) {
-            if (!debugFuncArgTypes_[i].appendAll(env_->funcSigs[i]->args()))
-                return false;
-            debugFuncReturnTypes_[i] = env_->funcSigs[i]->ret();
-        }
-    }
-
     return true;
 }
 
 bool
-ModuleGenerator::init(UniqueModuleEnvironment env, const CompileArgs& args,
-                      CompileMode compileMode, Metadata* maybeAsmJSMetadata)
+ModuleGenerator::init(Metadata* maybeAsmJSMetadata)
 {
-    compileArgs_ = &args;
-    compileMode_ = compileMode;
-    env_ = Move(env);
-
     if (!funcToCodeRange_.appendN(BAD_CODE_RANGE, env_->funcSigs.length()))
         return false;
 
-    if (!assumptions_.clone(args.assumptions))
+    if (!assumptions_.clone(compileArgs_->assumptions))
         return false;
 
     if (!exportedFuncs_.init())
         return false;
 
-    if (env_->isAsmJS() ? !initAsmJS(maybeAsmJSMetadata) : !initWasm(args))
+    if (env_->isAsmJS() ? !initAsmJS(maybeAsmJSMetadata) : !initWasm())
         return false;
 
-    if (args.scriptedCaller.filename) {
-        metadata_->filename = DuplicateString(args.scriptedCaller.filename.get());
+    if (compileArgs_->scriptedCaller.filename) {
+        metadata_->filename = DuplicateString(compileArgs_->scriptedCaller.filename.get());
         if (!metadata_->filename)
             return false;
     }
 
     return true;
 }
 
-ModuleEnvironment&
-ModuleGenerator::mutableEnv()
-{
-    // Mutation is not safe during parallel compilation.
-    MOZ_ASSERT(!startedFuncDefs_ || finishedFuncDefs_);
-    return *env_;
-}
-
 bool
 ModuleGenerator::finishOutstandingTask()
 {
     MOZ_ASSERT(parallel_);
 
     CompileTask* task = nullptr;
     {
         AutoLockHelperThreadState lock;
         while (true) {
             MOZ_ASSERT(outstanding_ > 0);
 
-            if (HelperThreadState().wasmFailed(lock, compileMode_)) {
+            if (HelperThreadState().wasmFailed(lock, mode())) {
                 if (error_) {
                     MOZ_ASSERT(!*error_, "Should have stopped earlier");
-                    *error_ = Move(HelperThreadState().harvestWasmError(lock, compileMode_));
+                    *error_ = Move(HelperThreadState().harvestWasmError(lock, mode()));
                 }
                 return false;
             }
 
-            if (!HelperThreadState().wasmFinishedList(lock, compileMode_).empty()) {
+            if (!HelperThreadState().wasmFinishedList(lock, mode()).empty()) {
                 outstanding_--;
-                task = HelperThreadState().wasmFinishedList(lock, compileMode_).popCopy();
+                task = HelperThreadState().wasmFinishedList(lock, mode()).popCopy();
                 break;
             }
 
             HelperThreadState().wait(lock, GlobalHelperThreadState::CONSUMER);
         }
     }
 
     return finishTask(task);
@@ -883,40 +849,36 @@ ModuleGenerator::startFuncDefs()
     // blocking.
 
     GlobalHelperThreadState& threads = HelperThreadState();
     MOZ_ASSERT(threads.threadCount > 1);
 
     uint32_t numTasks;
     if (CanUseExtraThreads() &&
         threads.cpuCount > 1 &&
-        threads.wasmCompilationInProgress(compileMode_).compareExchange(false, true))
+        threads.wasmCompilationInProgress(mode()).compareExchange(false, true))
     {
 #ifdef DEBUG
         {
             AutoLockHelperThreadState lock;
-            MOZ_ASSERT(!HelperThreadState().wasmFailed(lock, compileMode_));
-            MOZ_ASSERT(HelperThreadState().wasmWorklist(lock, compileMode_).empty());
-            MOZ_ASSERT(HelperThreadState().wasmFinishedList(lock, compileMode_).empty());
+            MOZ_ASSERT(!HelperThreadState().wasmFailed(lock, mode()));
+            MOZ_ASSERT(HelperThreadState().wasmWorklist(lock, mode()).empty());
+            MOZ_ASSERT(HelperThreadState().wasmFinishedList(lock, mode()).empty());
         }
 #endif
         parallel_ = true;
         numTasks = 2 * threads.maxWasmCompilationThreads();
     } else {
         numTasks = 1;
     }
 
     if (!tasks_.initCapacity(numTasks))
         return false;
-    for (size_t i = 0; i < numTasks; i++) {
-        tasks_.infallibleEmplaceBack(*env_,
-                                     tier_,
-                                     compileMode_,
-                                     COMPILATION_LIFO_DEFAULT_CHUNK_SIZE);
-    }
+    for (size_t i = 0; i < numTasks; i++)
+        tasks_.infallibleEmplaceBack(*env_, COMPILATION_LIFO_DEFAULT_CHUNK_SIZE);
 
     if (!freeTasks_.reserve(numTasks))
         return false;
     for (size_t i = 0; i < numTasks; i++)
         freeTasks_.infallibleAppend(&tasks_[i]);
 
     startedFuncDefs_ = true;
     MOZ_ASSERT(!finishedFuncDefs_);
@@ -954,23 +916,21 @@ ModuleGenerator::startFuncDef(uint32_t l
 bool
 ModuleGenerator::launchBatchCompile()
 {
     MOZ_ASSERT(currentTask_);
 
     if (cancelled_ && *cancelled_)
         return false;
 
-    currentTask_->setDebugEnabled(metadata_->debugEnabled);
-
     size_t numBatchedFuncs = currentTask_->units().length();
     MOZ_ASSERT(numBatchedFuncs);
 
     if (parallel_) {
-        if (!StartOffThreadWasmCompile(currentTask_, compileMode_))
+        if (!StartOffThreadWasmCompile(currentTask_, mode()))
             return false;
         outstanding_++;
     } else {
         if (!CompileFunction(currentTask_, error_))
             return false;
         if (!finishTask(currentTask_))
             return false;
     }
@@ -981,26 +941,26 @@ ModuleGenerator::launchBatchCompile()
     numFinishedFuncDefs_ += numBatchedFuncs;
     return true;
 }
 
 bool
 ModuleGenerator::finishFuncDef(uint32_t funcIndex, FunctionGenerator* fg)
 {
     MOZ_ASSERT(activeFuncDef_ == fg);
-    MOZ_ASSERT_IF(compileMode_ == CompileMode::Tier1, funcIndex < env_->numFuncs());
+    MOZ_ASSERT_IF(mode() == CompileMode::Tier1, funcIndex < env_->numFuncs());
 
     UniqueFuncBytes func = Move(fg->funcBytes_);
     func->setFunc(funcIndex, &funcSig(funcIndex));
     uint32_t funcBytecodeLength = func->bytes().length();
     if (!currentTask_->units().emplaceBack(Move(func)))
         return false;
 
     uint32_t threshold;
-    switch (tier_) {
+    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);
     if (batchedBytecode_ > threshold && !launchBatchCompile())
@@ -1067,17 +1027,17 @@ ModuleGenerator::finishFuncDefs()
             MOZ_ASSERT(funcCodeRange(i).funcIndex() == i);
     }
 #endif
 
     // Complete element segments with the code range index of every element, now
     // that all functions have been compiled.
 
     for (ElemSegment& elems : env_->elemSegments) {
-        Uint32Vector& codeRangeIndices = elems.elemCodeRangeIndices(tier_);
+        Uint32Vector& codeRangeIndices = elems.elemCodeRangeIndices(tier());
 
         MOZ_ASSERT(codeRangeIndices.empty());
         if (!codeRangeIndices.reserve(elems.elemFuncIndices.length()))
             return false;
 
         for (uint32_t funcIndex : elems.elemFuncIndices)
             codeRangeIndices.infallibleAppend(funcToCodeRange_[funcIndex]);
     }
@@ -1116,17 +1076,17 @@ ModuleGenerator::initSigTableElems(uint3
         return false;
     for (size_t i = 0; i < elemFuncIndices.length(); i++)
         codeRangeIndices[i] = funcToCodeRange_[elemFuncIndices[i]];
 
     InitExpr offset(Val(uint32_t(0)));
     if (!env_->elemSegments.emplaceBack(tableIndex, offset, Move(elemFuncIndices)))
         return false;
 
-    env_->elemSegments.back().elemCodeRangeIndices(tier_) = Move(codeRangeIndices);
+    env_->elemSegments.back().elemCodeRangeIndices(tier()) = Move(codeRangeIndices);
     return true;
 }
 
 static_assert(sizeof(ModuleHash) <= sizeof(mozilla::SHA1Sum::Hash),
               "The ModuleHash size shall not exceed the SHA1 hash size.");
 
 void
 ModuleGenerator::generateBytecodeHash(const ShareableBytes& bytecode)
@@ -1153,21 +1113,31 @@ ModuleGenerator::finishMetadata(const Sh
     metadata_->memoryUsage = env_->memoryUsage;
     metadata_->minMemoryLength = env_->minMemoryLength;
     metadata_->maxMemoryLength = env_->maxMemoryLength;
     metadata_->tables = Move(env_->tables);
     metadata_->globals = Move(env_->globals);
     metadata_->funcNames = Move(env_->funcNames);
     metadata_->customSections = Move(env_->customSections);
 
-    // Additional debug information to copy.
-    metadata_->debugFuncArgTypes = Move(debugFuncArgTypes_);
-    metadata_->debugFuncReturnTypes = Move(debugFuncReturnTypes_);
-    if (metadata_->debugEnabled)
+    // Copy over additional debug information.
+    if (env_->debugEnabled()) {
+        metadata_->debugEnabled = true;
+        const size_t numSigs = env_->funcSigs.length();
+        if (!metadata_->debugFuncArgTypes.resize(numSigs))
+            return false;
+        if (!metadata_->debugFuncReturnTypes.resize(numSigs))
+            return false;
+        for (size_t i = 0; i < numSigs; i++) {
+            if (!metadata_->debugFuncArgTypes[i].appendAll(env_->funcSigs[i]->args()))
+                return false;
+            metadata_->debugFuncReturnTypes[i] = env_->funcSigs[i]->ret();
+        }
         metadataTier_->debugFuncToCodeRange = Move(funcToCodeRange_);
+    }
 
     // These Vectors can get large and the excess capacity can be significant,
     // so realloc them down to size.
     metadataTier_->memoryAccesses.podResizeToFit();
     metadataTier_->codeRanges.podResizeToFit();
     metadataTier_->callSites.podResizeToFit();
     metadataTier_->debugTrapFarJumpOffsets.podResizeToFit();
     metadataTier_->debugFuncToCodeRange.podResizeToFit();
@@ -1213,23 +1183,23 @@ ModuleGenerator::finishCodeSegment(const
         MOZ_ASSERT(debugTrapFarJumpOffset >= lastOffset);
         lastOffset = debugTrapFarJumpOffset;
     }
 #endif
 
     if (!finishLinkData())
         return nullptr;
 
-    return CodeSegment::create(tier_, masm_, bytecode, *linkDataTier_, *metadata_);
+    return CodeSegment::create(tier(), masm_, bytecode, *linkDataTier_, *metadata_);
 }
 
 UniqueJumpTable
 ModuleGenerator::createJumpTable(const CodeSegment& codeSegment)
 {
-    MOZ_ASSERT(compileMode_ == CompileMode::Tier1);
+    MOZ_ASSERT(mode() == CompileMode::Tier1);
     MOZ_ASSERT(!isAsmJS());
 
     uint32_t tableSize = env_->numFuncImports() + env_->numFuncDefs();
     UniqueJumpTable jumpTable(js_pod_calloc<void*>(tableSize));
     if (!jumpTable)
         return nullptr;
 
     uint8_t* codeBase = codeSegment.base();
@@ -1239,32 +1209,32 @@ ModuleGenerator::createJumpTable(const C
     }
 
     return jumpTable;
 }
 
 SharedModule
 ModuleGenerator::finishModule(const ShareableBytes& bytecode)
 {
-    MOZ_ASSERT(compileMode_ == CompileMode::Once || compileMode_ == CompileMode::Tier1);
+    MOZ_ASSERT(mode() == CompileMode::Once || mode() == CompileMode::Tier1);
 
     UniqueConstCodeSegment codeSegment = finishCodeSegment(bytecode);
     if (!codeSegment)
         return nullptr;
 
     UniqueJumpTable maybeJumpTable;
-    if (compileMode_ == CompileMode::Tier1) {
+    if (mode() == CompileMode::Tier1) {
         maybeJumpTable = createJumpTable(*codeSegment);
         if (!maybeJumpTable)
             return nullptr;
     }
 
     UniqueConstBytes maybeDebuggingBytes;
-    if (metadata_->debugEnabled) {
-        MOZ_ASSERT(compileMode_ == CompileMode::Once);
+    if (env_->debugEnabled()) {
+        MOZ_ASSERT(mode() == CompileMode::Once);
         Bytes bytes;
         if (!bytes.resize(masm_.bytesNeeded()))
             return nullptr;
         masm_.executableCopy(bytes.begin(), /* flushICache = */ false);
         maybeDebuggingBytes = js::MakeUnique<Bytes>(Move(bytes));
         if (!maybeDebuggingBytes)
             return nullptr;
     }
@@ -1280,40 +1250,40 @@ ModuleGenerator::finishModule(const Shar
                                        Move(env_->imports),
                                        Move(env_->exports),
                                        Move(env_->dataSegments),
                                        Move(env_->elemSegments),
                                        bytecode));
     if (!module)
         return nullptr;
 
-    if (compileMode_ == CompileMode::Tier1)
+    if (mode() == CompileMode::Tier1)
         module->startTier2(*compileArgs_);
 
     return module;
 }
 
 bool
 ModuleGenerator::finishTier2(Module& module)
 {
-    MOZ_ASSERT(compileMode_ == CompileMode::Tier2);
-    MOZ_ASSERT(tier_ == Tier::Ion);
-    MOZ_ASSERT(!metadata_->debugEnabled);
+    MOZ_ASSERT(mode() == CompileMode::Tier2);
+    MOZ_ASSERT(tier() == Tier::Ion);
+    MOZ_ASSERT(!env_->debugEnabled());
 
     if (cancelled_ && *cancelled_)
         return false;
 
     UniqueConstCodeSegment codeSegment = finishCodeSegment(module.bytecode());
     if (!codeSegment)
         return false;
 
-    module.finishTier2(linkData_.takeLinkData(tier_),
-                       metadata_->takeMetadata(tier_),
+    module.finishTier2(linkData_.takeLinkData(tier()),
+                       metadata_->takeMetadata(tier()),
                        Move(codeSegment),
-                       Move(env_));
+                       env_);
     return true;
 }
 
 bool
 wasm::CompileFunction(CompileTask* task, UniqueChars* error)
 {
     TraceLoggerThread* logger = TraceLoggerForCurrentThread();
     AutoTraceLog logCompile(logger, TraceLogger_WasmCompilation);
--- a/js/src/wasm/WasmGenerator.h
+++ b/js/src/wasm/WasmGenerator.h
@@ -22,24 +22,18 @@
 #include "jit/MacroAssembler.h"
 #include "wasm/WasmCompile.h"
 #include "wasm/WasmModule.h"
 #include "wasm/WasmValidate.h"
 
 namespace js {
 namespace wasm {
 
+struct CompileArgs;
 struct ModuleEnvironment;
-
-typedef Vector<jit::MIRType, 8, SystemAllocPolicy> MIRTypeVector;
-typedef jit::ABIArgIter<MIRTypeVector> ABIArgMIRTypeIter;
-typedef jit::ABIArgIter<ValTypeVector> ABIArgValTypeIter;
-
-struct CompileArgs;
-
 class FunctionGenerator;
 
 // The FuncBytes class represents a single, concurrently-compilable function.
 // A FuncBytes object is composed of the wasm function body bytes along with the
 // ambient metadata describing the function necessary to compile it.
 
 class FuncBytes
 {
@@ -127,38 +121,32 @@ typedef Vector<FuncCompileUnit, 8, Syste
 // filled with a certain number of function's bodies that are sent off to a
 // compilation helper thread, which fills in the resulting code offsets, and
 // finally sent back to the validation thread. To save time allocating and
 // freeing memory, CompileTasks are reset() and reused.
 
 class CompileTask
 {
     const ModuleEnvironment&   env_;
-    Tier                       tier_;
-    CompileMode                mode_;
     LifoAlloc                  lifo_;
     Maybe<jit::TempAllocator>  alloc_;
     Maybe<jit::MacroAssembler> masm_;
     FuncCompileUnitVector      units_;
-    bool                       debugEnabled_;
 
     CompileTask(const CompileTask&) = delete;
     CompileTask& operator=(const CompileTask&) = delete;
 
     void init() {
         alloc_.emplace(&lifo_);
         masm_.emplace(jit::MacroAssembler::WasmToken(), *alloc_);
-        debugEnabled_ = false;
     }
 
   public:
-    CompileTask(const ModuleEnvironment& env, Tier tier, CompileMode mode, size_t defaultChunkSize)
+    CompileTask(const ModuleEnvironment& env, size_t defaultChunkSize)
       : env_(env),
-        tier_(tier),
-        mode_(mode),
         lifo_(defaultChunkSize)
     {
         init();
     }
     LifoAlloc& lifo() {
         return lifo_;
     }
     jit::TempAllocator& alloc() {
@@ -169,26 +157,23 @@ class CompileTask
     }
     jit::MacroAssembler& masm() {
         return *masm_;
     }
     FuncCompileUnitVector& units() {
         return units_;
     }
     Tier tier() const {
-        return tier_;
+        return env_.tier;
     }
     CompileMode mode() const {
-        return mode_;
+        return env_.mode;
     }
     bool debugEnabled() const {
-        return debugEnabled_;
-    }
-    void setDebugEnabled(bool enabled) {
-        debugEnabled_ = enabled;
+        return env_.debug == DebugEnabled::True;
     }
     bool reset(UniqueFuncBytesVector* freeFuncBytes) {
         for (FuncCompileUnit& unit : units_) {
             if (!freeFuncBytes->emplaceBack(Move(unit.recycle())))
                 return false;
         }
 
         units_.clear();
@@ -212,45 +197,41 @@ struct Tier2GeneratorTask;
 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, CallableOffsets> TrapExitOffsetArray;
 
     // Constant parameters
-    SharedCompileArgs               compileArgs_;
-    CompileMode                     compileMode_;
-    Tier                            tier_;
-    UniqueChars*                    error_;
-    Atomic<bool>*                   cancelled_;
+    SharedCompileArgs const         compileArgs_;
+    UniqueChars* const              error_;
+    Atomic<bool>* const             cancelled_;
+    ModuleEnvironment* const        env_;
 
     // Data that is moved into the result of finish()
     Assumptions                     assumptions_;
     LinkDataTier*                   linkDataTier_; // Owned by linkData_
     LinkData                        linkData_;
     MetadataTier*                   metadataTier_; // Owned by metadata_
     MutableMetadata                 metadata_;
     UniqueJumpTable                 jumpTable_;
 
     // Data scoped to the ModuleGenerator's lifetime
-    UniqueModuleEnvironment         env_;
     uint32_t                        numSigs_;
     uint32_t                        numTables_;
     LifoAlloc                       lifo_;
     jit::JitContext                 jcx_;
     jit::TempAllocator              masmAlloc_;
     jit::MacroAssembler             masm_;
     Uint32Vector                    funcToCodeRange_;
     Uint32Set                       exportedFuncs_;
     uint32_t                        lastPatchedCallsite_;
     uint32_t                        startOfUnpatchedCallsites_;
     Uint32Vector                    debugTrapFarJumps_;
-    FuncArgTypesVector              debugFuncArgTypes_;
-    FuncReturnTypesVector           debugFuncReturnTypes_;
 
     // Parallel compilation
     bool                            parallel_;
     uint32_t                        outstanding_;
     CompileTaskVector               tasks_;
     CompileTaskPtrVector            freeTasks_;
     UniqueFuncBytesVector           freeFuncBytes_;
     CompileTask*                    currentTask_;
@@ -278,57 +259,42 @@ class MOZ_STACK_CLASS ModuleGenerator
     UniqueJumpTable createJumpTable(const CodeSegment& codeSegment);
     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);
+    MOZ_MUST_USE bool initWasm();
+
+    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:
-    explicit ModuleGenerator(UniqueChars* error, Atomic<bool>* cancelled);
+    ModuleGenerator(const CompileArgs& args, ModuleEnvironment* env,
+                    Atomic<bool>* cancelled, UniqueChars* error);
     ~ModuleGenerator();
 
-    MOZ_MUST_USE bool init(UniqueModuleEnvironment env, const CompileArgs& args,
-                           CompileMode compileMode = CompileMode::Once,
-                           Metadata* maybeAsmJSMetadata = nullptr);
-
-    const ModuleEnvironment& env() const { return *env_; }
-    ModuleEnvironment& mutableEnv();
-
-    bool isAsmJS() const { return metadata_->kind == ModuleKind::AsmJS; }
-    CompileMode mode() const { return compileMode_; }
-    Tier tier() const { return tier_; }
-    jit::MacroAssembler& masm() { return masm_; }
-
-    // Memory:
-    bool usesMemory() const { return env_->usesMemory(); }
-    uint32_t minMemoryLength() const { return env_->minMemoryLength; }
-
-    // Tables:
-    uint32_t numTables() const { return numTables_; }
-    const TableDescVector& tables() const { return env_->tables; }
-
-    // Signatures:
-    uint32_t numSigs() const { return numSigs_; }
-    const SigWithId& sig(uint32_t sigIndex) const;
-    const SigWithId& funcSig(uint32_t funcIndex) const;
-    const SigWithIdPtrVector& funcSigs() const { return env_->funcSigs; }
-
-    // Globals:
-    const GlobalDescVector& globals() const { return env_->globals; }
+    MOZ_MUST_USE bool init(Metadata* maybeAsmJSMetadata = nullptr);
 
     // Function definitions:
     MOZ_MUST_USE bool startFuncDefs();
     MOZ_MUST_USE bool startFuncDef(uint32_t lineOrBytecode, FunctionGenerator* fg);
     MOZ_MUST_USE bool finishFuncDef(uint32_t funcIndex, FunctionGenerator* fg);
     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;
+
     // 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);
@@ -350,44 +316,23 @@ class MOZ_STACK_CLASS ModuleGenerator
 // After the body is complete, ModuleGenerator::finishFuncDef must be called
 // before the FunctionGenerator is destroyed and the next function is started.
 
 class MOZ_STACK_CLASS FunctionGenerator
 {
     friend class ModuleGenerator;
 
     ModuleGenerator* m_;
-    bool             usesSimd_;
-    bool             usesAtomics_;
-
     UniqueFuncBytes  funcBytes_;
 
   public:
     FunctionGenerator()
-      : m_(nullptr), usesSimd_(false), usesAtomics_(false), funcBytes_(nullptr)
+      : m_(nullptr), funcBytes_(nullptr)
     {}
 
-    bool usesSimd() const {
-        return usesSimd_;
-    }
-    void setUsesSimd() {
-        usesSimd_ = true;
-    }
-
-    bool usesAtomics() const {
-        return usesAtomics_;
-    }
-    void setUsesAtomics() {
-        usesAtomics_ = true;
-    }
-
-    bool isAsmJS() const {
-      return m_->isAsmJS();
-    }
-
     Bytes& bytes() {
         return funcBytes_->bytes();
     }
     MOZ_MUST_USE bool addCallSiteLineNum(uint32_t lineno) {
         return funcBytes_->addCallSiteLineNum(lineno);
     }
 };
 
--- a/js/src/wasm/WasmIonCompile.cpp
+++ b/js/src/wasm/WasmIonCompile.cpp
@@ -186,17 +186,17 @@ class FunctionCompiler
 
         const ValTypeVector& args = func_.sig().args();
 
         if (!mirGen_.ensureBallast())
             return false;
         if (!newBlock(/* prev */ nullptr, &curBlock_))
             return false;
 
-        for (ABIArgValTypeIter i(args); !i.done(); i++) {
+        for (ABIArgIter<ValTypeVector> i(args); !i.done(); i++) {
             MWasmParameter* ins = MWasmParameter::New(alloc(), *i, i.mirType());
             curBlock_->add(ins);
             curBlock_->initSlot(info().localSlot(i.index()), ins);
             if (!mirGen_.ensureBallast())
                 return false;
         }
 
         // Set up a parameter that receives the hidden TLS pointer argument.
--- a/js/src/wasm/WasmModule.cpp
+++ b/js/src/wasm/WasmModule.cpp
@@ -300,17 +300,17 @@ Module::notifyCompilationListeners()
     }
 
     for (RefPtr<JS::WasmModuleListener>& listener : listeners)
         listener->onCompilationComplete();
 }
 
 void
 Module::finishTier2(UniqueLinkDataTier linkData2, UniqueMetadataTier metadata2,
-                    UniqueConstCodeSegment code2, UniqueModuleEnvironment env2)
+                    UniqueConstCodeSegment code2, ModuleEnvironment* 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)));
--- a/js/src/wasm/WasmModule.h
+++ b/js/src/wasm/WasmModule.h
@@ -221,17 +221,17 @@ class Module : public JS::WasmModule
     // Tier-2 compilation may be initiated after the Module is constructed at
     // most once, ideally before any client can attempt to serialize the Module.
     // When tier-2 compilation completes, ModuleGenerator calls finishTier2()
     // from a helper thread, passing tier-variant data which will be installed
     // and made visible.
 
     void startTier2(const CompileArgs& args);
     void finishTier2(UniqueLinkDataTier linkData2, UniqueMetadataTier metadata2,
-                     UniqueConstCodeSegment code2, UniqueModuleEnvironment env2);
+                     UniqueConstCodeSegment code2, ModuleEnvironment* 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.
 
--- a/js/src/wasm/WasmStubs.cpp
+++ b/js/src/wasm/WasmStubs.cpp
@@ -26,16 +26,20 @@
 #include "jit/MacroAssembler-inl.h"
 
 using namespace js;
 using namespace js::jit;
 using namespace js::wasm;
 
 using mozilla::ArrayLength;
 
+typedef Vector<jit::MIRType, 8, SystemAllocPolicy> MIRTypeVector;
+typedef jit::ABIArgIter<MIRTypeVector> ABIArgMIRTypeIter;
+typedef jit::ABIArgIter<ValTypeVector> ABIArgValTypeIter;
+
 static void
 FinishOffsets(MacroAssembler& masm, Offsets* offsets)
 {
     // On old ARM hardware, constant pools could be inserted and they need to
     // be flushed before considering the size of the masm.
     masm.flushBuffer();
     offsets->end = masm.size();
 }
--- a/js/src/wasm/WasmTypes.h
+++ b/js/src/wasm/WasmTypes.h
@@ -356,29 +356,37 @@ ToCString(ValType type)
 // linkdata.  The tiers are normally explicit (Baseline and Ion); implicit tiers
 // can be obtained through accessors on Code objects (eg, stableTier).
 
 enum class Tier
 {
     Baseline,
     Debug = Baseline,
     Ion,
-    Serialized = Ion,
+    Serialized = Ion
 };
 
 // The CompileMode controls how compilation of a module is performed (notably,
 // how many times we compile it).
 
 enum class CompileMode
 {
     Once,
     Tier1,
     Tier2
 };
 
+// Typed enum for whether debugging is enabled.
+
+enum class DebugEnabled
+{
+    False,
+    True
+};
+
 // Iterator over tiers present in a tiered data structure.
 
 class Tiers
 {
     Tier t_[2];
     uint32_t n_;
 
   public:
--- a/js/src/wasm/WasmValidate.h
+++ b/js/src/wasm/WasmValidate.h
@@ -30,37 +30,48 @@ namespace wasm {
 // code section and then used immutably during. When compiling a module using a
 // ModuleGenerator, the ModuleEnvironment holds state shared between the
 // ModuleGenerator thread and background compile threads. All the threads
 // are given a read-only view of the ModuleEnvironment, thus preventing race
 // conditions.
 
 struct ModuleEnvironment
 {
-    ModuleKind                kind;
+    // Constant parameters for the entire compilation:
+    const CompileMode         mode;
+    const Tier                tier;
+    const DebugEnabled        debug;
+    const ModuleKind          kind;
+
+    // Module fields filled out incrementally during decoding:
     MemoryUsage               memoryUsage;
     Atomic<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;
     ElemSegmentVector         elemSegments;
     DataSegmentVector         dataSegments;
     NameInBytecodeVector      funcNames;
     CustomSectionVector       customSections;
 
-    explicit ModuleEnvironment(ModuleKind kind = ModuleKind::Wasm)
-      : kind(kind),
+    explicit ModuleEnvironment(CompileMode mode = CompileMode::Once,
+                               Tier tier = Tier::Ion,
+                               DebugEnabled debug = DebugEnabled::False,
+                               ModuleKind kind = ModuleKind::Wasm)
+      : mode(mode),
+        tier(tier),
+        debug(debug),
+        kind(kind),
         memoryUsage(MemoryUsage::None),
         minMemoryLength(0)
     {}
 
     size_t numTables() const {
         return tables.length();
     }
     size_t numSigs() const {
@@ -86,26 +97,27 @@ struct ModuleEnvironment
         return funcImportGlobalDataOffsets.length();
     }
     bool usesMemory() const {
         return UsesMemory(memoryUsage);
     }
     bool isAsmJS() const {
         return kind == ModuleKind::AsmJS;
     }
+    bool debugEnabled() const {
+        return debug == DebugEnabled::True;
+    }
     bool funcIsImport(uint32_t funcIndex) const {
         return funcIndex < funcImportGlobalDataOffsets.length();
     }
     uint32_t funcIndexToSigIndex(uint32_t funcIndex) const {
         return funcSigs[funcIndex] - sigs.begin();
     }
 };
 
-typedef UniquePtr<ModuleEnvironment> UniqueModuleEnvironment;
-
 // The Encoder class appends bytes to the Bytes object it is given during
 // construction. The client is responsible for the Bytes's lifetime and must
 // keep the Bytes alive as long as the Encoder is used.
 
 class Encoder
 {
     Bytes& bytes_;