diff options
Diffstat (limited to 'js/src/wasm/WasmFeatures.cpp')
-rw-r--r-- | js/src/wasm/WasmFeatures.cpp | 312 |
1 files changed, 312 insertions, 0 deletions
diff --git a/js/src/wasm/WasmFeatures.cpp b/js/src/wasm/WasmFeatures.cpp new file mode 100644 index 0000000000..05804353ae --- /dev/null +++ b/js/src/wasm/WasmFeatures.cpp @@ -0,0 +1,312 @@ +/* -*- 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 <size_t ArrayLength> +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() { +#if !MOZ_LITTLE_ENDIAN() + return false; +#else + + if (!HasJitBackend()) { + return false; + } + + if (gc::SystemPageSize() > wasm::PageSize) { + return false; + } + + if (!JitOptions.supportsUnalignedAccesses) { + return false; + } + + 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() && EnsureFullSignalHandlers(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 +} |