From 680fd3178ced59d52ad43c24d668a25ef35d769d Mon Sep 17 00:00:00 2001 From: Lars T Hansen Date: Mon, 3 Sep 2018 20:02:38 +0200 Subject: [PATCH] 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. --HG-- extra : rebase_source : d2c2bd01309124caaa66f13564be2bcf24f34946 --- js/src/wasm/AsmJS.cpp | 6 +- js/src/wasm/WasmBaselineCompile.cpp | 4 +- js/src/wasm/WasmCompile.cpp | 102 ++++++++++++++++------- js/src/wasm/WasmGenerator.cpp | 2 +- js/src/wasm/WasmGenerator.h | 4 +- js/src/wasm/WasmIonCompile.cpp | 2 +- js/src/wasm/WasmValidate.cpp | 14 +++- js/src/wasm/WasmValidate.h | 124 ++++++++++++++++++++++------ 8 files changed, 191 insertions(+), 67 deletions(-) diff --git a/js/src/wasm/AsmJS.cpp b/js/src/wasm/AsmJS.cpp index c043402bd1e6..4c4cff6d1b7a 100644 --- a/js/src/wasm/AsmJS.cpp +++ b/js/src/wasm/AsmJS.cpp @@ -1470,6 +1470,7 @@ class MOZ_STACK_CLASS JS_HAZ_ROOTED ModuleValidator ArrayViewVector arrayViews_; // State used to build the AsmJSModule in finish(): + CompilerEnvironment compilerEnv_; ModuleEnvironment env_; MutableAsmJSMetadata asmJSMetadata_; @@ -1530,12 +1531,13 @@ class MOZ_STACK_CLASS JS_HAZ_ROOTED ModuleValidator 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); } diff --git a/js/src/wasm/WasmBaselineCompile.cpp b/js/src/wasm/WasmBaselineCompile.cpp index ac38143eec95..261a07181be8 100644 --- a/js/src/wasm/WasmBaselineCompile.cpp +++ b/js/src/wasm/WasmBaselineCompile.cpp @@ -3382,7 +3382,7 @@ class BaseCompiler final : public BaseCompilerInterface 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 @@ -10352,7 +10352,7 @@ 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. diff --git a/js/src/wasm/WasmCompile.cpp b/js/src/wasm/WasmCompile.cpp index 467981933450..85a38c42b64b 100644 --- a/js/src/wasm/WasmCompile.cpp +++ b/js/src/wasm/WasmCompile.cpp @@ -81,11 +81,11 @@ CompileArgs::CompileArgs(JSContext* cx, ScriptedCaller&& scriptedCaller) 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 @@ -373,10 +373,51 @@ TieringBeneficial(uint32_t codeSize) 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; @@ -384,24 +425,25 @@ InitialCompileFlags(const CompileArgs& args, Decoder& d, CompileMode* mode, Tier 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 @@ -462,12 +504,9 @@ wasm::CompileBuffer(const CompileArgs& args, const ShareableBytes& bytecode, Uni 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; @@ -493,13 +532,17 @@ wasm::CompileTier2(const CompileArgs& args, Module& module, Atomic* cancel 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; @@ -617,12 +660,9 @@ wasm::CompileStreaming(const CompileArgs& args, { 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; diff --git a/js/src/wasm/WasmGenerator.cpp b/js/src/wasm/WasmGenerator.cpp index ba382c129674..cea6d451d9cc 100644 --- a/js/src/wasm/WasmGenerator.cpp +++ b/js/src/wasm/WasmGenerator.cpp @@ -607,7 +607,7 @@ 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; diff --git a/js/src/wasm/WasmGenerator.h b/js/src/wasm/WasmGenerator.h index 594c27724f88..d7b52c0c4544 100644 --- a/js/src/wasm/WasmGenerator.h +++ b/js/src/wasm/WasmGenerator.h @@ -198,8 +198,8 @@ class MOZ_STACK_CLASS ModuleGenerator 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: diff --git a/js/src/wasm/WasmIonCompile.cpp b/js/src/wasm/WasmIonCompile.cpp index 8bee71b6b009..661e425deb01 100644 --- a/js/src/wasm/WasmIonCompile.cpp +++ b/js/src/wasm/WasmIonCompile.cpp @@ -3483,7 +3483,7 @@ 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); diff --git a/js/src/wasm/WasmValidate.cpp b/js/src/wasm/WasmValidate.cpp index 0b38661f496d..efc4876f23b7 100644 --- a/js/src/wasm/WasmValidate.cpp +++ b/js/src/wasm/WasmValidate.cpp @@ -2072,8 +2072,13 @@ wasm::DecodeModuleEnvironment(Decoder& d, ModuleEnvironment* env) #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; @@ -2342,12 +2347,15 @@ 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); diff --git a/js/src/wasm/WasmValidate.h b/js/src/wasm/WasmValidate.h index 678363c4bdd9..ba29c399de35 100644 --- a/js/src/wasm/WasmValidate.h +++ b/js/src/wasm/WasmValidate.h @@ -45,6 +45,83 @@ struct SectionRange typedef Maybe 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 @@ -56,21 +133,17 @@ typedef Maybe MaybeSectionRange; 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: @@ -105,18 +178,14 @@ struct ModuleEnvironment 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 @@ -124,6 +193,15 @@ struct ModuleEnvironment 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(); } @@ -140,11 +218,7 @@ struct ModuleEnvironment 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; @@ -156,7 +230,7 @@ struct ModuleEnvironment 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();