From d796bdc2f7672980a41dcf0d085e76ca3ae195fa Mon Sep 17 00:00:00 2001 From: Ryan Hunt Date: Wed, 12 Jul 2023 15:32:36 +0000 Subject: [PATCH] Bug 1832378 - wasm: Refactor feature flags and let GC build in beta/release but remain disabled by-default. r=yury This commit cleans up some of our feature logic. - A WasmFeatures.h internal header is added for all this code - The stage of a feature is turned into an enum, not a separate macro. This is generally easier to read. As an improvement the concept of 'force enable flag' extension point was added. This is used to enable the GC proposal even if the function references proposal is not enabled. This commit also lets wasm-gc/fr start building in beta/release. They will remain off by default though (behind a pref). Differential Revision: https://phabricator.services.mozilla.com/D177681 --- js/moz.configure | 24 +- js/public/ContextOptions.h | 12 +- js/public/WasmFeatures.h | 189 ++++++----- js/src/builtin/TestingFunctions.cpp | 5 +- js/src/fuzz-tests/testWasm.cpp | 1 + js/src/jit-test/tests/wasm/gc/directives.txt | 2 +- js/src/jit/Lowering.cpp | 2 +- js/src/jit/MIR.cpp | 1 + js/src/jit/ShuffleAnalysis.cpp | 1 + js/src/jit/arm64/Lowering-arm64.cpp | 2 + js/src/jit/x86-shared/Lowering-x86-shared.cpp | 1 + js/src/jsapi-tests/testAtomicOperations.cpp | 2 +- js/src/shell/js.cpp | 79 ++--- js/src/shell/jsshell.h | 2 +- js/src/vm/GlobalObject.cpp | 1 + js/src/wasm/AsmJS.cpp | 1 + js/src/wasm/WasmCompile.cpp | 3 +- js/src/wasm/WasmCompileArgs.h | 4 +- js/src/wasm/WasmFeatures.cpp | 319 ++++++++++++++++++ js/src/wasm/WasmFeatures.h | 115 +++++++ js/src/wasm/WasmIntrinsic.cpp | 1 + js/src/wasm/WasmJS.cpp | 278 +-------------- js/src/wasm/WasmJS.h | 93 ----- js/src/wasm/WasmValType.cpp | 1 + js/src/wasm/WasmValidate.h | 2 +- js/src/wasm/moz.build | 1 + js/xpconnect/src/XPCJSContext.cpp | 6 +- 27 files changed, 614 insertions(+), 534 deletions(-) create mode 100644 js/src/wasm/WasmFeatures.cpp create mode 100644 js/src/wasm/WasmFeatures.h diff --git a/js/moz.configure b/js/moz.configure index 7636dbc8a51cb..512012a952586 100644 --- a/js/moz.configure +++ b/js/moz.configure @@ -666,20 +666,14 @@ option( # =========================== -@depends(milestone.is_nightly) -def default_wasm_function_references(is_nightly): - if is_nightly: - return True - - option( - "--enable-wasm-function-references", - default=default_wasm_function_references, + "--disable-wasm-function-references", + default=True, help="{Enable|Disable} WebAssembly function-references", ) -@depends("--enable-wasm-function-references", "--wasm-no-experimental") +@depends("--disable-wasm-function-references", "--wasm-no-experimental") def wasm_function_references(value, no_experimental): if no_experimental: return @@ -695,19 +689,19 @@ set_define("ENABLE_WASM_FUNCTION_REFERENCES", wasm_function_references) # =========================== -@depends(milestone.is_nightly, "--enable-wasm-function-references") -def default_wasm_gc(is_nightly, function_references): - if is_nightly and function_references: +@depends("--disable-wasm-function-references") +def default_wasm_gc(function_references): + if function_references: return True option( - "--enable-wasm-gc", default=default_wasm_gc, help="{Enable|Disable} WebAssembly GC" + "--disable-wasm-gc", default=default_wasm_gc, help="{Enable|Disable} WebAssembly GC" ) @depends( - "--enable-wasm-gc", "--enable-wasm-function-references", "--wasm-no-experimental" + "--disable-wasm-gc", "--disable-wasm-function-references", "--wasm-no-experimental" ) def wasm_gc(value, function_references, no_experimental): if no_experimental or not value: @@ -716,7 +710,7 @@ def wasm_gc(value, function_references, no_experimental): if function_references: return True - die("--enable-wasm-gc only possible with --enable-wasm-function-references") + die("--disable-wasm-gc only possible with --disable-wasm-function-references") set_config("ENABLE_WASM_GC", wasm_gc) diff --git a/js/public/ContextOptions.h b/js/public/ContextOptions.h index f45bd2b838728..a62b427f591a9 100644 --- a/js/public/ContextOptions.h +++ b/js/public/ContextOptions.h @@ -27,11 +27,9 @@ class JS_PUBLIC_API ContextOptions { wasmVerbose_(false), wasmBaseline_(true), wasmIon_(true), -#define WASM_DEFAULT_FEATURE(NAME, ...) wasm##NAME##_(true), -#define WASM_EXPERIMENTAL_FEATURE(NAME, ...) wasm##NAME##_(false), - JS_FOR_WASM_FEATURES(WASM_DEFAULT_FEATURE, WASM_DEFAULT_FEATURE, WASM_EXPERIMENTAL_FEATURE) -#undef WASM_DEFAULT_FEATURE -#undef WASM_EXPERIMENTAL_FEATURE +#define WASM_FEATURE(NAME, LOWER_NAME, STAGE, ...) wasm##NAME##_(STAGE == WasmFeatureStage::Default), + JS_FOR_WASM_FEATURES(WASM_FEATURE) +#undef WASM_FEATURE testWasmAwaitTier2_(false), throwOnAsmJSValidationFailure_(false), disableIon_(false), @@ -106,7 +104,7 @@ class JS_PUBLIC_API ContextOptions { wasm##NAME##_ = flag; \ return *this; \ } - JS_FOR_WASM_FEATURES(WASM_FEATURE, WASM_FEATURE, WASM_FEATURE) + JS_FOR_WASM_FEATURES(WASM_FEATURE) #undef WASM_FEATURE bool throwOnAsmJSValidationFailure() const { @@ -214,7 +212,7 @@ class JS_PUBLIC_API ContextOptions { bool wasmBaseline_ : 1; bool wasmIon_ : 1; #define WASM_FEATURE(NAME, ...) bool wasm##NAME##_ : 1; - JS_FOR_WASM_FEATURES(WASM_FEATURE, WASM_FEATURE, WASM_FEATURE) + JS_FOR_WASM_FEATURES(WASM_FEATURE) #undef WASM_FEATURE bool testWasmAwaitTier2_ : 1; bool throwOnAsmJSValidationFailure_ : 1; diff --git a/js/public/WasmFeatures.h b/js/public/WasmFeatures.h index 939388fe39df6..91453be53cb32 100644 --- a/js/public/WasmFeatures.h +++ b/js/public/WasmFeatures.h @@ -55,11 +55,6 @@ // 5. [fuzzing] Add the feature to gluesmith/src/lib.rs, if wasm-smith has // support for it. -#ifdef ENABLE_WASM_SIMD -# define WASM_SIMD_ENABLED 1 -#else -# define WASM_SIMD_ENABLED 0 -#endif #ifdef ENABLE_WASM_RELAXED_SIMD # define WASM_RELAXED_SIMD_ENABLED 1 #else @@ -101,84 +96,114 @@ # define WASM_MULTI_MEMORY_ENABLED 0 #endif +enum class WasmFeatureStage { + Experimental = 0, + Tentative, + Default, +}; + // clang-format off -#define JS_FOR_WASM_FEATURES(DEFAULT, TENTATIVE, EXPERIMENTAL) \ - TENTATIVE(/* capitalized name */ ExtendedConst, \ - /* lower case name */ extendedConst, \ - /* compile predicate */ WASM_EXTENDED_CONST_ENABLED, \ - /* compiler predicate */ true, \ - /* flag predicate */ true, \ - /* shell flag */ "extended-const", \ - /* preference name */ "extended_const") \ - TENTATIVE( \ - /* capitalized name */ Exceptions, \ - /* lower case name */ exceptions, \ - /* compile predicate */ true, \ - /* compiler predicate */ BaselineAvailable(cx) || IonAvailable(cx), \ - /* flag predicate */ true, \ - /* shell flag */ "exceptions", \ - /* preference name */ "exceptions") \ - EXPERIMENTAL(/* capitalized name */ FunctionReferences, \ - /* lower case name */ functionReferences, \ - /* compile predicate */ WASM_FUNCTION_REFERENCES_ENABLED, \ - /* compiler predicate */ BaselineAvailable(cx) || \ - IonAvailable(cx), \ - /* flag predicate */ true, \ - /* shell flag */ "function-references", \ - /* preference name */ "function_references") \ - EXPERIMENTAL(/* capitalized name */ Gc, \ - /* lower case name */ gc, \ - /* compile predicate */ WASM_GC_ENABLED, \ - /* compiler predicate */ AnyCompilerAvailable(cx), \ - /* flag predicate */ WasmFunctionReferencesFlag(cx), \ - /* shell flag */ "gc", \ - /* preference name */ "gc") \ - TENTATIVE(/* capitalized name */ RelaxedSimd, \ - /* lower case name */ v128Relaxed, \ - /* compile predicate */ WASM_RELAXED_SIMD_ENABLED, \ - /* compiler predicate */ AnyCompilerAvailable(cx), \ - /* flag predicate */ js::jit::JitSupportsWasmSimd(), \ - /* shell flag */ "relaxed-simd", \ - /* preference name */ "relaxed_simd") \ - TENTATIVE( \ - /* capitalized name */ Memory64, \ - /* lower case name */ memory64, \ - /* compile predicate */ WASM_MEMORY64_ENABLED, \ - /* compiler predicate */ BaselineAvailable(cx) || IonAvailable(cx), \ - /* flag predicate */ true, \ - /* shell flag */ "memory64", \ - /* preference name */ "memory64") \ - EXPERIMENTAL( \ - /* capitalized name */ MemoryControl, \ - /* lower case name */ memoryControl, \ - /* compile predicate */ WASM_MEMORY_CONTROL_ENABLED, \ - /* compiler predicate */ BaselineAvailable(cx) || IonAvailable(cx), \ - /* flag predicate */ true, \ - /* shell flag */ "memory-control", \ - /* preference name */ "memory_control") \ - EXPERIMENTAL( \ - /* capitalized name */ MultiMemory, \ - /* lower case name */ multiMemory, \ - /* compile predicate */ WASM_MULTI_MEMORY_ENABLED, \ - /* compiler predicate */ BaselineAvailable(cx) || IonAvailable(cx), \ - /* flag predicate */ true, \ - /* shell flag */ "multi-memory", \ - /* preference name */ "multi_memory") \ - EXPERIMENTAL(/* capitalized name */ MozIntGemm, \ - /* lower case name */ mozIntGemm, \ - /* compile predicate */ WASM_MOZ_INTGEMM_ENABLED, \ - /* compiler predicate */ BaselineAvailable(cx) || \ - IonAvailable(cx), \ - /* flag predicate */ IsSimdPrivilegedContext(cx), \ - /* shell flag */ "moz-intgemm", \ - /* preference name */ "moz_intgemm") \ - EXPERIMENTAL(/* capitalized name */ TestSerialization, \ - /* lower case name */ testSerialization, \ - /* compile predicate */ 1, \ - /* compiler predicate */ IonAvailable(cx), \ - /* flag predicate */ true, \ - /* shell flag */ "test-serialization", \ - /* preference name */ "test-serialization") +#define JS_FOR_WASM_FEATURES(FEATURE) \ + FEATURE( \ + /* capitalized name */ ExtendedConst, \ + /* lower case name */ extendedConst, \ + /* stage */ WasmFeatureStage::Tentative, \ + /* compile predicate */ WASM_EXTENDED_CONST_ENABLED, \ + /* compiler predicate */ true, \ + /* flag predicate */ true, \ + /* flag force enable */ false, \ + /* shell flag */ "extended-const", \ + /* preference name */ "extended_const") \ + FEATURE( \ + /* capitalized name */ Exceptions, \ + /* lower case name */ exceptions, \ + /* stage */ WasmFeatureStage::Tentative, \ + /* compile predicate */ true, \ + /* compiler predicate */ AnyCompilerAvailable(cx), \ + /* flag predicate */ true, \ + /* flag force enable */ false, \ + /* shell flag */ "exceptions", \ + /* preference name */ "exceptions") \ + FEATURE( \ + /* capitalized name */ FunctionReferences, \ + /* lower case name */ functionReferences, \ + /* stage */ WasmFeatureStage::Experimental, \ + /* compile predicate */ WASM_FUNCTION_REFERENCES_ENABLED, \ + /* compiler predicate */ AnyCompilerAvailable(cx), \ + /* flag predicate */ true, \ + /* flag force enable */ WasmGcFlag(cx), \ + /* shell flag */ "function-references", \ + /* preference name */ "function_references") \ + FEATURE( \ + /* capitalized name */ Gc, \ + /* lower case name */ gc, \ + /* stage */ WasmFeatureStage::Experimental, \ + /* compile predicate */ WASM_GC_ENABLED, \ + /* compiler predicate */ AnyCompilerAvailable(cx), \ + /* flag predicate */ true, \ + /* flag force enable */ false, \ + /* shell flag */ "gc", \ + /* preference name */ "gc") \ + FEATURE( \ + /* capitalized name */ RelaxedSimd, \ + /* lower case name */ v128Relaxed, \ + /* stage */ WasmFeatureStage::Tentative, \ + /* compile predicate */ WASM_RELAXED_SIMD_ENABLED, \ + /* compiler predicate */ AnyCompilerAvailable(cx), \ + /* flag predicate */ js::jit::JitSupportsWasmSimd(), \ + /* flag force enable */ false, \ + /* shell flag */ "relaxed-simd", \ + /* preference name */ "relaxed_simd") \ + FEATURE( \ + /* capitalized name */ Memory64, \ + /* lower case name */ memory64, \ + /* stage */ WasmFeatureStage::Tentative, \ + /* compile predicate */ WASM_MEMORY64_ENABLED, \ + /* compiler predicate */ AnyCompilerAvailable(cx), \ + /* flag predicate */ true, \ + /* flag force enable */ false, \ + /* shell flag */ "memory64", \ + /* preference name */ "memory64") \ + FEATURE( \ + /* capitalized name */ MemoryControl, \ + /* lower case name */ memoryControl, \ + /* stage */ WasmFeatureStage::Experimental, \ + /* compile predicate */ WASM_MEMORY_CONTROL_ENABLED, \ + /* compiler predicate */ AnyCompilerAvailable(cx), \ + /* flag predicate */ true, \ + /* flag force enable */ false, \ + /* shell flag */ "memory-control", \ + /* preference name */ "memory_control") \ + FEATURE( \ + /* capitalized name */ MultiMemory, \ + /* lower case name */ multiMemory, \ + /* stage */ WasmFeatureStage::Experimental, \ + /* compile predicate */ WASM_MULTI_MEMORY_ENABLED, \ + /* compiler predicate */ AnyCompilerAvailable(cx), \ + /* flag predicate */ true, \ + /* flag force enable */ false, \ + /* shell flag */ "multi-memory", \ + /* preference name */ "multi_memory") \ + FEATURE( \ + /* capitalized name */ MozIntGemm, \ + /* lower case name */ mozIntGemm, \ + /* stage */ WasmFeatureStage::Experimental, \ + /* compile predicate */ WASM_MOZ_INTGEMM_ENABLED, \ + /* compiler predicate */ AnyCompilerAvailable(cx), \ + /* flag predicate */ IsSimdPrivilegedContext(cx), \ + /* flag force enable */ false, \ + /* shell flag */ "moz-intgemm", \ + /* preference name */ "moz_intgemm") \ + FEATURE( \ + /* capitalized name */ TestSerialization, \ + /* lower case name */ testSerialization, \ + /* stage */ WasmFeatureStage::Experimental, \ + /* compile predicate */ 1, \ + /* compiler predicate */ IonAvailable(cx), \ + /* flag predicate */ true, \ + /* flag force enable */ false, \ + /* shell flag */ "test-serialization", \ + /* preference name */ "test-serialization") // clang-format on diff --git a/js/src/builtin/TestingFunctions.cpp b/js/src/builtin/TestingFunctions.cpp index 3394dbff5d78d..db0e16087751d 100644 --- a/js/src/builtin/TestingFunctions.cpp +++ b/js/src/builtin/TestingFunctions.cpp @@ -127,6 +127,7 @@ #include "vm/StringType.h" #include "wasm/AsmJS.h" #include "wasm/WasmBaselineCompile.h" +#include "wasm/WasmFeatures.h" #include "wasm/WasmGcObject.h" #include "wasm/WasmInstance.h" #include "wasm/WasmIntrinsic.h" @@ -910,7 +911,7 @@ static bool WasmThreadsEnabled(JSContext* cx, unsigned argc, Value* vp) { args.rval().setBoolean(wasm::NAME##Available(cx)); \ return true; \ } -JS_FOR_WASM_FEATURES(WASM_FEATURE, WASM_FEATURE, WASM_FEATURE); +JS_FOR_WASM_FEATURES(WASM_FEATURE); #undef WASM_FEATURE static bool WasmSimdEnabled(JSContext* cx, unsigned argc, Value* vp) { @@ -9125,7 +9126,7 @@ gc::ZealModeHelpText), JS_FN_HELP("wasm" #NAME "Enabled", Wasm##NAME##Enabled, 0, 0, \ "wasm" #NAME "Enabled()", \ " Returns a boolean indicating whether the WebAssembly " #NAME " proposal is enabled."), -JS_FOR_WASM_FEATURES(WASM_FEATURE, WASM_FEATURE, WASM_FEATURE) +JS_FOR_WASM_FEATURES(WASM_FEATURE) #undef WASM_FEATURE JS_FN_HELP("wasmThreadsEnabled", WasmThreadsEnabled, 0, 0, diff --git a/js/src/fuzz-tests/testWasm.cpp b/js/src/fuzz-tests/testWasm.cpp index 25c3ea09e3c07..8013196367d00 100644 --- a/js/src/fuzz-tests/testWasm.cpp +++ b/js/src/fuzz-tests/testWasm.cpp @@ -15,6 +15,7 @@ #include "vm/TypedArrayObject.h" #include "wasm/WasmCompile.h" +#include "wasm/WasmFeatures.h" #include "wasm/WasmIonCompile.h" #include "wasm/WasmJS.h" #include "wasm/WasmTable.h" diff --git a/js/src/jit-test/tests/wasm/gc/directives.txt b/js/src/jit-test/tests/wasm/gc/directives.txt index 49594ced70a58..565f2bcb87e06 100644 --- a/js/src/jit-test/tests/wasm/gc/directives.txt +++ b/js/src/jit-test/tests/wasm/gc/directives.txt @@ -1 +1 @@ -|jit-test| test-also=--wasm-compiler=optimizing; test-also=--wasm-function-references --wasm-gc; test-also=--wasm-compiler=baseline --wasm-function-references --wasm-gc; include:wasm.js +|jit-test| test-also=--wasm-compiler=optimizing; --wasm-gc; test-also=--wasm-compiler=baseline --wasm-gc; include:wasm.js diff --git a/js/src/jit/Lowering.cpp b/js/src/jit/Lowering.cpp index 5558ca16fc5b7..77b5186c4824e 100644 --- a/js/src/jit/Lowering.cpp +++ b/js/src/jit/Lowering.cpp @@ -25,8 +25,8 @@ #include "js/experimental/JitInfo.h" // JSJitInfo #include "util/Memory.h" #include "wasm/WasmCodegenTypes.h" +#include "wasm/WasmFeatures.h" // for wasm::ReportSimdAnalysis #include "wasm/WasmInstanceData.h" -#include "wasm/WasmJS.h" // for wasm::ReportSimdAnalysis #include "jit/shared/Lowering-shared-inl.h" #include "vm/BytecodeUtil-inl.h" diff --git a/js/src/jit/MIR.cpp b/js/src/jit/MIR.cpp index 6ccc9efcac32a..028a3f44fb55a 100644 --- a/js/src/jit/MIR.cpp +++ b/js/src/jit/MIR.cpp @@ -37,6 +37,7 @@ #include "vm/PlainObject.h" // js::PlainObject #include "vm/Uint8Clamped.h" #include "wasm/WasmCode.h" +#include "wasm/WasmFeatures.h" // for wasm::ReportSimdAnalysis #include "vm/JSAtom-inl.h" #include "wasm/WasmInstance-inl.h" diff --git a/js/src/jit/ShuffleAnalysis.cpp b/js/src/jit/ShuffleAnalysis.cpp index 20158b18d4aa9..3ffbdc33874e6 100644 --- a/js/src/jit/ShuffleAnalysis.cpp +++ b/js/src/jit/ShuffleAnalysis.cpp @@ -5,6 +5,7 @@ #include "jit/ShuffleAnalysis.h" #include "jit/MIR.h" +#include "wasm/WasmFeatures.h" using namespace js; using namespace jit; diff --git a/js/src/jit/arm64/Lowering-arm64.cpp b/js/src/jit/arm64/Lowering-arm64.cpp index 10ef9cc3909ae..4c52077087979 100644 --- a/js/src/jit/arm64/Lowering-arm64.cpp +++ b/js/src/jit/arm64/Lowering-arm64.cpp @@ -11,6 +11,8 @@ #include "jit/arm64/Assembler-arm64.h" #include "jit/Lowering.h" #include "jit/MIR.h" +#include "wasm/WasmFeatures.h" // for wasm::ReportSimdAnalysis + #include "jit/shared/Lowering-shared-inl.h" using namespace js; diff --git a/js/src/jit/x86-shared/Lowering-x86-shared.cpp b/js/src/jit/x86-shared/Lowering-x86-shared.cpp index 4fe46e2afb809..ac0e189e5eeb0 100644 --- a/js/src/jit/x86-shared/Lowering-x86-shared.cpp +++ b/js/src/jit/x86-shared/Lowering-x86-shared.cpp @@ -10,6 +10,7 @@ #include "jit/Lowering.h" #include "jit/MIR.h" +#include "wasm/WasmFeatures.h" // for wasm::ReportSimdAnalysis #include "jit/shared/Lowering-shared-inl.h" diff --git a/js/src/jsapi-tests/testAtomicOperations.cpp b/js/src/jsapi-tests/testAtomicOperations.cpp index 06000f7676742..1a59fec08f260 100644 --- a/js/src/jsapi-tests/testAtomicOperations.cpp +++ b/js/src/jsapi-tests/testAtomicOperations.cpp @@ -13,7 +13,7 @@ #include "vm/ArrayBufferObject.h" #include "vm/SharedMem.h" #include "vm/Uint8Clamped.h" -#include "wasm/WasmJS.h" +#include "wasm/WasmFeatures.h" using namespace js; diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp index 1944c6f7aef1c..23cadaa2d46c2 100644 --- a/js/src/shell/js.cpp +++ b/js/src/shell/js.cpp @@ -201,6 +201,7 @@ #include "vm/ToSource.h" // js::ValueToSource #include "vm/TypedArrayObject.h" #include "vm/WrapperObject.h" +#include "wasm/WasmFeatures.h" #include "wasm/WasmJS.h" #include "vm/Compartment-inl.h" @@ -605,13 +606,10 @@ bool shell::enableSharedMemory = SHARED_MEMORY_DEFAULT; bool shell::enableWasmBaseline = false; bool shell::enableWasmOptimizing = false; -#define WASM_DEFAULT_FEATURE(NAME, ...) bool shell::enableWasm##NAME = true; -#define WASM_EXPERIMENTAL_FEATURE(NAME, ...) \ - bool shell::enableWasm##NAME = false; -JS_FOR_WASM_FEATURES(WASM_DEFAULT_FEATURE, WASM_DEFAULT_FEATURE, - WASM_EXPERIMENTAL_FEATURE); -#undef WASM_DEFAULT_FEATURE -#undef WASM_EXPERIMENTAL_FEATURE +#define WASM_FEATURE(NAME, _, STAGE, ...) \ + bool shell::enableWasm##NAME = STAGE != WasmFeatureStage::Experimental; +JS_FOR_WASM_FEATURES(WASM_FEATURE); +#undef WASM_FEATURE bool shell::enableWasmVerbose = false; bool shell::enableTestWasmAwaitTier2 = false; @@ -10765,7 +10763,7 @@ static void SetWorkerContextOptions(JSContext* cx) { .setWasmBaseline(enableWasmBaseline) .setWasmIon(enableWasmOptimizing) #define WASM_FEATURE(NAME, ...) .setWasm##NAME(enableWasm##NAME) - JS_FOR_WASM_FEATURES(WASM_FEATURE, WASM_FEATURE, WASM_FEATURE) + JS_FOR_WASM_FEATURES(WASM_FEATURE) #undef WASM_FEATURE .setWasmVerbose(enableWasmVerbose) @@ -11499,24 +11497,18 @@ bool InitOptionParser(OptionParser& op) { !op.addBoolOption('\0', "test-wasm-await-tier2", "Forcibly activate tiering and block " "instantiation on completion of tier2") || -#define WASM_DEFAULT_FEATURE(NAME, LOWER_NAME, COMPILE_PRED, COMPILER_PRED, \ - FLAG_PRED, SHELL, ...) \ - !op.addBoolOption('\0', "no-wasm-" SHELL, "Disable wasm " SHELL "feature.") || -#define WASM_TENTATIVE_FEATURE(NAME, LOWER_NAME, COMPILE_PRED, COMPILER_PRED, \ - FLAG_PRED, SHELL, ...) \ - !op.addBoolOption('\0', "no-wasm-" SHELL, \ - "Disable wasm " SHELL "feature.") || \ - !op.addBoolOption('\0', "wasm-" SHELL, "No-op.") || -#define WASM_EXPERIMENTAL_FEATURE(NAME, LOWER_NAME, COMPILE_PRED, \ - COMPILER_PRED, FLAG_PRED, SHELL, ...) \ - !op.addBoolOption('\0', "wasm-" SHELL, \ - "Enable experimental wasm " SHELL "feature.") || \ - !op.addBoolOption('\0', "no-wasm-" SHELL, "No-op.") || - JS_FOR_WASM_FEATURES(WASM_DEFAULT_FEATURE, WASM_TENTATIVE_FEATURE, - WASM_EXPERIMENTAL_FEATURE) -#undef WASM_DEFAULT_FEATURE -#undef WASM_TENTATIVE_FEATURE -#undef WASM_EXPERIMENTAL_FEATURE +#define WASM_FEATURE(NAME, LOWER_NAME, STAGE, COMPILE_PRED, COMPILER_PRED, \ + FLAG_PRED, FLAG_FORCE_ON, SHELL, ...) \ + !op.addBoolOption('\0', "no-wasm-" SHELL, \ + STAGE == WasmFeatureStage::Experimental \ + ? "No-op." \ + : "Disable wasm " SHELL "feature.") || \ + !op.addBoolOption('\0', "wasm-" SHELL, \ + STAGE == WasmFeatureStage::Experimental \ + ? "Enable wasm " SHELL "feature." \ + : "No-op.") || + JS_FOR_WASM_FEATURES(WASM_FEATURE) +#undef WASM_FEATURE !op.addBoolOption('\0', "no-native-regexp", "Disable native regexp compilation") || !op.addIntOption( @@ -12176,16 +12168,13 @@ bool SetContextWasmOptions(JSContext* cx, const OptionParser& op) { } } -#define WASM_DEFAULT_FEATURE(NAME, LOWER_NAME, COMPILE_PRED, COMPILER_PRED, \ - FLAG_PRED, SHELL, ...) \ - enableWasm##NAME = !op.getBoolOption("no-wasm-" SHELL); -#define WASM_EXPERIMENTAL_FEATURE(NAME, LOWER_NAME, COMPILE_PRED, \ - COMPILER_PRED, FLAG_PRED, SHELL, ...) \ - enableWasm##NAME = op.getBoolOption("wasm-" SHELL); - JS_FOR_WASM_FEATURES(WASM_DEFAULT_FEATURE, WASM_DEFAULT_FEATURE, - WASM_EXPERIMENTAL_FEATURE); -#undef WASM_DEFAULT_FEATURE -#undef WASM_EXPERIMENTAL_FEATURE +#define WASM_FEATURE(NAME, LOWER_NAME, STAGE, COMPILE_PRED, COMPILER_PRED, \ + FLAG_PRED, FLAG_FORCE_ON, SHELL, ...) \ + enableWasm##NAME = op.getBoolOption(stage == WasmFeatureStage::Experimental \ + ? "wasm-" SHELL \ + : "no-wasm-" SHELL); \ + JS_FOR_WASM_FEATURES(WASM_FEATURE); +#undef WASM_FEATURE enableWasmVerbose = op.getBoolOption("wasm-verbose"); enableTestWasmAwaitTier2 = op.getBoolOption("test-wasm-await-tier2"); @@ -12197,7 +12186,7 @@ bool SetContextWasmOptions(JSContext* cx, const OptionParser& op) { .setWasmBaseline(enableWasmBaseline) .setWasmIon(enableWasmOptimizing) #define WASM_FEATURE(NAME, ...) .setWasm##NAME(enableWasm##NAME) - JS_FOR_WASM_FEATURES(WASM_FEATURE, WASM_FEATURE, WASM_FEATURE) + JS_FOR_WASM_FEATURES(WASM_FEATURE) #undef WASM_FEATURE ; @@ -12218,16 +12207,12 @@ bool SetContextWasmOptions(JSContext* cx, const OptionParser& op) { // Also the following are to be propagated. const char* to_propagate[] = { -# define WASM_DEFAULT_FEATURE(NAME, LOWER_NAME, COMPILE_PRED, COMPILER_PRED, \ - FLAG_PRED, SHELL, ...) \ - "--no-wasm-" SHELL, -# define WASM_EXPERIMENTAL_FEATURE(NAME, LOWER_NAME, COMPILE_PRED, \ - COMPILER_PRED, FLAG_PRED, SHELL, ...) \ - "--wasm-" SHELL, - JS_FOR_WASM_FEATURES(WASM_DEFAULT_FEATURE, WASM_DEFAULT_FEATURE, - WASM_EXPERIMENTAL_FEATURE) -# undef WASM_DEFAULT_FEATURE -# undef WASM_EXPERIMENTAL_FEATURE +# define WASM_FEATURE(NAME, LOWER_NAME, STAGE, COMPILE_PRED, COMPILER_PRED, \ + FLAG_PRED, FLAG_FORCE_ON, SHELL, ...) \ + STAGE == WasmFeatureStage::Experimental ? "--wasm-" SHELL \ + : "--no-wasm-" SHELL, + JS_FOR_WASM_FEATURES(WASM_FEATURE) +# undef WASM_FEATURE // Compiler selection options "--test-wasm-await-tier2", NULL}; diff --git a/js/src/shell/jsshell.h b/js/src/shell/jsshell.h index c32a835877939..95d0fc83c1953 100644 --- a/js/src/shell/jsshell.h +++ b/js/src/shell/jsshell.h @@ -114,7 +114,7 @@ extern bool enableWasmBaseline; extern bool enableWasmOptimizing; #define WASM_FEATURE(NAME, ...) extern bool enableWasm##NAME; -JS_FOR_WASM_FEATURES(WASM_FEATURE, WASM_FEATURE, WASM_FEATURE); +JS_FOR_WASM_FEATURES(WASM_FEATURE); #undef WASM_FEATURE extern bool enableWasmVerbose; diff --git a/js/src/vm/GlobalObject.cpp b/js/src/vm/GlobalObject.cpp index 837d144542e97..ed28b26dbc383 100644 --- a/js/src/vm/GlobalObject.cpp +++ b/js/src/vm/GlobalObject.cpp @@ -68,6 +68,7 @@ #include "vm/RegExpStatics.h" #include "vm/SelfHosting.h" #include "vm/StringObject.h" +#include "wasm/WasmFeatures.h" #include "wasm/WasmJS.h" #ifdef ENABLE_RECORD_TUPLE # include "vm/RecordType.h" diff --git a/js/src/wasm/AsmJS.cpp b/js/src/wasm/AsmJS.cpp index 64704ae3ce7ee..b3172fbb35060 100644 --- a/js/src/wasm/AsmJS.cpp +++ b/js/src/wasm/AsmJS.cpp @@ -64,6 +64,7 @@ #include "vm/TypedArrayObject.h" #include "vm/Warnings.h" // js::WarnNumberASCII #include "wasm/WasmCompile.h" +#include "wasm/WasmFeatures.h" #include "wasm/WasmGenerator.h" #include "wasm/WasmInstance.h" #include "wasm/WasmIonCompile.h" diff --git a/js/src/wasm/WasmCompile.cpp b/js/src/wasm/WasmCompile.cpp index 3471de1ad28e6..2bd382dc7b7dd 100644 --- a/js/src/wasm/WasmCompile.cpp +++ b/js/src/wasm/WasmCompile.cpp @@ -31,6 +31,7 @@ #include "vm/HelperThreads.h" #include "vm/Realm.h" #include "wasm/WasmBaselineCompile.h" +#include "wasm/WasmFeatures.h" #include "wasm/WasmGenerator.h" #include "wasm/WasmIonCompile.h" #include "wasm/WasmOpIter.h" @@ -90,7 +91,7 @@ FeatureArgs FeatureArgs::build(JSContext* cx, const FeatureOptions& options) { #define WASM_FEATURE(NAME, LOWER_NAME, ...) \ features.LOWER_NAME = wasm::NAME##Available(cx); - JS_FOR_WASM_FEATURES(WASM_FEATURE, WASM_FEATURE, WASM_FEATURE); + JS_FOR_WASM_FEATURES(WASM_FEATURE); #undef WASM_FEATURE features.sharedMemory = diff --git a/js/src/wasm/WasmCompileArgs.h b/js/src/wasm/WasmCompileArgs.h index 1cf45cb020ac0..8d738ccd80a3c 100644 --- a/js/src/wasm/WasmCompileArgs.h +++ b/js/src/wasm/WasmCompileArgs.h @@ -85,7 +85,7 @@ struct FeatureArgs { FeatureArgs() : #define WASM_FEATURE(NAME, LOWER_NAME, ...) LOWER_NAME(false), - JS_FOR_WASM_FEATURES(WASM_FEATURE, WASM_FEATURE, WASM_FEATURE) + JS_FOR_WASM_FEATURES(WASM_FEATURE) #undef WASM_FEATURE sharedMemory(Shareable::False), simd(false), @@ -98,7 +98,7 @@ struct FeatureArgs { static FeatureArgs build(JSContext* cx, const FeatureOptions& options); #define WASM_FEATURE(NAME, LOWER_NAME, ...) bool LOWER_NAME; - JS_FOR_WASM_FEATURES(WASM_FEATURE, WASM_FEATURE, WASM_FEATURE) + JS_FOR_WASM_FEATURES(WASM_FEATURE) #undef WASM_FEATURE Shareable sharedMemory; diff --git a/js/src/wasm/WasmFeatures.cpp b/js/src/wasm/WasmFeatures.cpp new file mode 100644 index 0000000000000..70a6f8e40c184 --- /dev/null +++ b/js/src/wasm/WasmFeatures.cpp @@ -0,0 +1,319 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: set ts=8 sts=2 et sw=2 tw=80: + * + * Copyright 2016 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "wasm/WasmFeatures.h" + +#include "jit/AtomicOperations.h" +#include "jit/JitContext.h" +#include "jit/JitOptions.h" +#include "util/StringBuffer.h" +#include "vm/JSContext.h" +#include "vm/Realm.h" +#include "vm/StringType.h" +#include "wasm/WasmBaselineCompile.h" +#include "wasm/WasmIonCompile.h" +#include "wasm/WasmSignalHandlers.h" + +using namespace js; +using namespace js::wasm; +using namespace js::jit; + +// About the fuzzer intercession points: If fuzzing has been selected and only a +// single compiler has been selected then we will disable features that are not +// supported by that single compiler. This is strictly a concession to the +// fuzzer infrastructure. + +static inline bool IsFuzzingIon(JSContext* cx) { + return IsFuzzing() && !cx->options().wasmBaseline() && + cx->options().wasmIon(); +} + +// These functions read flags and apply fuzzing intercession policies. Never go +// directly to the flags in code below, always go via these accessors. + +static inline bool WasmThreadsFlag(JSContext* cx) { + return cx->realm() && + cx->realm()->creationOptions().getSharedMemoryAndAtomicsEnabled(); +} + +#define WASM_FEATURE(NAME, ...) \ + static inline bool Wasm##NAME##Flag(JSContext* cx); +JS_FOR_WASM_FEATURES(WASM_FEATURE); +#undef WASM_FEATURE + +#define WASM_FEATURE(NAME, LOWER_NAME, STAGE, COMPILE_PRED, COMPILER_PRED, \ + FLAG_PRED, FLAG_FORCE_ON, ...) \ + static inline bool Wasm##NAME##Flag(JSContext* cx) { \ + if (!(COMPILE_PRED)) { \ + return false; \ + } \ + return ((FLAG_PRED) && cx->options().wasm##NAME()) || (FLAG_FORCE_ON); \ + } +JS_FOR_WASM_FEATURES(WASM_FEATURE); +#undef WASM_FEATURE + +static inline bool WasmDebuggerActive(JSContext* cx) { + if (IsFuzzingIon(cx)) { + return false; + } + return cx->realm() && cx->realm()->debuggerObservesWasm(); +} + +/* + * [SMDOC] Compiler and feature selection; compiler and feature availability. + * + * In order to make the computation of whether a wasm feature or wasm compiler + * is available predictable, we have established some rules, and implemented + * those rules. + * + * Code elsewhere should use the predicates below to test for features and + * compilers, it should never try to compute feature and compiler availability + * in other ways. + * + * At the outset, there is a set of selected compilers C containing at most one + * baseline compiler [*] and at most one optimizing compiler [**], and a set of + * selected features F. These selections come from defaults and from overrides + * by command line switches in the shell and javascript.option.wasm_X in the + * browser. Defaults for both features and compilers may be platform specific, + * for example, some compilers may not be available on some platforms because + * they do not support the architecture at all or they do not support features + * that must be enabled by default on the platform. + * + * [*] Currently we have only one, "baseline" aka "Rabaldr", but other + * implementations have additional baseline translators, eg from wasm + * bytecode to an internal code processed by an interpreter. + * + * [**] Currently we have only one, "ion" aka "Baldr". + * + * + * Compiler availability: + * + * The set of features F induces a set of available compilers A: these are the + * compilers that all support all the features in F. (Some of these compilers + * may not be in the set C.) + * + * The sets C and A are intersected, yielding a set of enabled compilers E. + * Notably, the set E may be empty, in which case wasm is effectively disabled + * (though the WebAssembly object is still present in the global environment). + * + * An important consequence is that selecting a feature that is not supported by + * a particular compiler disables that compiler completely -- there is no notion + * of a compiler being available but suddenly failing when an unsupported + * feature is used by a program. If a compiler is available, it supports all + * the features that have been selected. + * + * Equally important, a feature cannot be enabled by default on a platform if + * the feature is not supported by all the compilers we wish to have enabled by + * default on the platform. We MUST by-default disable features on a platform + * that are not supported by all the compilers on the platform. + * + * In a shell build, the testing functions wasmCompilersPresent, + * wasmCompileMode, and wasmIonDisabledByFeatures can be used to probe compiler + * availability and the reasons for a compiler being unavailable. + * + * + * Feature availability: + * + * A feature is available if it is selected and there is at least one available + * compiler that implements it. + * + * For example, --wasm-gc selects the GC feature, and if Baseline is available + * then the feature is available. + * + * In a shell build, there are per-feature testing functions (of the form + * wasmFeatureEnabled) to probe whether specific features are available. + */ + +// Compiler availability predicates. These must be kept in sync with the +// feature predicates in the next section below. +// +// These can't call the feature predicates since the feature predicates call +// back to these predicates. So there will be a small amount of duplicated +// logic here, but as compilers reach feature parity that duplication will go +// away. + +bool wasm::BaselineAvailable(JSContext* cx) { + if (!cx->options().wasmBaseline() || !BaselinePlatformSupport()) { + return false; + } + bool isDisabled = false; + MOZ_ALWAYS_TRUE(BaselineDisabledByFeatures(cx, &isDisabled)); + return !isDisabled; +} + +bool wasm::IonAvailable(JSContext* cx) { + if (!cx->options().wasmIon() || !IonPlatformSupport()) { + return false; + } + bool isDisabled = false; + MOZ_ALWAYS_TRUE(IonDisabledByFeatures(cx, &isDisabled)); + return !isDisabled; +} + +bool wasm::WasmCompilerForAsmJSAvailable(JSContext* cx) { + return IonAvailable(cx); +} + +template +static inline bool Append(JSStringBuilder* reason, const char (&s)[ArrayLength], + char* sep) { + if ((*sep && !reason->append(*sep)) || !reason->append(s)) { + return false; + } + *sep = ','; + return true; +} + +bool wasm::BaselineDisabledByFeatures(JSContext* cx, bool* isDisabled, + JSStringBuilder* reason) { + // Baseline cannot be used if we are testing serialization. + bool testSerialization = WasmTestSerializationFlag(cx); + if (reason) { + char sep = 0; + if (testSerialization && !Append(reason, "testSerialization", &sep)) { + return false; + } + } + *isDisabled = testSerialization; + return true; +} + +bool wasm::IonDisabledByFeatures(JSContext* cx, bool* isDisabled, + JSStringBuilder* reason) { + // Ion has no debugging support. + bool debug = WasmDebuggerActive(cx); + if (reason) { + char sep = 0; + if (debug && !Append(reason, "debug", &sep)) { + return false; + } + } + *isDisabled = debug; + return true; +} + +bool wasm::AnyCompilerAvailable(JSContext* cx) { + return wasm::BaselineAvailable(cx) || wasm::IonAvailable(cx); +} + +// Feature predicates. These must be kept in sync with the predicates in the +// section above. +// +// The meaning of these predicates is tricky: A predicate is true for a feature +// if the feature is enabled and/or compiled-in *and* we have *at least one* +// compiler that can support the feature. Subsequent compiler selection must +// ensure that only compilers that actually support the feature are used. + +#define WASM_FEATURE(NAME, LOWER_NAME, STAGE, COMPILE_PRED, COMPILER_PRED, \ + ...) \ + bool wasm::NAME##Available(JSContext* cx) { \ + return Wasm##NAME##Flag(cx) && (COMPILER_PRED); \ + } +JS_FOR_WASM_FEATURES(WASM_FEATURE) +#undef WASM_FEATURE + +bool wasm::IsSimdPrivilegedContext(JSContext* cx) { + // This may be slightly more lenient than we want in an ideal world, but it + // remains safe. + return cx->realm() && cx->realm()->principals() && + cx->realm()->principals()->isSystemOrAddonPrincipal(); +} + +bool wasm::SimdAvailable(JSContext* cx) { + return js::jit::JitSupportsWasmSimd(); +} + +bool wasm::ThreadsAvailable(JSContext* cx) { + return WasmThreadsFlag(cx) && AnyCompilerAvailable(cx); +} + +bool wasm::HasPlatformSupport(JSContext* cx) { +#if !MOZ_LITTLE_ENDIAN() + return false; +#else + + if (!HasJitBackend()) { + return false; + } + + if (gc::SystemPageSize() > wasm::PageSize) { + return false; + } + + if (!JitOptions.supportsUnalignedAccesses) { + return false; + } + +# ifndef __wasi__ + // WASI doesn't support signals so we don't have this function. + if (!wasm::EnsureFullSignalHandlers(cx)) { + return false; + } +# endif + + if (!jit::JitSupportsAtomics()) { + return false; + } + + // Wasm threads require 8-byte lock-free atomics. + if (!jit::AtomicOperations::isLockfree8()) { + return false; + } + + // Test only whether the compilers are supported on the hardware, not whether + // they are enabled. + return BaselinePlatformSupport() || IonPlatformSupport(); +#endif +} + +bool wasm::HasSupport(JSContext* cx) { + // If the general wasm pref is on, it's on for everything. + bool prefEnabled = cx->options().wasm(); + // If the general pref is off, check trusted principals. + if (MOZ_UNLIKELY(!prefEnabled)) { + prefEnabled = cx->options().wasmForTrustedPrinciples() && cx->realm() && + cx->realm()->principals() && + cx->realm()->principals()->isSystemOrAddonPrincipal(); + } + // Do not check for compiler availability, as that may be run-time variant. + // For HasSupport() we want a stable answer depending only on prefs. + return prefEnabled && HasPlatformSupport(cx); +} + +bool wasm::StreamingCompilationAvailable(JSContext* cx) { + // This should match EnsureStreamSupport(). + return HasSupport(cx) && AnyCompilerAvailable(cx) && + cx->runtime()->offThreadPromiseState.ref().initialized() && + CanUseExtraThreads() && cx->runtime()->consumeStreamCallback && + cx->runtime()->reportStreamErrorCallback; +} + +bool wasm::CodeCachingAvailable(JSContext* cx) { + // Fuzzilli breaks the out-of-process compilation mechanism, + // so we disable it permanently in those builds. +#ifdef FUZZING_JS_FUZZILLI + return false; +#else + + // At the moment, we require Ion support for code caching. The main reason + // for this is that wasm::CompileAndSerialize() does not have access to + // information about which optimizing compiler it should use. See comments in + // CompileAndSerialize(), below. + return StreamingCompilationAvailable(cx) && IonAvailable(cx); +#endif +} diff --git a/js/src/wasm/WasmFeatures.h b/js/src/wasm/WasmFeatures.h new file mode 100644 index 0000000000000..696a09a87eb12 --- /dev/null +++ b/js/src/wasm/WasmFeatures.h @@ -0,0 +1,115 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: set ts=8 sts=2 et sw=2 tw=80: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef js_wasm_WasmFeatures_h +#define js_wasm_WasmFeatures_h + +#include "js/WasmFeatures.h" +#include "js/TypeDecls.h" + +namespace js { + +class JSStringBuilder; + +namespace wasm { + +// Return whether WebAssembly can in principle be compiled on this platform (ie +// combination of hardware and OS), assuming at least one of the compilers that +// supports the platform is not disabled by other settings. +// +// This predicate must be checked and must be true to call any of the top-level +// wasm eval/compile methods. + +bool HasPlatformSupport(JSContext* cx); + +// Return whether WebAssembly is supported on this platform. This determines +// whether the WebAssembly object is exposed to JS in this context / realm and +// +// It does *not* guarantee that a compiler is actually available; that has to be +// checked separately, as it is sometimes run-time variant, depending on whether +// a debugger has been created or not. + +bool HasSupport(JSContext* cx); + +// Predicates for compiler availability. +// +// These three predicates together select zero or one baseline compiler and zero +// or one optimizing compiler, based on: what's compiled into the executable, +// what's supported on the current platform, what's selected by options, and the +// current run-time environment. As it is possible for the computed values to +// change (when a value changes in about:config or the debugger pane is shown or +// hidden), it is inadvisable to cache these values in such a way that they +// could become invalid. Generally it is cheap always to recompute them. + +bool BaselineAvailable(JSContext* cx); +bool IonAvailable(JSContext* cx); + +// Test all three. + +bool AnyCompilerAvailable(JSContext* cx); + +// Asm.JS is translated to wasm and then compiled using the wasm optimizing +// compiler; test whether this compiler is available. + +bool WasmCompilerForAsmJSAvailable(JSContext* cx); + +// Predicates for white-box compiler disablement testing. +// +// These predicates determine whether the optimizing compilers were disabled by +// features that are enabled at compile-time or run-time. They do not consider +// the hardware platform on whether other compilers are enabled. +// +// If `reason` is not null then it is populated with a string that describes +// the specific features that disable the compiler. +// +// Returns false on OOM (which happens only when a reason is requested), +// otherwise true, with the result in `*isDisabled` and optionally the reason in +// `*reason`. + +bool BaselineDisabledByFeatures(JSContext* cx, bool* isDisabled, + JSStringBuilder* reason = nullptr); +bool IonDisabledByFeatures(JSContext* cx, bool* isDisabled, + JSStringBuilder* reason = nullptr); + +// Predicates for feature availability. +// +// The following predicates check whether particular wasm features are enabled, +// and for each, whether at least one compiler is (currently) available that +// supports the feature. + +// Streaming compilation. +bool StreamingCompilationAvailable(JSContext* cx); + +// Caching of optimized code. Implies both streaming compilation and an +// optimizing compiler tier. +bool CodeCachingAvailable(JSContext* cx); + +// Shared memory and atomics. +bool ThreadsAvailable(JSContext* cx); + +#define WASM_FEATURE(NAME, ...) bool NAME##Available(JSContext* cx); +JS_FOR_WASM_FEATURES(WASM_FEATURE) +#undef WASM_FEATURE + +// SIMD operations. +bool SimdAvailable(JSContext* cx); + +// Privileged content that can access experimental intrinsics +bool IsSimdPrivilegedContext(JSContext* cx); + +#if defined(ENABLE_WASM_SIMD) && defined(DEBUG) +// Report the result of a Simd simplification to the testing infrastructure. +void ReportSimdAnalysis(const char* data); +#endif + +// Returns true if WebAssembly as configured by compile-time flags and run-time +// options can support try/catch, throw, rethrow, and branch_on_exn (evolving). +bool ExceptionsAvailable(JSContext* cx); + +} // namespace wasm +} // namespace js + +#endif // js_wasm_WasmFeatures_h diff --git a/js/src/wasm/WasmIntrinsic.cpp b/js/src/wasm/WasmIntrinsic.cpp index 31a09107b09be..f2de500c72ae7 100644 --- a/js/src/wasm/WasmIntrinsic.cpp +++ b/js/src/wasm/WasmIntrinsic.cpp @@ -21,6 +21,7 @@ #include "util/Text.h" #include "vm/GlobalObject.h" +#include "wasm/WasmFeatures.h" #include "wasm/WasmGenerator.h" #include "wasm/WasmIntrinsicGenerated.h" #include "wasm/WasmJS.h" diff --git a/js/src/wasm/WasmJS.cpp b/js/src/wasm/WasmJS.cpp index c60b413474fec..179664956ccd0 100644 --- a/js/src/wasm/WasmJS.cpp +++ b/js/src/wasm/WasmJS.cpp @@ -62,6 +62,7 @@ #include "wasm/WasmBuiltins.h" #include "wasm/WasmCompile.h" #include "wasm/WasmDebug.h" +#include "wasm/WasmFeatures.h" #include "wasm/WasmInstance.h" #include "wasm/WasmIntrinsic.h" #include "wasm/WasmIonCompile.h" @@ -102,283 +103,6 @@ using mozilla::Nothing; using mozilla::RangedPtr; using mozilla::Span; -// About the fuzzer intercession points: If fuzzing has been selected and only a -// single compiler has been selected then we will disable features that are not -// supported by that single compiler. This is strictly a concession to the -// fuzzer infrastructure. - -static inline bool IsFuzzingIon(JSContext* cx) { - return IsFuzzing() && !cx->options().wasmBaseline() && - cx->options().wasmIon(); -} - -// These functions read flags and apply fuzzing intercession policies. Never go -// directly to the flags in code below, always go via these accessors. - -static inline bool WasmThreadsFlag(JSContext* cx) { - return cx->realm() && - cx->realm()->creationOptions().getSharedMemoryAndAtomicsEnabled(); -} - -#define WASM_FEATURE(NAME, LOWER_NAME, COMPILE_PRED, COMPILER_PRED, FLAG_PRED, \ - ...) \ - static inline bool Wasm##NAME##Flag(JSContext* cx) { \ - return (COMPILE_PRED) && (FLAG_PRED) && cx->options().wasm##NAME(); \ - } -JS_FOR_WASM_FEATURES(WASM_FEATURE, WASM_FEATURE, WASM_FEATURE); -#undef WASM_FEATURE - -static inline bool WasmDebuggerActive(JSContext* cx) { - if (IsFuzzingIon(cx)) { - return false; - } - return cx->realm() && cx->realm()->debuggerObservesWasm(); -} - -/* - * [SMDOC] Compiler and feature selection; compiler and feature availability. - * - * In order to make the computation of whether a wasm feature or wasm compiler - * is available predictable, we have established some rules, and implemented - * those rules. - * - * Code elsewhere should use the predicates below to test for features and - * compilers, it should never try to compute feature and compiler availability - * in other ways. - * - * At the outset, there is a set of selected compilers C containing at most one - * baseline compiler [*] and at most one optimizing compiler [**], and a set of - * selected features F. These selections come from defaults and from overrides - * by command line switches in the shell and javascript.option.wasm_X in the - * browser. Defaults for both features and compilers may be platform specific, - * for example, some compilers may not be available on some platforms because - * they do not support the architecture at all or they do not support features - * that must be enabled by default on the platform. - * - * [*] Currently we have only one, "baseline" aka "Rabaldr", but other - * implementations have additional baseline translators, eg from wasm - * bytecode to an internal code processed by an interpreter. - * - * [**] Currently we have only one, "ion" aka "Baldr". - * - * - * Compiler availability: - * - * The set of features F induces a set of available compilers A: these are the - * compilers that all support all the features in F. (Some of these compilers - * may not be in the set C.) - * - * The sets C and A are intersected, yielding a set of enabled compilers E. - * Notably, the set E may be empty, in which case wasm is effectively disabled - * (though the WebAssembly object is still present in the global environment). - * - * An important consequence is that selecting a feature that is not supported by - * a particular compiler disables that compiler completely -- there is no notion - * of a compiler being available but suddenly failing when an unsupported - * feature is used by a program. If a compiler is available, it supports all - * the features that have been selected. - * - * Equally important, a feature cannot be enabled by default on a platform if - * the feature is not supported by all the compilers we wish to have enabled by - * default on the platform. We MUST by-default disable features on a platform - * that are not supported by all the compilers on the platform. - * - * In a shell build, the testing functions wasmCompilersPresent, - * wasmCompileMode, and wasmIonDisabledByFeatures can be used to probe compiler - * availability and the reasons for a compiler being unavailable. - * - * - * Feature availability: - * - * A feature is available if it is selected and there is at least one available - * compiler that implements it. - * - * For example, --wasm-gc selects the GC feature, and if Baseline is available - * then the feature is available. - * - * In a shell build, there are per-feature testing functions (of the form - * wasmFeatureEnabled) to probe whether specific features are available. - */ - -// Compiler availability predicates. These must be kept in sync with the -// feature predicates in the next section below. -// -// These can't call the feature predicates since the feature predicates call -// back to these predicates. So there will be a small amount of duplicated -// logic here, but as compilers reach feature parity that duplication will go -// away. - -bool wasm::BaselineAvailable(JSContext* cx) { - if (!cx->options().wasmBaseline() || !BaselinePlatformSupport()) { - return false; - } - bool isDisabled = false; - MOZ_ALWAYS_TRUE(BaselineDisabledByFeatures(cx, &isDisabled)); - return !isDisabled; -} - -bool wasm::IonAvailable(JSContext* cx) { - if (!cx->options().wasmIon() || !IonPlatformSupport()) { - return false; - } - bool isDisabled = false; - MOZ_ALWAYS_TRUE(IonDisabledByFeatures(cx, &isDisabled)); - return !isDisabled; -} - -bool wasm::WasmCompilerForAsmJSAvailable(JSContext* cx) { - return IonAvailable(cx); -} - -template -static inline bool Append(JSStringBuilder* reason, const char (&s)[ArrayLength], - char* sep) { - if ((*sep && !reason->append(*sep)) || !reason->append(s)) { - return false; - } - *sep = ','; - return true; -} - -bool wasm::BaselineDisabledByFeatures(JSContext* cx, bool* isDisabled, - JSStringBuilder* reason) { - // Baseline cannot be used if we are testing serialization. - bool testSerialization = WasmTestSerializationFlag(cx); - if (reason) { - char sep = 0; - if (testSerialization && !Append(reason, "testSerialization", &sep)) { - return false; - } - } - *isDisabled = testSerialization; - return true; -} - -bool wasm::IonDisabledByFeatures(JSContext* cx, bool* isDisabled, - JSStringBuilder* reason) { - // Ion has no debugging support. - bool debug = WasmDebuggerActive(cx); - if (reason) { - char sep = 0; - if (debug && !Append(reason, "debug", &sep)) { - return false; - } - } - *isDisabled = debug; - return true; -} - -bool wasm::AnyCompilerAvailable(JSContext* cx) { - return wasm::BaselineAvailable(cx) || wasm::IonAvailable(cx); -} - -// Feature predicates. These must be kept in sync with the predicates in the -// section above. -// -// The meaning of these predicates is tricky: A predicate is true for a feature -// if the feature is enabled and/or compiled-in *and* we have *at least one* -// compiler that can support the feature. Subsequent compiler selection must -// ensure that only compilers that actually support the feature are used. - -#define WASM_FEATURE(NAME, LOWER_NAME, COMPILE_PRED, COMPILER_PRED, FLAG_PRED, \ - ...) \ - bool wasm::NAME##Available(JSContext* cx) { \ - return Wasm##NAME##Flag(cx) && (COMPILER_PRED); \ - } -JS_FOR_WASM_FEATURES(WASM_FEATURE, WASM_FEATURE, WASM_FEATURE) -#undef WASM_FEATURE - -bool wasm::IsSimdPrivilegedContext(JSContext* cx) { - // This may be slightly more lenient than we want in an ideal world, but it - // remains safe. - return cx->realm() && cx->realm()->principals() && - cx->realm()->principals()->isSystemOrAddonPrincipal(); -} - -bool wasm::SimdAvailable(JSContext* cx) { - return js::jit::JitSupportsWasmSimd(); -} - -bool wasm::ThreadsAvailable(JSContext* cx) { - return WasmThreadsFlag(cx) && AnyCompilerAvailable(cx); -} - -bool wasm::HasPlatformSupport(JSContext* cx) { -#if !MOZ_LITTLE_ENDIAN() - return false; -#else - - if (!HasJitBackend()) { - return false; - } - - if (gc::SystemPageSize() > wasm::PageSize) { - return false; - } - - if (!JitOptions.supportsUnalignedAccesses) { - return false; - } - -# ifndef __wasi__ - // WASI doesn't support signals so we don't have this function. - if (!wasm::EnsureFullSignalHandlers(cx)) { - return false; - } -# endif - - if (!jit::JitSupportsAtomics()) { - return false; - } - - // Wasm threads require 8-byte lock-free atomics. - if (!jit::AtomicOperations::isLockfree8()) { - return false; - } - - // Test only whether the compilers are supported on the hardware, not whether - // they are enabled. - return BaselinePlatformSupport() || IonPlatformSupport(); -#endif -} - -bool wasm::HasSupport(JSContext* cx) { - // If the general wasm pref is on, it's on for everything. - bool prefEnabled = cx->options().wasm(); - // If the general pref is off, check trusted principals. - if (MOZ_UNLIKELY(!prefEnabled)) { - prefEnabled = cx->options().wasmForTrustedPrinciples() && cx->realm() && - cx->realm()->principals() && - cx->realm()->principals()->isSystemOrAddonPrincipal(); - } - // Do not check for compiler availability, as that may be run-time variant. - // For HasSupport() we want a stable answer depending only on prefs. - return prefEnabled && HasPlatformSupport(cx); -} - -bool wasm::StreamingCompilationAvailable(JSContext* cx) { - // This should match EnsureStreamSupport(). - return HasSupport(cx) && AnyCompilerAvailable(cx) && - cx->runtime()->offThreadPromiseState.ref().initialized() && - CanUseExtraThreads() && cx->runtime()->consumeStreamCallback && - cx->runtime()->reportStreamErrorCallback; -} - -bool wasm::CodeCachingAvailable(JSContext* cx) { - // Fuzzilli breaks the out-of-process compilation mechanism, - // so we disable it permanently in those builds. -#ifdef FUZZING_JS_FUZZILLI - return false; -#else - - // At the moment, we require Ion support for code caching. The main reason - // for this is that wasm::CompileAndSerialize() does not have access to - // information about which optimizing compiler it should use. See comments in - // CompileAndSerialize(), below. - return StreamingCompilationAvailable(cx) && IonAvailable(cx); -#endif -} - // ============================================================================ // Imports diff --git a/js/src/wasm/WasmJS.h b/js/src/wasm/WasmJS.h index 2f1a21f78acab..d3af41d074515 100644 --- a/js/src/wasm/WasmJS.h +++ b/js/src/wasm/WasmJS.h @@ -71,99 +71,6 @@ namespace wasm { struct ImportValues; -// Return whether WebAssembly can in principle be compiled on this platform (ie -// combination of hardware and OS), assuming at least one of the compilers that -// supports the platform is not disabled by other settings. -// -// This predicate must be checked and must be true to call any of the top-level -// wasm eval/compile methods. - -bool HasPlatformSupport(JSContext* cx); - -// Return whether WebAssembly is supported on this platform. This determines -// whether the WebAssembly object is exposed to JS in this context / realm and -// -// It does *not* guarantee that a compiler is actually available; that has to be -// checked separately, as it is sometimes run-time variant, depending on whether -// a debugger has been created or not. - -bool HasSupport(JSContext* cx); - -// Predicates for compiler availability. -// -// These three predicates together select zero or one baseline compiler and zero -// or one optimizing compiler, based on: what's compiled into the executable, -// what's supported on the current platform, what's selected by options, and the -// current run-time environment. As it is possible for the computed values to -// change (when a value changes in about:config or the debugger pane is shown or -// hidden), it is inadvisable to cache these values in such a way that they -// could become invalid. Generally it is cheap always to recompute them. - -bool BaselineAvailable(JSContext* cx); -bool IonAvailable(JSContext* cx); - -// Test all three. - -bool AnyCompilerAvailable(JSContext* cx); - -// Asm.JS is translated to wasm and then compiled using the wasm optimizing -// compiler; test whether this compiler is available. - -bool WasmCompilerForAsmJSAvailable(JSContext* cx); - -// Predicates for white-box compiler disablement testing. -// -// These predicates determine whether the optimizing compilers were disabled by -// features that are enabled at compile-time or run-time. They do not consider -// the hardware platform on whether other compilers are enabled. -// -// If `reason` is not null then it is populated with a string that describes -// the specific features that disable the compiler. -// -// Returns false on OOM (which happens only when a reason is requested), -// otherwise true, with the result in `*isDisabled` and optionally the reason in -// `*reason`. - -bool BaselineDisabledByFeatures(JSContext* cx, bool* isDisabled, - JSStringBuilder* reason = nullptr); -bool IonDisabledByFeatures(JSContext* cx, bool* isDisabled, - JSStringBuilder* reason = nullptr); - -// Predicates for feature availability. -// -// The following predicates check whether particular wasm features are enabled, -// and for each, whether at least one compiler is (currently) available that -// supports the feature. - -// Streaming compilation. -bool StreamingCompilationAvailable(JSContext* cx); - -// Caching of optimized code. Implies both streaming compilation and an -// optimizing compiler tier. -bool CodeCachingAvailable(JSContext* cx); - -// Shared memory and atomics. -bool ThreadsAvailable(JSContext* cx); - -#define WASM_FEATURE(NAME, ...) bool NAME##Available(JSContext* cx); -JS_FOR_WASM_FEATURES(WASM_FEATURE, WASM_FEATURE, WASM_FEATURE) -#undef WASM_FEATURE - -// SIMD operations. -bool SimdAvailable(JSContext* cx); - -// Privileged content that can access experimental intrinsics -bool IsSimdPrivilegedContext(JSContext* cx); - -#if defined(ENABLE_WASM_SIMD) && defined(DEBUG) -// Report the result of a Simd simplification to the testing infrastructure. -void ReportSimdAnalysis(const char* data); -#endif - -// Returns true if WebAssembly as configured by compile-time flags and run-time -// options can support try/catch, throw, rethrow, and branch_on_exn (evolving). -bool ExceptionsAvailable(JSContext* cx); - // Compiles the given binary wasm module given the ArrayBufferObject // and links the module's imports with the given import object. diff --git a/js/src/wasm/WasmValType.cpp b/js/src/wasm/WasmValType.cpp index 3265dae27f026..d3420f0bb6eb3 100644 --- a/js/src/wasm/WasmValType.cpp +++ b/js/src/wasm/WasmValType.cpp @@ -27,6 +27,7 @@ #include "vm/JSAtom.h" #include "vm/JSObject.h" #include "vm/StringType.h" +#include "wasm/WasmFeatures.h" #include "wasm/WasmJS.h" #include "vm/JSAtom-inl.h" diff --git a/js/src/wasm/WasmValidate.h b/js/src/wasm/WasmValidate.h index 2c5470491be85..5aeea2bcbd687 100644 --- a/js/src/wasm/WasmValidate.h +++ b/js/src/wasm/WasmValidate.h @@ -120,7 +120,7 @@ struct ModuleEnvironment { #define WASM_FEATURE(NAME, SHORT_NAME, ...) \ bool SHORT_NAME##Enabled() const { return features.SHORT_NAME; } - JS_FOR_WASM_FEATURES(WASM_FEATURE, WASM_FEATURE, WASM_FEATURE) + JS_FOR_WASM_FEATURES(WASM_FEATURE) #undef WASM_FEATURE Shareable sharedMemoryEnabled() const { return features.sharedMemory; } bool simdAvailable() const { return features.simd; } diff --git a/js/src/wasm/moz.build b/js/src/wasm/moz.build index 3899fbf41b3b1..2e4a4978b0165 100644 --- a/js/src/wasm/moz.build +++ b/js/src/wasm/moz.build @@ -24,6 +24,7 @@ UNIFIED_SOURCES += [ "WasmCompile.cpp", "WasmDebug.cpp", "WasmDebugFrame.cpp", + "WasmFeatures.cpp", "WasmFrameIter.cpp", "WasmGC.cpp", "WasmGcObject.cpp", diff --git a/js/xpconnect/src/XPCJSContext.cpp b/js/xpconnect/src/XPCJSContext.cpp index a5ca9cbe30852..a067617a96660 100644 --- a/js/xpconnect/src/XPCJSContext.cpp +++ b/js/xpconnect/src/XPCJSContext.cpp @@ -836,10 +836,10 @@ void xpc::SetPrefableContextOptions(JS::ContextOptions& options) { .setWasmIon(Preferences::GetBool(JS_OPTIONS_DOT_STR "wasm_optimizingjit")) .setWasmBaseline( Preferences::GetBool(JS_OPTIONS_DOT_STR "wasm_baselinejit")) -#define WASM_FEATURE(NAME, LOWER_NAME, COMPILE_PRED, COMPILER_PRED, FLAG_PRED, \ - SHELL, PREF) \ +#define WASM_FEATURE(NAME, LOWER_NAME, STAGE, COMPILE_PRED, COMPILER_PRED, \ + FLAG_PRED, FLAG_FORCE_ON, SHELL, PREF) \ .setWasm##NAME(Preferences::GetBool(JS_OPTIONS_DOT_STR "wasm_" PREF)) - JS_FOR_WASM_FEATURES(WASM_FEATURE, WASM_FEATURE, WASM_FEATURE) + JS_FOR_WASM_FEATURES(WASM_FEATURE) #undef WASM_FEATURE .setWasmVerbose(Preferences::GetBool(JS_OPTIONS_DOT_STR "wasm_verbose")) .setThrowOnAsmJSValidationFailure(Preferences::GetBool(