Bug 1487329 - Select wasm baseline compiler if content opts into gc types. r=bbouvier
☠☠ backed out by 15812ad59a90 ☠ ☠
authorLars T Hansen <lhansen@mozilla.com>
Mon, 03 Sep 2018 20:02:38 +0200
changeset 434745 7e8d4a1689601b48d152978b87da7b6dbfd249b2
parent 434744 2168cbf8a779fe9c7f9ecefb8f57d11ca3ac2fb1
child 434746 0c373148a649d6e0c0e97a5f94e5d873d345f505
push id107482
push userlhansen@mozilla.com
push dateWed, 05 Sep 2018 08:58:11 +0000
treeherdermozilla-inbound@0c373148a649 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbbouvier
bugs1487329
milestone64.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 1487329 - Select wasm baseline compiler if content opts into gc types. r=bbouvier Creates a level of indirection to encapsulate the compilation parameters (mode, tier, debug, gc-enabled) so as to allow their computation to be delayed. This centers on the new struct CompilerEnvironment, defined in WasmValidate.h. After this change, compiler selection is driven by the presence of the gc-feature-opt-in section. We finalize the values in CompilerEnvironment after having parsed that section. (That parsing is still under #ifdef.) --wasm-gc is still used as a higher-level control; if it is not present there will be no gc support at all. But once we remove that flag, very little will change here; all code that reads that flag can instead pass HasGcTypes::True, and compiler selection will be entirely driven by the presence of the opt-in section. There are a few too many uses of HasGcTypes::False here; most of these will disappear along with the --wasm-gc flag.
js/src/wasm/AsmJS.cpp
js/src/wasm/WasmBaselineCompile.cpp
js/src/wasm/WasmCompile.cpp
js/src/wasm/WasmGenerator.cpp
js/src/wasm/WasmGenerator.h
js/src/wasm/WasmIonCompile.cpp
js/src/wasm/WasmValidate.cpp
js/src/wasm/WasmValidate.h
--- a/js/src/wasm/AsmJS.cpp
+++ b/js/src/wasm/AsmJS.cpp
@@ -1465,16 +1465,17 @@ class MOZ_STACK_CLASS JS_HAZ_ROOTED Modu
     FuncVector            funcDefs_;
     TableVector           tables_;
     GlobalMap             globalMap_;
     SigSet                sigSet_;
     FuncImportMap         funcImportMap_;
     ArrayViewVector       arrayViews_;
 
     // State used to build the AsmJSModule in finish():
+    CompilerEnvironment   compilerEnv_;
     ModuleEnvironment     env_;
     MutableAsmJSMetadata  asmJSMetadata_;
 
     // Error reporting:
     UniqueChars           errorString_;
     uint32_t              errorOffset_;
     bool                  errorOverRecursed_;
 
@@ -1525,22 +1526,23 @@ class MOZ_STACK_CLASS JS_HAZ_ROOTED Modu
         dummyFunction_(cx),
         validationLifo_(VALIDATION_LIFO_DEFAULT_CHUNK_SIZE),
         funcDefs_(cx),
         tables_(cx),
         globalMap_(cx),
         sigSet_(cx),
         funcImportMap_(cx),
         arrayViews_(cx),
-        env_(CompileMode::Once, Tier::Ion, DebugEnabled::False, HasGcTypes::False,
-             Shareable::False, ModuleKind::AsmJS),
+        compilerEnv_(CompileMode::Once, Tier::Ion, DebugEnabled::False, HasGcTypes::False),
+        env_(HasGcTypes::False, &compilerEnv_, Shareable::False, ModuleKind::AsmJS),
         errorString_(nullptr),
         errorOffset_(UINT32_MAX),
         errorOverRecursed_(false)
     {
+        compilerEnv_.computeParameters(HasGcTypes::False);
         env_.minMemoryLength = RoundUpToNextValidAsmJSHeapLength(0);
     }
 
     ~ModuleValidator() {
         if (errorString_) {
             MOZ_ASSERT(errorOffset_ != UINT32_MAX);
             typeFailure(errorOffset_, errorString_.get());
         }
--- a/js/src/wasm/WasmBaselineCompile.cpp
+++ b/js/src/wasm/WasmBaselineCompile.cpp
@@ -3377,17 +3377,17 @@ class BaseCompiler final : public BaseCo
     //
     // Function prologue and epilogue.
 
     void beginFunction() {
         JitSpew(JitSpew_Codegen, "# Emitting wasm baseline code");
 
         GenerateFunctionPrologue(masm,
                                  env_.funcTypes[func_.index]->id,
-                                 env_.mode == CompileMode::Tier1 ? Some(func_.index) : Nothing(),
+                                 env_.mode() == CompileMode::Tier1 ? Some(func_.index) : Nothing(),
                                  &offsets_);
 
         // Initialize DebugFrame fields before the stack overflow trap so that
         // we have the invariant that all observable Frames in a debugEnabled
         // Module have valid DebugFrames.
         if (env_.debugEnabled()) {
 #ifdef JS_CODEGEN_ARM64
             static_assert(DebugFrame::offsetOfFrame() % WasmStackAlignment == 0, "aligned");
@@ -10347,17 +10347,17 @@ js::wasm::BaselineCanCompile()
 #endif
 }
 
 bool
 js::wasm::BaselineCompileFunctions(const ModuleEnvironment& env, LifoAlloc& lifo,
                                    const FuncCompileInputVector& inputs, CompiledCode* code,
                                    UniqueChars* error)
 {
-    MOZ_ASSERT(env.tier == Tier::Baseline);
+    MOZ_ASSERT(env.tier() == Tier::Baseline);
     MOZ_ASSERT(env.kind == ModuleKind::Wasm);
 
     // The MacroAssembler will sometimes access the jitContext.
 
     TempAllocator alloc(&lifo);
     JitContext jitContext(&alloc);
     MOZ_ASSERT(IsCompilingWasm());
     WasmMacroAssembler masm(alloc);
--- a/js/src/wasm/WasmCompile.cpp
+++ b/js/src/wasm/WasmCompile.cpp
@@ -76,21 +76,21 @@ CompileArgs::CompileArgs(JSContext* cx, 
   : scriptedCaller(std::move(scriptedCaller))
 {
 #ifdef ENABLE_WASM_GC
     bool gcEnabled = cx->options().wasmGc();
 #else
     bool gcEnabled = false;
 #endif
 
-    baselineEnabled = cx->options().wasmBaseline() || gcEnabled;
-    ionEnabled = cx->options().wasmIon() && !gcEnabled;
+    baselineEnabled = cx->options().wasmBaseline();
+    ionEnabled = cx->options().wasmIon();
     sharedMemoryEnabled = cx->realm()->creationOptions().getSharedMemoryAndAtomicsEnabled();
     gcTypesConfigured = gcEnabled ? HasGcTypes::True : HasGcTypes::False;
-    testTiering = (cx->options().testWasmAwaitTier2() || JitOptions.wasmDelayTier2) && !gcEnabled;
+    testTiering = cx->options().testWasmAwaitTier2() || JitOptions.wasmDelayTier2;
 
     // Debug information such as source view or debug traps will require
     // additional memory and permanently stay in baseline code, so we try to
     // only enable it when a developer actually cares: when the debugger tab
     // is open.
     debugEnabled = cx->realm()->debuggerObservesAsmJS();
 }
 
@@ -368,45 +368,87 @@ TieringBeneficial(uint32_t codeSize)
 
     if ((MaxCodeBytesPerProcess - availMemory) + needMemory > cutoff)
         return false;
 #endif
 
     return true;
 }
 
-static void
-InitialCompileFlags(const CompileArgs& args, Decoder& d, CompileMode* mode, Tier* tier,
-                    DebugEnabled* debug)
+CompilerEnvironment::CompilerEnvironment(const CompileArgs& args)
+  : state_(InitialWithArgs),
+    args_(&args)
+{
+}
+
+CompilerEnvironment::CompilerEnvironment(CompileMode mode,
+                                         Tier tier,
+                                         DebugEnabled debugEnabled,
+                                         HasGcTypes gcTypesConfigured)
+  : state_(InitialWithModeTierDebug),
+    mode_(mode),
+    tier_(tier),
+    debug_(debugEnabled),
+    gcTypes_(gcTypesConfigured)
+{
+}
+
+void
+CompilerEnvironment::computeParameters(HasGcTypes gcFeatureOptIn)
 {
+    MOZ_ASSERT(state_ == InitialWithModeTierDebug);
+
+    if (gcTypes_ == HasGcTypes::True)
+        gcTypes_ = gcFeatureOptIn;
+    state_ = Computed;
+}
+
+void
+CompilerEnvironment::computeParameters(Decoder& d, HasGcTypes gcFeatureOptIn)
+{
+    MOZ_ASSERT(!isComputed());
+
+    if (state_ == InitialWithModeTierDebug) {
+        computeParameters(gcFeatureOptIn);
+        return;
+    }
+
+    bool gcEnabled = args_->gcTypesConfigured == HasGcTypes::True &&
+                     gcFeatureOptIn == HasGcTypes::True;
+    bool argBaselineEnabled = args_->baselineEnabled || gcEnabled;
+    bool argIonEnabled = args_->ionEnabled && !gcEnabled;
+    bool argTestTiering = args_->testTiering && !gcEnabled;
+    bool argDebugEnabled = args_->debugEnabled;
+
     uint32_t codeSectionSize = 0;
 
     SectionRange range;
     if (StartsCodeSection(d.begin(), d.end(), &range))
         codeSectionSize = range.size;
 
     // Attempt to default to ion if baseline is disabled.
-    bool baselineEnabled = BaselineCanCompile() && (args.baselineEnabled || args.testTiering);
-    bool debugEnabled = BaselineCanCompile() && args.debugEnabled;
-    bool ionEnabled = IonCanCompile() && (args.ionEnabled || !baselineEnabled || args.testTiering);
+    bool baselineEnabled = BaselineCanCompile() && (argBaselineEnabled || argTestTiering);
+    bool debugEnabled = BaselineCanCompile() && argDebugEnabled;
+    bool ionEnabled = IonCanCompile() && (argIonEnabled || !baselineEnabled || argTestTiering);
 
     // HasCompilerSupport() should prevent failure here
     MOZ_RELEASE_ASSERT(baselineEnabled || ionEnabled);
 
     if (baselineEnabled && ionEnabled && !debugEnabled && CanUseExtraThreads() &&
-        (TieringBeneficial(codeSectionSize) || args.testTiering))
+        (TieringBeneficial(codeSectionSize) || argTestTiering))
     {
-        *mode = CompileMode::Tier1;
-        *tier = Tier::Baseline;
+        mode_ = CompileMode::Tier1;
+        tier_ = Tier::Baseline;
     } else {
-        *mode = CompileMode::Once;
-        *tier = debugEnabled || !ionEnabled ? Tier::Baseline : Tier::Ion;
+        mode_ = CompileMode::Once;
+        tier_ = debugEnabled || !ionEnabled ? Tier::Baseline : Tier::Ion;
     }
-
-    *debug = debugEnabled ? DebugEnabled::True : DebugEnabled::False;
+    debug_ = debugEnabled ? DebugEnabled::True : DebugEnabled::False;
+    gcTypes_ = gcEnabled ? HasGcTypes::True : HasGcTypes::False;
+    state_ = Computed;
 }
 
 template <class DecoderT>
 static bool
 DecodeFunctionBody(DecoderT& d, ModuleGenerator& mg, uint32_t funcIndex)
 {
     uint32_t bodySize;
     if (!d.readVarU32(&bodySize))
@@ -457,22 +499,19 @@ DecodeCodeSection(const ModuleEnvironmen
 SharedModule
 wasm::CompileBuffer(const CompileArgs& args, const ShareableBytes& bytecode, UniqueChars* error,
                     UniqueCharsVector* warnings)
 {
     MOZ_RELEASE_ASSERT(wasm::HaveSignalHandlers());
 
     Decoder d(bytecode.bytes, 0, error, warnings);
 
-    CompileMode mode;
-    Tier tier;
-    DebugEnabled debug;
-    InitialCompileFlags(args, d, &mode, &tier, &debug);
-
-    ModuleEnvironment env(mode, tier, debug, args.gcTypesConfigured,
+    CompilerEnvironment compilerEnv(args);
+    ModuleEnvironment env(args.gcTypesConfigured,
+                          &compilerEnv,
                           args.sharedMemoryEnabled ? Shareable::True : Shareable::False);
     if (!DecodeModuleEnvironment(d, &env))
         return nullptr;
 
     ModuleGenerator mg(args, &env, nullptr, error);
     if (!mg.init())
         return nullptr;
 
@@ -488,23 +527,27 @@ wasm::CompileBuffer(const CompileArgs& a
 void
 wasm::CompileTier2(const CompileArgs& args, Module& module, Atomic<bool>* cancelled)
 {
     MOZ_RELEASE_ASSERT(wasm::HaveSignalHandlers());
 
     UniqueChars error;
     Decoder d(module.bytecode().bytes, 0, &error);
 
-    MOZ_ASSERT(args.gcTypesConfigured == HasGcTypes::False, "can't ion-compile with gc types yet");
-
-    ModuleEnvironment env(CompileMode::Tier2, Tier::Ion, DebugEnabled::False, HasGcTypes::False,
+    HasGcTypes gcTypesConfigured = HasGcTypes::False; // No Ion support yet
+    CompilerEnvironment compilerEnv(CompileMode::Tier2, Tier::Ion, DebugEnabled::False,
+                                    gcTypesConfigured);
+    ModuleEnvironment env(gcTypesConfigured,
+                          &compilerEnv,
                           args.sharedMemoryEnabled ? Shareable::True : Shareable::False);
     if (!DecodeModuleEnvironment(d, &env))
         return;
 
+    MOZ_ASSERT(env.gcTypesEnabled() == HasGcTypes::False, "can't ion-compile with gc types yet");
+
     ModuleGenerator mg(args, &env, cancelled, &error);
     if (!mg.init())
         return;
 
     if (!DecodeCodeSection(env, d, mg))
         return;
 
     if (!DecodeModuleTail(d, &env))
@@ -612,22 +655,19 @@ wasm::CompileStreaming(const CompileArgs
 {
     MOZ_ASSERT(wasm::HaveSignalHandlers());
 
     Maybe<ModuleEnvironment> env;
 
     {
         Decoder d(envBytes, 0, error, warnings);
 
-        CompileMode mode;
-        Tier tier;
-        DebugEnabled debug;
-        InitialCompileFlags(args, d, &mode, &tier, &debug);
-
-        env.emplace(mode, tier, debug, args.gcTypesConfigured,
+        CompilerEnvironment compilerEnv(args);
+        env.emplace(args.gcTypesConfigured,
+                    &compilerEnv,
                     args.sharedMemoryEnabled ? Shareable::True : Shareable::False);
         if (!DecodeModuleEnvironment(d, env.ptr()))
             return nullptr;
 
         MOZ_ASSERT(d.done());
     }
 
     ModuleGenerator mg(args, env.ptr(), &cancelled, error);
--- a/js/src/wasm/WasmGenerator.cpp
+++ b/js/src/wasm/WasmGenerator.cpp
@@ -602,17 +602,17 @@ ModuleGenerator::linkCompiledCode(const 
 }
 
 static bool
 ExecuteCompileTask(CompileTask* task, UniqueChars* error)
 {
     MOZ_ASSERT(task->lifo.isEmpty());
     MOZ_ASSERT(task->output.empty());
 
-    switch (task->env.tier) {
+    switch (task->env.tier()) {
       case Tier::Ion:
         if (!IonCompileFunctions(task->env, task->lifo, task->inputs, &task->output, error))
             return false;
         break;
       case Tier::Baseline:
         if (!BaselineCompileFunctions(task->env, task->lifo, task->inputs, &task->output, error))
             return false;
         break;
--- a/js/src/wasm/WasmGenerator.h
+++ b/js/src/wasm/WasmGenerator.h
@@ -193,18 +193,18 @@ class MOZ_STACK_CLASS ModuleGenerator
     bool finishTask(CompileTask* task);
     bool launchBatchCompile();
     bool finishOutstandingTask();
     bool finishCode();
     bool finishMetadata(const ShareableBytes& bytecode);
     UniqueModuleSegment finish(const ShareableBytes& bytecode);
 
     bool isAsmJS() const { return env_->isAsmJS(); }
-    Tier tier() const { return env_->tier; }
-    CompileMode mode() const { return env_->mode; }
+    Tier tier() const { return env_->tier(); }
+    CompileMode mode() const { return env_->mode(); }
     bool debugEnabled() const { return env_->debugEnabled(); }
 
   public:
     ModuleGenerator(const CompileArgs& args, ModuleEnvironment* env,
                     const Atomic<bool>* cancelled, UniqueChars* error);
     ~ModuleGenerator();
     MOZ_MUST_USE bool init(Metadata* maybeAsmJSMetadata = nullptr);
 
--- a/js/src/wasm/WasmIonCompile.cpp
+++ b/js/src/wasm/WasmIonCompile.cpp
@@ -3478,17 +3478,17 @@ EmitBodyExprs(FunctionCompiler& f)
 #undef CHECK
 }
 
 bool
 wasm::IonCompileFunctions(const ModuleEnvironment& env, LifoAlloc& lifo,
                           const FuncCompileInputVector& inputs, CompiledCode* code,
                           UniqueChars* error)
 {
-    MOZ_ASSERT(env.tier == Tier::Ion);
+    MOZ_ASSERT(env.tier() == Tier::Ion);
 
     TempAllocator alloc(&lifo);
     JitContext jitContext(&alloc);
     MOZ_ASSERT(IsCompilingWasm());
     WasmMacroAssembler masm(alloc);
 
     // Swap in already-allocated empty vectors to avoid malloc/free.
     MOZ_ASSERT(code->empty());
--- a/js/src/wasm/WasmValidate.cpp
+++ b/js/src/wasm/WasmValidate.cpp
@@ -2067,18 +2067,23 @@ bool
 wasm::DecodeModuleEnvironment(Decoder& d, ModuleEnvironment* env)
 {
     if (!DecodePreamble(d))
         return false;
 
 #ifdef ENABLE_WASM_GC
     if (!DecodeGCFeatureOptInSection(d, env))
         return false;
+    HasGcTypes gcFeatureOptIn = env->gcFeatureOptIn;
+#else
+    HasGcTypes gcFeatureOptIn = HasGcTypes::False;
 #endif
 
+    env->compilerEnv->computeParameters(d, gcFeatureOptIn);
+
     if (!DecodeTypeSection(d, env))
         return false;
 
     if (!DecodeImportSection(d, env))
         return false;
 
     if (!DecodeFunctionSection(d, env))
         return false;
@@ -2337,22 +2342,25 @@ wasm::DecodeModuleTail(Decoder& d, Modul
 // Validate algorithm.
 
 bool
 wasm::Validate(JSContext* cx, const ShareableBytes& bytecode, UniqueChars* error)
 {
     Decoder d(bytecode.bytes, 0, error);
 
 #ifdef ENABLE_WASM_GC
-    HasGcTypes gcSupport = cx->options().wasmGc() ? HasGcTypes::True : HasGcTypes::False;
+    HasGcTypes gcTypesConfigured = cx->options().wasmGc() ? HasGcTypes::True : HasGcTypes::False;
 #else
-    HasGcTypes gcSupport = HasGcTypes::False;
+    HasGcTypes gcTypesConfigured = HasGcTypes::False;
 #endif
 
-    ModuleEnvironment env(CompileMode::Once, Tier::Ion, DebugEnabled::False, gcSupport,
+    CompilerEnvironment compilerEnv(CompileMode::Once, Tier::Ion, DebugEnabled::False,
+                                    gcTypesConfigured);
+    ModuleEnvironment env(gcTypesConfigured,
+                          &compilerEnv,
                           cx->realm()->creationOptions().getSharedMemoryAndAtomicsEnabled()
                           ? Shareable::True
                           : Shareable::False);
     if (!DecodeModuleEnvironment(d, &env))
         return false;
 
     if (!DecodeCodeSection(d, &env))
         return false;
--- a/js/src/wasm/WasmValidate.h
+++ b/js/src/wasm/WasmValidate.h
@@ -40,42 +40,115 @@ struct SectionRange
     }
     bool operator==(const SectionRange& rhs) const {
         return start == rhs.start && size == rhs.size;
     }
 };
 
 typedef Maybe<SectionRange> MaybeSectionRange;
 
+// CompilerEnvironment holds any values that will be needed to compute
+// compilation parameters once the module's feature opt-in sections have been
+// parsed.
+//
+// Subsequent to construction a computeParameters() call will compute the final
+// compilation parameters, and the object can then be queried for their values.
+
+struct CompileArgs;
+class Decoder;
+
+struct CompilerEnvironment
+{
+    // The object starts in one of two "initial" states; computeParameters moves
+    // it into the "computed" state.
+    enum State
+    {
+        InitialWithArgs,
+        InitialWithModeTierDebug,
+        Computed
+    };
+
+    State state_;
+    union {
+        // Value if the state_ == InitialWithArgs.
+        const CompileArgs* args_;
+
+        // Value in the other two states.
+        struct {
+            CompileMode    mode_;
+            Tier           tier_;
+            DebugEnabled   debug_;
+            HasGcTypes     gcTypes_;
+        };
+    };
+
+  public:
+    // Retain a reference to the CompileArgs.  A subsequent computeParameters()
+    // will compute all parameters from the CompileArgs and additional values.
+    CompilerEnvironment(const CompileArgs& args);
+
+    // Save the provided values for mode, tier, and debug, and the initial value
+    // for gcTypes.  A subsequent computeParameters() will compute the final
+    // value of gcTypes.
+    CompilerEnvironment(CompileMode mode,
+                        Tier tier,
+                        DebugEnabled debugEnabled,
+                        HasGcTypes gcTypesConfigured);
+
+    // Compute any remaining compilation parameters.
+    void computeParameters(Decoder& d, HasGcTypes gcFeatureOptIn);
+
+    // Compute any remaining compilation parameters.  Only use this method if
+    // the CompilerEnvironment was created with values for mode, tier, and
+    // debug.
+    void computeParameters(HasGcTypes gcFeatureOptIn);
+
+    bool isComputed() const {
+        return state_ == Computed;
+    }
+    CompileMode mode() const {
+        MOZ_ASSERT(isComputed());
+        return mode_;
+    }
+    Tier tier() const {
+        MOZ_ASSERT(isComputed());
+        return tier_;
+    }
+    DebugEnabled debug() const {
+        MOZ_ASSERT(isComputed());
+        return debug_;
+    }
+    HasGcTypes gcTypes() const {
+        MOZ_ASSERT(isComputed());
+        return gcTypes_;
+    }
+};
+
 // ModuleEnvironment contains all the state necessary to validate, process or
 // render functions. It is created by decoding all the sections before the 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
 {
     // Constant parameters for the entire compilation:
-    const DebugEnabled        debug;
-    const ModuleKind          kind;
-    const CompileMode         mode;
-    const Shareable           sharedMemoryEnabled;
+    const ModuleKind           kind;
+    const Shareable            sharedMemoryEnabled;
     // `gcTypesConfigured` reflects the value of the flags --wasm-gc and
     // javascript.options.wasm_gc.  These flags will disappear eventually, thus
     // allowing the removal of this variable and its replacement everywhere by
     // the value HasGcTypes::True.
     //
-    // For now, the value is used (a) in the value of gcTypesEnabled(), which
-    // controls whether ref types and struct types and associated instructions
-    // are accepted during validation, and (b) to control whether we emit code
-    // to suppress GC while wasm activations are on the stack.
-    const HasGcTypes          gcTypesConfigured;
-    const Tier                tier;
+    // For now, the value is used to control whether we emit code to suppress GC
+    // while wasm activations are on the stack.
+    const HasGcTypes           gcTypesConfigured;
+    CompilerEnvironment* const compilerEnv;
 
     // Module fields decoded from the module environment (or initialized while
     // validating an asm.js module) and immutable during compilation:
 #ifdef ENABLE_WASM_GC
     // `gcFeatureOptIn` reflects the presence in a module of a GcFeatureOptIn
     // section.  This variable will be removed eventually, allowing it to be
     // replaced everywhere by the value HasGcTypes::True.
     //
@@ -100,68 +173,69 @@ struct ModuleEnvironment
     MaybeSectionRange         codeSection;
 
     // Fields decoded as part of the wasm module tail:
     DataSegmentVector         dataSegments;
     Maybe<NameInBytecode>     moduleName;
     NameInBytecodeVector      funcNames;
     CustomSectionVector       customSections;
 
-    explicit ModuleEnvironment(CompileMode mode,
-                               Tier tier,
-                               DebugEnabled debug,
-                               HasGcTypes hasGcTypes,
+    explicit ModuleEnvironment(HasGcTypes gcTypesConfigured,
+                               CompilerEnvironment* compilerEnv,
                                Shareable sharedMemoryEnabled,
                                ModuleKind kind = ModuleKind::Wasm)
-      : debug(debug),
-        kind(kind),
-        mode(mode),
+      : kind(kind),
         sharedMemoryEnabled(sharedMemoryEnabled),
-        gcTypesConfigured(hasGcTypes),
-        tier(tier),
+        gcTypesConfigured(gcTypesConfigured),
+        compilerEnv(compilerEnv),
 #ifdef ENABLE_WASM_GC
         gcFeatureOptIn(HasGcTypes::False),
 #endif
         memoryUsage(MemoryUsage::None),
         minMemoryLength(0)
     {}
 
+    Tier tier() const {
+        return compilerEnv->tier();
+    }
+    CompileMode mode() const {
+        return compilerEnv->mode();
+    }
+    DebugEnabled debug() const {
+        return compilerEnv->debug();
+    }
     size_t numTables() const {
         return tables.length();
     }
     size_t numTypes() const {
         return types.length();
     }
     size_t numFuncs() const {
         return funcTypes.length();
     }
     size_t numFuncImports() const {
         return funcImportGlobalDataOffsets.length();
     }
     size_t numFuncDefs() const {
         return funcTypes.length() - funcImportGlobalDataOffsets.length();
     }
     HasGcTypes gcTypesEnabled() const {
-#ifdef ENABLE_WASM_GC
-        if (gcTypesConfigured == HasGcTypes::True)
-            return gcFeatureOptIn;
-#endif
-        return HasGcTypes::False;
+        return compilerEnv->gcTypes();
     }
     bool usesMemory() const {
         return memoryUsage != MemoryUsage::None;
     }
     bool usesSharedMemory() const {
         return memoryUsage == MemoryUsage::Shared;
     }
     bool isAsmJS() const {
         return kind == ModuleKind::AsmJS;
     }
     bool debugEnabled() const {
-        return debug == DebugEnabled::True;
+        return compilerEnv->debug() == DebugEnabled::True;
     }
     bool funcIsImport(uint32_t funcIndex) const {
         return funcIndex < funcImportGlobalDataOffsets.length();
     }
 };
 
 // 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