summaryrefslogtreecommitdiffstats
path: root/js/src/wasm/WasmTypes.h
diff options
context:
space:
mode:
authorMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
committerMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
commit5f8de423f190bbb79a62f804151bc24824fa32d8 (patch)
tree10027f336435511475e392454359edea8e25895d /js/src/wasm/WasmTypes.h
parent49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff)
downloadUXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip
Add m-esr52 at 52.6.0
Diffstat (limited to 'js/src/wasm/WasmTypes.h')
-rw-r--r--js/src/wasm/WasmTypes.h1510
1 files changed, 1510 insertions, 0 deletions
diff --git a/js/src/wasm/WasmTypes.h b/js/src/wasm/WasmTypes.h
new file mode 100644
index 000000000..c79af12e5
--- /dev/null
+++ b/js/src/wasm/WasmTypes.h
@@ -0,0 +1,1510 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ *
+ * Copyright 2015 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.
+ */
+
+#ifndef wasm_types_h
+#define wasm_types_h
+
+#include "mozilla/EnumeratedArray.h"
+#include "mozilla/HashFunctions.h"
+#include "mozilla/Maybe.h"
+#include "mozilla/Move.h"
+#include "mozilla/RefCounted.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/Unused.h"
+
+#include "NamespaceImports.h"
+
+#include "ds/LifoAlloc.h"
+#include "jit/IonTypes.h"
+#include "js/UniquePtr.h"
+#include "js/Utility.h"
+#include "js/Vector.h"
+#include "vm/MallocProvider.h"
+#include "wasm/WasmBinaryConstants.h"
+
+namespace js {
+
+class PropertyName;
+namespace jit { struct BaselineScript; }
+
+// This is a widespread header, so lets keep out the core wasm impl types.
+
+class WasmMemoryObject;
+typedef GCPtr<WasmMemoryObject*> GCPtrWasmMemoryObject;
+typedef Rooted<WasmMemoryObject*> RootedWasmMemoryObject;
+typedef Handle<WasmMemoryObject*> HandleWasmMemoryObject;
+typedef MutableHandle<WasmMemoryObject*> MutableHandleWasmMemoryObject;
+
+class WasmModuleObject;
+typedef Rooted<WasmModuleObject*> RootedWasmModuleObject;
+typedef Handle<WasmModuleObject*> HandleWasmModuleObject;
+typedef MutableHandle<WasmModuleObject*> MutableHandleWasmModuleObject;
+
+class WasmInstanceObject;
+typedef GCVector<WasmInstanceObject*> WasmInstanceObjectVector;
+typedef Rooted<WasmInstanceObject*> RootedWasmInstanceObject;
+typedef Handle<WasmInstanceObject*> HandleWasmInstanceObject;
+typedef MutableHandle<WasmInstanceObject*> MutableHandleWasmInstanceObject;
+
+class WasmTableObject;
+typedef Rooted<WasmTableObject*> RootedWasmTableObject;
+typedef Handle<WasmTableObject*> HandleWasmTableObject;
+typedef MutableHandle<WasmTableObject*> MutableHandleWasmTableObject;
+
+namespace wasm {
+
+using mozilla::DebugOnly;
+using mozilla::EnumeratedArray;
+using mozilla::Maybe;
+using mozilla::Move;
+using mozilla::MallocSizeOf;
+using mozilla::Nothing;
+using mozilla::PodZero;
+using mozilla::PodCopy;
+using mozilla::PodEqual;
+using mozilla::RefCounted;
+using mozilla::Some;
+using mozilla::Unused;
+
+typedef Vector<uint32_t, 0, SystemAllocPolicy> Uint32Vector;
+typedef Vector<uint8_t, 0, SystemAllocPolicy> Bytes;
+
+typedef int8_t I8x16[16];
+typedef int16_t I16x8[8];
+typedef int32_t I32x4[4];
+typedef float F32x4[4];
+
+class Code;
+class CodeRange;
+class Memory;
+class Module;
+class Instance;
+class Table;
+
+// To call Vector::podResizeToFit, a type must specialize mozilla::IsPod
+// which is pretty verbose to do within js::wasm, so factor that process out
+// into a macro.
+
+#define WASM_DECLARE_POD_VECTOR(Type, VectorName) \
+} } namespace mozilla { \
+template <> struct IsPod<js::wasm::Type> : TrueType {}; \
+} namespace js { namespace wasm { \
+typedef Vector<Type, 0, SystemAllocPolicy> VectorName;
+
+// A wasm Module and everything it contains must support serialization and
+// deserialization. Some data can be simply copied as raw bytes and,
+// as a convention, is stored in an inline CacheablePod struct. Everything else
+// should implement the below methods which are called recusively by the
+// containing Module.
+
+#define WASM_DECLARE_SERIALIZABLE(Type) \
+ size_t serializedSize() const; \
+ uint8_t* serialize(uint8_t* cursor) const; \
+ const uint8_t* deserialize(const uint8_t* cursor); \
+ size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
+
+#define WASM_DECLARE_SERIALIZABLE_VIRTUAL(Type) \
+ virtual size_t serializedSize() const; \
+ virtual uint8_t* serialize(uint8_t* cursor) const; \
+ virtual const uint8_t* deserialize(const uint8_t* cursor); \
+ virtual size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
+
+#define WASM_DECLARE_SERIALIZABLE_OVERRIDE(Type) \
+ size_t serializedSize() const override; \
+ uint8_t* serialize(uint8_t* cursor) const override; \
+ const uint8_t* deserialize(const uint8_t* cursor) override; \
+ size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const override;
+
+// This reusable base class factors out the logic for a resource that is shared
+// by multiple instances/modules but should only be counted once when computing
+// about:memory stats.
+
+template <class T>
+struct ShareableBase : RefCounted<T>
+{
+ using SeenSet = HashSet<const T*, DefaultHasher<const T*>, SystemAllocPolicy>;
+
+ size_t sizeOfIncludingThisIfNotSeen(MallocSizeOf mallocSizeOf, SeenSet* seen) const {
+ const T* self = static_cast<const T*>(this);
+ typename SeenSet::AddPtr p = seen->lookupForAdd(self);
+ if (p)
+ return 0;
+ bool ok = seen->add(p, self);
+ (void)ok; // oh well
+ return mallocSizeOf(self) + self->sizeOfExcludingThis(mallocSizeOf);
+ }
+};
+
+// ValType utilities
+
+static inline bool
+IsSimdType(ValType vt)
+{
+ switch (vt) {
+ case ValType::I8x16:
+ case ValType::I16x8:
+ case ValType::I32x4:
+ case ValType::F32x4:
+ case ValType::B8x16:
+ case ValType::B16x8:
+ case ValType::B32x4:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static inline uint32_t
+NumSimdElements(ValType vt)
+{
+ MOZ_ASSERT(IsSimdType(vt));
+ switch (vt) {
+ case ValType::I8x16:
+ case ValType::B8x16:
+ return 16;
+ case ValType::I16x8:
+ case ValType::B16x8:
+ return 8;
+ case ValType::I32x4:
+ case ValType::F32x4:
+ case ValType::B32x4:
+ return 4;
+ default:
+ MOZ_CRASH("Unhandled SIMD type");
+ }
+}
+
+static inline ValType
+SimdElementType(ValType vt)
+{
+ MOZ_ASSERT(IsSimdType(vt));
+ switch (vt) {
+ case ValType::I8x16:
+ case ValType::I16x8:
+ case ValType::I32x4:
+ return ValType::I32;
+ case ValType::F32x4:
+ return ValType::F32;
+ case ValType::B8x16:
+ case ValType::B16x8:
+ case ValType::B32x4:
+ return ValType::I32;
+ default:
+ MOZ_CRASH("Unhandled SIMD type");
+ }
+}
+
+static inline ValType
+SimdBoolType(ValType vt)
+{
+ MOZ_ASSERT(IsSimdType(vt));
+ switch (vt) {
+ case ValType::I8x16:
+ case ValType::B8x16:
+ return ValType::B8x16;
+ case ValType::I16x8:
+ case ValType::B16x8:
+ return ValType::B16x8;
+ case ValType::I32x4:
+ case ValType::F32x4:
+ case ValType::B32x4:
+ return ValType::B32x4;
+ default:
+ MOZ_CRASH("Unhandled SIMD type");
+ }
+}
+
+static inline bool
+IsSimdBoolType(ValType vt)
+{
+ return vt == ValType::B8x16 || vt == ValType::B16x8 || vt == ValType::B32x4;
+}
+
+static inline jit::MIRType
+ToMIRType(ValType vt)
+{
+ switch (vt) {
+ case ValType::I32: return jit::MIRType::Int32;
+ case ValType::I64: return jit::MIRType::Int64;
+ case ValType::F32: return jit::MIRType::Float32;
+ case ValType::F64: return jit::MIRType::Double;
+ case ValType::I8x16: return jit::MIRType::Int8x16;
+ case ValType::I16x8: return jit::MIRType::Int16x8;
+ case ValType::I32x4: return jit::MIRType::Int32x4;
+ case ValType::F32x4: return jit::MIRType::Float32x4;
+ case ValType::B8x16: return jit::MIRType::Bool8x16;
+ case ValType::B16x8: return jit::MIRType::Bool16x8;
+ case ValType::B32x4: return jit::MIRType::Bool32x4;
+ }
+ MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("bad type");
+}
+
+// The ExprType enum represents the type of a WebAssembly expression or return
+// value and may either be a value type or void. Soon, expression types will be
+// generalized to a list of ValType and this enum will go away, replaced,
+// wherever it is used, by a varU32 + list of ValType.
+
+enum class ExprType
+{
+ Void = uint8_t(TypeCode::BlockVoid),
+
+ I32 = uint8_t(TypeCode::I32),
+ I64 = uint8_t(TypeCode::I64),
+ F32 = uint8_t(TypeCode::F32),
+ F64 = uint8_t(TypeCode::F64),
+
+ I8x16 = uint8_t(TypeCode::I8x16),
+ I16x8 = uint8_t(TypeCode::I16x8),
+ I32x4 = uint8_t(TypeCode::I32x4),
+ F32x4 = uint8_t(TypeCode::F32x4),
+ B8x16 = uint8_t(TypeCode::B8x16),
+ B16x8 = uint8_t(TypeCode::B16x8),
+ B32x4 = uint8_t(TypeCode::B32x4),
+
+ Limit = uint8_t(TypeCode::Limit)
+};
+
+static inline bool
+IsVoid(ExprType et)
+{
+ return et == ExprType::Void;
+}
+
+static inline ValType
+NonVoidToValType(ExprType et)
+{
+ MOZ_ASSERT(!IsVoid(et));
+ return ValType(et);
+}
+
+static inline ExprType
+ToExprType(ValType vt)
+{
+ return ExprType(vt);
+}
+
+static inline bool
+IsSimdType(ExprType et)
+{
+ return IsVoid(et) ? false : IsSimdType(ValType(et));
+}
+
+static inline jit::MIRType
+ToMIRType(ExprType et)
+{
+ return IsVoid(et) ? jit::MIRType::None : ToMIRType(ValType(et));
+}
+
+static inline const char*
+ToCString(ExprType type)
+{
+ switch (type) {
+ case ExprType::Void: return "void";
+ case ExprType::I32: return "i32";
+ case ExprType::I64: return "i64";
+ case ExprType::F32: return "f32";
+ case ExprType::F64: return "f64";
+ case ExprType::I8x16: return "i8x16";
+ case ExprType::I16x8: return "i16x8";
+ case ExprType::I32x4: return "i32x4";
+ case ExprType::F32x4: return "f32x4";
+ case ExprType::B8x16: return "b8x16";
+ case ExprType::B16x8: return "b16x8";
+ case ExprType::B32x4: return "b32x4";
+ case ExprType::Limit:;
+ }
+ MOZ_CRASH("bad expression type");
+}
+
+static inline const char*
+ToCString(ValType type)
+{
+ return ToCString(ToExprType(type));
+}
+
+// Because WebAssembly allows one to define the payload of a NaN value,
+// including the signal/quiet bit (highest order bit of payload), another
+// represenation of floating-point values is required: on some platforms (x86
+// without SSE2), passing a floating-point argument to a function call may use
+// the x87 stack, which has the side-effect of clearing the signal/quiet bit.
+// Because the signal/quiet bit must be preserved (by spec), we use the raw
+// punned integer representation of floating points instead, in function calls.
+//
+// When we leave the WebAssembly sandbox back to JS, NaNs are canonicalized, so
+// this isn't observable from JS.
+
+template<class T>
+class Raw
+{
+ typedef typename mozilla::FloatingPoint<T>::Bits Bits;
+ Bits value_;
+
+ public:
+ Raw() : value_(0) {}
+
+ explicit Raw(T value)
+ : value_(mozilla::BitwiseCast<Bits>(value))
+ {}
+
+ template<class U> MOZ_IMPLICIT Raw(U) = delete;
+
+ static Raw fromBits(Bits bits) { Raw r; r.value_ = bits; return r; }
+
+ Bits bits() const { return value_; }
+ T fp() const { return mozilla::BitwiseCast<T>(value_); }
+};
+
+using RawF64 = Raw<double>;
+using RawF32 = Raw<float>;
+
+// The Val class represents a single WebAssembly value of a given value type,
+// mostly for the purpose of numeric literals and initializers. A Val does not
+// directly map to a JS value since there is not (currently) a precise
+// representation of i64 values. A Val may contain non-canonical NaNs since,
+// within WebAssembly, floats are not canonicalized. Canonicalization must
+// happen at the JS boundary.
+
+class Val
+{
+ ValType type_;
+ union U {
+ uint32_t i32_;
+ uint64_t i64_;
+ RawF32 f32_;
+ RawF64 f64_;
+ I8x16 i8x16_;
+ I16x8 i16x8_;
+ I32x4 i32x4_;
+ F32x4 f32x4_;
+ U() {}
+ } u;
+
+ public:
+ Val() = default;
+
+ explicit Val(uint32_t i32) : type_(ValType::I32) { u.i32_ = i32; }
+ explicit Val(uint64_t i64) : type_(ValType::I64) { u.i64_ = i64; }
+
+ explicit Val(RawF32 f32) : type_(ValType::F32) { u.f32_ = f32; }
+ explicit Val(RawF64 f64) : type_(ValType::F64) { u.f64_ = f64; }
+ MOZ_IMPLICIT Val(float) = delete;
+ MOZ_IMPLICIT Val(double) = delete;
+
+ explicit Val(const I8x16& i8x16, ValType type = ValType::I8x16) : type_(type) {
+ MOZ_ASSERT(type_ == ValType::I8x16 || type_ == ValType::B8x16);
+ memcpy(u.i8x16_, i8x16, sizeof(u.i8x16_));
+ }
+ explicit Val(const I16x8& i16x8, ValType type = ValType::I16x8) : type_(type) {
+ MOZ_ASSERT(type_ == ValType::I16x8 || type_ == ValType::B16x8);
+ memcpy(u.i16x8_, i16x8, sizeof(u.i16x8_));
+ }
+ explicit Val(const I32x4& i32x4, ValType type = ValType::I32x4) : type_(type) {
+ MOZ_ASSERT(type_ == ValType::I32x4 || type_ == ValType::B32x4);
+ memcpy(u.i32x4_, i32x4, sizeof(u.i32x4_));
+ }
+ explicit Val(const F32x4& f32x4) : type_(ValType::F32x4) {
+ memcpy(u.f32x4_, f32x4, sizeof(u.f32x4_));
+ }
+
+ ValType type() const { return type_; }
+ bool isSimd() const { return IsSimdType(type()); }
+
+ uint32_t i32() const { MOZ_ASSERT(type_ == ValType::I32); return u.i32_; }
+ uint64_t i64() const { MOZ_ASSERT(type_ == ValType::I64); return u.i64_; }
+ RawF32 f32() const { MOZ_ASSERT(type_ == ValType::F32); return u.f32_; }
+ RawF64 f64() const { MOZ_ASSERT(type_ == ValType::F64); return u.f64_; }
+
+ const I8x16& i8x16() const {
+ MOZ_ASSERT(type_ == ValType::I8x16 || type_ == ValType::B8x16);
+ return u.i8x16_;
+ }
+ const I16x8& i16x8() const {
+ MOZ_ASSERT(type_ == ValType::I16x8 || type_ == ValType::B16x8);
+ return u.i16x8_;
+ }
+ const I32x4& i32x4() const {
+ MOZ_ASSERT(type_ == ValType::I32x4 || type_ == ValType::B32x4);
+ return u.i32x4_;
+ }
+ const F32x4& f32x4() const {
+ MOZ_ASSERT(type_ == ValType::F32x4);
+ return u.f32x4_;
+ }
+
+ void writePayload(uint8_t* dst) const;
+};
+
+typedef Vector<Val, 0, SystemAllocPolicy> ValVector;
+
+// The Sig class represents a WebAssembly function signature which takes a list
+// of value types and returns an expression type. The engine uses two in-memory
+// representations of the argument Vector's memory (when elements do not fit
+// inline): normal malloc allocation (via SystemAllocPolicy) and allocation in
+// a LifoAlloc (via LifoAllocPolicy). The former Sig objects can have any
+// lifetime since they own the memory. The latter Sig objects must not outlive
+// the associated LifoAlloc mark/release interval (which is currently the
+// duration of module validation+compilation). Thus, long-lived objects like
+// WasmModule must use malloced allocation.
+
+class Sig
+{
+ ValTypeVector args_;
+ ExprType ret_;
+
+ public:
+ Sig() : args_(), ret_(ExprType::Void) {}
+ Sig(ValTypeVector&& args, ExprType ret) : args_(Move(args)), ret_(ret) {}
+
+ MOZ_MUST_USE bool clone(const Sig& rhs) {
+ ret_ = rhs.ret_;
+ MOZ_ASSERT(args_.empty());
+ return args_.appendAll(rhs.args_);
+ }
+
+ ValType arg(unsigned i) const { return args_[i]; }
+ const ValTypeVector& args() const { return args_; }
+ const ExprType& ret() const { return ret_; }
+
+ HashNumber hash() const {
+ return AddContainerToHash(args_, HashNumber(ret_));
+ }
+ bool operator==(const Sig& rhs) const {
+ return ret() == rhs.ret() && EqualContainers(args(), rhs.args());
+ }
+ bool operator!=(const Sig& rhs) const {
+ return !(*this == rhs);
+ }
+
+ WASM_DECLARE_SERIALIZABLE(Sig)
+};
+
+struct SigHashPolicy
+{
+ typedef const Sig& Lookup;
+ static HashNumber hash(Lookup sig) { return sig.hash(); }
+ static bool match(const Sig* lhs, Lookup rhs) { return *lhs == rhs; }
+};
+
+// An InitExpr describes a deferred initializer expression, used to initialize
+// a global or a table element offset. Such expressions are created during
+// decoding and actually executed on module instantiation.
+
+class InitExpr
+{
+ public:
+ enum class Kind {
+ Constant,
+ GetGlobal
+ };
+
+ private:
+ Kind kind_;
+ union U {
+ Val val_;
+ struct {
+ uint32_t index_;
+ ValType type_;
+ } global;
+ U() {}
+ } u;
+
+ public:
+ InitExpr() = default;
+
+ explicit InitExpr(Val val) : kind_(Kind::Constant) {
+ u.val_ = val;
+ }
+
+ explicit InitExpr(uint32_t globalIndex, ValType type) : kind_(Kind::GetGlobal) {
+ u.global.index_ = globalIndex;
+ u.global.type_ = type;
+ }
+
+ Kind kind() const { return kind_; }
+
+ bool isVal() const { return kind() == Kind::Constant; }
+ Val val() const { MOZ_ASSERT(isVal()); return u.val_; }
+
+ uint32_t globalIndex() const { MOZ_ASSERT(kind() == Kind::GetGlobal); return u.global.index_; }
+
+ ValType type() const {
+ switch (kind()) {
+ case Kind::Constant: return u.val_.type();
+ case Kind::GetGlobal: return u.global.type_;
+ }
+ MOZ_CRASH("unexpected initExpr type");
+ }
+};
+
+// CacheableChars is used to cacheably store UniqueChars.
+
+struct CacheableChars : UniqueChars
+{
+ CacheableChars() = default;
+ explicit CacheableChars(char* ptr) : UniqueChars(ptr) {}
+ MOZ_IMPLICIT CacheableChars(UniqueChars&& rhs) : UniqueChars(Move(rhs)) {}
+ WASM_DECLARE_SERIALIZABLE(CacheableChars)
+};
+
+typedef Vector<CacheableChars, 0, SystemAllocPolicy> CacheableCharsVector;
+
+// Import describes a single wasm import. An ImportVector describes all
+// of a single module's imports.
+//
+// ImportVector is built incrementally by ModuleGenerator and then stored
+// immutably by Module.
+
+struct Import
+{
+ CacheableChars module;
+ CacheableChars field;
+ DefinitionKind kind;
+
+ Import() = default;
+ Import(UniqueChars&& module, UniqueChars&& field, DefinitionKind kind)
+ : module(Move(module)), field(Move(field)), kind(kind)
+ {}
+
+ WASM_DECLARE_SERIALIZABLE(Import)
+};
+
+typedef Vector<Import, 0, SystemAllocPolicy> ImportVector;
+
+// A GlobalDesc describes a single global variable. Currently, asm.js and wasm
+// exposes mutable and immutable private globals, but can't import nor export
+// mutable globals.
+
+enum class GlobalKind
+{
+ Import,
+ Constant,
+ Variable
+};
+
+class GlobalDesc
+{
+ union V {
+ struct {
+ union U {
+ InitExpr initial_;
+ struct {
+ ValType type_;
+ uint32_t index_;
+ } import;
+ U() {}
+ } val;
+ unsigned offset_;
+ bool isMutable_;
+ } var;
+ Val cst_;
+ V() {}
+ } u;
+ GlobalKind kind_;
+
+ public:
+ GlobalDesc() = default;
+
+ explicit GlobalDesc(InitExpr initial, bool isMutable)
+ : kind_((isMutable || !initial.isVal()) ? GlobalKind::Variable : GlobalKind::Constant)
+ {
+ if (isVariable()) {
+ u.var.val.initial_ = initial;
+ u.var.isMutable_ = isMutable;
+ u.var.offset_ = UINT32_MAX;
+ } else {
+ u.cst_ = initial.val();
+ }
+ }
+
+ explicit GlobalDesc(ValType type, bool isMutable, uint32_t importIndex)
+ : kind_(GlobalKind::Import)
+ {
+ u.var.val.import.type_ = type;
+ u.var.val.import.index_ = importIndex;
+ u.var.isMutable_ = isMutable;
+ u.var.offset_ = UINT32_MAX;
+ }
+
+ void setOffset(unsigned offset) {
+ MOZ_ASSERT(!isConstant());
+ MOZ_ASSERT(u.var.offset_ == UINT32_MAX);
+ u.var.offset_ = offset;
+ }
+ unsigned offset() const {
+ MOZ_ASSERT(!isConstant());
+ MOZ_ASSERT(u.var.offset_ != UINT32_MAX);
+ return u.var.offset_;
+ }
+
+ GlobalKind kind() const { return kind_; }
+ bool isVariable() const { return kind_ == GlobalKind::Variable; }
+ bool isConstant() const { return kind_ == GlobalKind::Constant; }
+ bool isImport() const { return kind_ == GlobalKind::Import; }
+
+ bool isMutable() const { return !isConstant() && u.var.isMutable_; }
+ Val constantValue() const { MOZ_ASSERT(isConstant()); return u.cst_; }
+ const InitExpr& initExpr() const { MOZ_ASSERT(isVariable()); return u.var.val.initial_; }
+ uint32_t importIndex() const { MOZ_ASSERT(isImport()); return u.var.val.import.index_; }
+
+ ValType type() const {
+ switch (kind_) {
+ case GlobalKind::Import: return u.var.val.import.type_;
+ case GlobalKind::Variable: return u.var.val.initial_.type();
+ case GlobalKind::Constant: return u.cst_.type();
+ }
+ MOZ_CRASH("unexpected global kind");
+ }
+};
+
+typedef Vector<GlobalDesc, 0, SystemAllocPolicy> GlobalDescVector;
+
+// DataSegment describes the offset of a data segment in the bytecode that is
+// to be copied at a given offset into linear memory upon instantiation.
+
+struct DataSegment
+{
+ InitExpr offset;
+ uint32_t bytecodeOffset;
+ uint32_t length;
+};
+
+typedef Vector<DataSegment, 0, SystemAllocPolicy> DataSegmentVector;
+
+// SigIdDesc describes a signature id that can be used by call_indirect and
+// table-entry prologues to structurally compare whether the caller and callee's
+// signatures *structurally* match. To handle the general case, a Sig is
+// allocated and stored in a process-wide hash table, so that pointer equality
+// implies structural equality. As an optimization for the 99% case where the
+// Sig has a small number of parameters, the Sig is bit-packed into a uint32
+// immediate value so that integer equality implies structural equality. Both
+// cases can be handled with a single comparison by always setting the LSB for
+// the immediates (the LSB is necessarily 0 for allocated Sig pointers due to
+// alignment).
+
+class SigIdDesc
+{
+ public:
+ enum class Kind { None, Immediate, Global };
+ static const uintptr_t ImmediateBit = 0x1;
+
+ private:
+ Kind kind_;
+ size_t bits_;
+
+ SigIdDesc(Kind kind, size_t bits) : kind_(kind), bits_(bits) {}
+
+ public:
+ Kind kind() const { return kind_; }
+ static bool isGlobal(const Sig& sig);
+
+ SigIdDesc() : kind_(Kind::None), bits_(0) {}
+ static SigIdDesc global(const Sig& sig, uint32_t globalDataOffset);
+ static SigIdDesc immediate(const Sig& sig);
+
+ bool isGlobal() const { return kind_ == Kind::Global; }
+
+ size_t immediate() const { MOZ_ASSERT(kind_ == Kind::Immediate); return bits_; }
+ uint32_t globalDataOffset() const { MOZ_ASSERT(kind_ == Kind::Global); return bits_; }
+};
+
+// SigWithId pairs a Sig with SigIdDesc, describing either how to compile code
+// that compares this signature's id or, at instantiation what signature ids to
+// allocate in the global hash and where to put them.
+
+struct SigWithId : Sig
+{
+ SigIdDesc id;
+
+ SigWithId() = default;
+ explicit SigWithId(Sig&& sig, SigIdDesc id) : Sig(Move(sig)), id(id) {}
+ void operator=(Sig&& rhs) { Sig::operator=(Move(rhs)); }
+
+ WASM_DECLARE_SERIALIZABLE(SigWithId)
+};
+
+typedef Vector<SigWithId, 0, SystemAllocPolicy> SigWithIdVector;
+typedef Vector<const SigWithId*, 0, SystemAllocPolicy> SigWithIdPtrVector;
+
+// The (,Profiling,Func)Offsets classes are used to record the offsets of
+// different key points in a CodeRange during compilation.
+
+struct Offsets
+{
+ explicit Offsets(uint32_t begin = 0, uint32_t end = 0)
+ : begin(begin), end(end)
+ {}
+
+ // These define a [begin, end) contiguous range of instructions compiled
+ // into a CodeRange.
+ uint32_t begin;
+ uint32_t end;
+
+ void offsetBy(uint32_t offset) {
+ begin += offset;
+ end += offset;
+ }
+};
+
+struct ProfilingOffsets : Offsets
+{
+ MOZ_IMPLICIT ProfilingOffsets(uint32_t profilingReturn = 0)
+ : Offsets(), profilingReturn(profilingReturn)
+ {}
+
+ // For CodeRanges with ProfilingOffsets, 'begin' is the offset of the
+ // profiling entry.
+ uint32_t profilingEntry() const { return begin; }
+
+ // The profiling return is the offset of the return instruction, which
+ // precedes the 'end' by a variable number of instructions due to
+ // out-of-line codegen.
+ uint32_t profilingReturn;
+
+ void offsetBy(uint32_t offset) {
+ Offsets::offsetBy(offset);
+ profilingReturn += offset;
+ }
+};
+
+struct FuncOffsets : ProfilingOffsets
+{
+ MOZ_IMPLICIT FuncOffsets()
+ : ProfilingOffsets(),
+ tableEntry(0),
+ tableProfilingJump(0),
+ nonProfilingEntry(0),
+ profilingJump(0),
+ profilingEpilogue(0)
+ {}
+
+ // Function CodeRanges have a table entry which takes an extra signature
+ // argument which is checked against the callee's signature before falling
+ // through to the normal prologue. When profiling is enabled, a nop on the
+ // fallthrough is patched to instead jump to the profiling epilogue.
+ uint32_t tableEntry;
+ uint32_t tableProfilingJump;
+
+ // Function CodeRanges have an additional non-profiling entry that comes
+ // after the profiling entry and a non-profiling epilogue that comes before
+ // the profiling epilogue.
+ uint32_t nonProfilingEntry;
+
+ // When profiling is enabled, the 'nop' at offset 'profilingJump' is
+ // overwritten to be a jump to 'profilingEpilogue'.
+ uint32_t profilingJump;
+ uint32_t profilingEpilogue;
+
+ void offsetBy(uint32_t offset) {
+ ProfilingOffsets::offsetBy(offset);
+ tableEntry += offset;
+ tableProfilingJump += offset;
+ nonProfilingEntry += offset;
+ profilingJump += offset;
+ profilingEpilogue += offset;
+ }
+};
+
+// A wasm::Trap represents a wasm-defined trap that can occur during execution
+// which triggers a WebAssembly.RuntimeError. Generated code may jump to a Trap
+// symbolically, passing the bytecode offset to report as the trap offset. The
+// generated jump will be bound to a tiny stub which fills the offset and
+// then jumps to a per-Trap shared stub at the end of the module.
+
+enum class Trap
+{
+ // The Unreachable opcode has been executed.
+ Unreachable,
+ // An integer arithmetic operation led to an overflow.
+ IntegerOverflow,
+ // Trying to coerce NaN to an integer.
+ InvalidConversionToInteger,
+ // Integer division by zero.
+ IntegerDivideByZero,
+ // Out of bounds on wasm memory accesses and asm.js SIMD/atomic accesses.
+ OutOfBounds,
+ // call_indirect to null.
+ IndirectCallToNull,
+ // call_indirect signature mismatch.
+ IndirectCallBadSig,
+
+ // (asm.js only) SIMD float to int conversion failed because the input
+ // wasn't in bounds.
+ ImpreciseSimdConversion,
+
+ // The internal stack space was exhausted. For compatibility, this throws
+ // the same over-recursed error as JS.
+ StackOverflow,
+
+ Limit
+};
+
+// A wrapper around the bytecode offset of a wasm instruction within a whole
+// module. Trap offsets should refer to the first byte of the instruction that
+// triggered the trap and should ultimately derive from OpIter::trapOffset.
+
+struct TrapOffset
+{
+ uint32_t bytecodeOffset;
+
+ TrapOffset() = default;
+ explicit TrapOffset(uint32_t bytecodeOffset) : bytecodeOffset(bytecodeOffset) {}
+};
+
+// While the frame-pointer chain allows the stack to be unwound without
+// metadata, Error.stack still needs to know the line/column of every call in
+// the chain. A CallSiteDesc describes a single callsite to which CallSite adds
+// the metadata necessary to walk up to the next frame. Lastly CallSiteAndTarget
+// adds the function index of the callee.
+
+class CallSiteDesc
+{
+ uint32_t lineOrBytecode_ : 30;
+ uint32_t kind_ : 2;
+ public:
+ enum Kind {
+ Func, // pc-relative call to a specific function
+ Dynamic, // dynamic callee called via register
+ Symbolic, // call to a single symbolic callee
+ TrapExit // call to a trap exit
+ };
+ CallSiteDesc() {}
+ explicit CallSiteDesc(Kind kind)
+ : lineOrBytecode_(0), kind_(kind)
+ {
+ MOZ_ASSERT(kind == Kind(kind_));
+ }
+ CallSiteDesc(uint32_t lineOrBytecode, Kind kind)
+ : lineOrBytecode_(lineOrBytecode), kind_(kind)
+ {
+ MOZ_ASSERT(kind == Kind(kind_));
+ MOZ_ASSERT(lineOrBytecode == lineOrBytecode_);
+ }
+ uint32_t lineOrBytecode() const { return lineOrBytecode_; }
+ Kind kind() const { return Kind(kind_); }
+};
+
+class CallSite : public CallSiteDesc
+{
+ uint32_t returnAddressOffset_;
+ uint32_t stackDepth_;
+
+ public:
+ CallSite() {}
+
+ CallSite(CallSiteDesc desc, uint32_t returnAddressOffset, uint32_t stackDepth)
+ : CallSiteDesc(desc),
+ returnAddressOffset_(returnAddressOffset),
+ stackDepth_(stackDepth)
+ { }
+
+ void setReturnAddressOffset(uint32_t r) { returnAddressOffset_ = r; }
+ void offsetReturnAddressBy(int32_t o) { returnAddressOffset_ += o; }
+ uint32_t returnAddressOffset() const { return returnAddressOffset_; }
+
+ // The stackDepth measures the amount of stack space pushed since the
+ // function was called. In particular, this includes the pushed return
+ // address on all archs (whether or not the call instruction pushes the
+ // return address (x86/x64) or the prologue does (ARM/MIPS)).
+ uint32_t stackDepth() const { return stackDepth_; }
+};
+
+WASM_DECLARE_POD_VECTOR(CallSite, CallSiteVector)
+
+class CallSiteAndTarget : public CallSite
+{
+ uint32_t index_;
+
+ public:
+ explicit CallSiteAndTarget(CallSite cs)
+ : CallSite(cs)
+ {
+ MOZ_ASSERT(cs.kind() != Func);
+ }
+ CallSiteAndTarget(CallSite cs, uint32_t funcIndex)
+ : CallSite(cs), index_(funcIndex)
+ {
+ MOZ_ASSERT(cs.kind() == Func);
+ }
+ CallSiteAndTarget(CallSite cs, Trap trap)
+ : CallSite(cs),
+ index_(uint32_t(trap))
+ {
+ MOZ_ASSERT(cs.kind() == TrapExit);
+ }
+
+ uint32_t funcIndex() const { MOZ_ASSERT(kind() == Func); return index_; }
+ Trap trap() const { MOZ_ASSERT(kind() == TrapExit); return Trap(index_); }
+};
+
+typedef Vector<CallSiteAndTarget, 0, SystemAllocPolicy> CallSiteAndTargetVector;
+
+// A wasm::SymbolicAddress represents a pointer to a well-known function or
+// object that is embedded in wasm code. Since wasm code is serialized and
+// later deserialized into a different address space, symbolic addresses must be
+// used for *all* pointers into the address space. The MacroAssembler records a
+// list of all SymbolicAddresses and the offsets of their use in the code for
+// later patching during static linking.
+
+enum class SymbolicAddress
+{
+ ToInt32,
+#if defined(JS_CODEGEN_ARM)
+ aeabi_idivmod,
+ aeabi_uidivmod,
+ AtomicCmpXchg,
+ AtomicXchg,
+ AtomicFetchAdd,
+ AtomicFetchSub,
+ AtomicFetchAnd,
+ AtomicFetchOr,
+ AtomicFetchXor,
+#endif
+ ModD,
+ SinD,
+ CosD,
+ TanD,
+ ASinD,
+ ACosD,
+ ATanD,
+ CeilD,
+ CeilF,
+ FloorD,
+ FloorF,
+ TruncD,
+ TruncF,
+ NearbyIntD,
+ NearbyIntF,
+ ExpD,
+ LogD,
+ PowD,
+ ATan2D,
+ Context,
+ InterruptUint32,
+ ReportOverRecursed,
+ HandleExecutionInterrupt,
+ ReportTrap,
+ ReportOutOfBounds,
+ ReportUnalignedAccess,
+ CallImport_Void,
+ CallImport_I32,
+ CallImport_I64,
+ CallImport_F64,
+ CoerceInPlace_ToInt32,
+ CoerceInPlace_ToNumber,
+ DivI64,
+ UDivI64,
+ ModI64,
+ UModI64,
+ TruncateDoubleToInt64,
+ TruncateDoubleToUint64,
+ Uint64ToFloatingPoint,
+ Int64ToFloatingPoint,
+ GrowMemory,
+ CurrentMemory,
+ Limit
+};
+
+void*
+AddressOf(SymbolicAddress imm, ExclusiveContext* cx);
+
+// Assumptions captures ambient state that must be the same when compiling and
+// deserializing a module for the compiled code to be valid. If it's not, then
+// the module must be recompiled from scratch.
+
+struct Assumptions
+{
+ uint32_t cpuId;
+ JS::BuildIdCharVector buildId;
+
+ explicit Assumptions(JS::BuildIdCharVector&& buildId);
+
+ // If Assumptions is constructed without arguments, initBuildIdFromContext()
+ // must be called to complete initialization.
+ Assumptions();
+ bool initBuildIdFromContext(ExclusiveContext* cx);
+
+ bool clone(const Assumptions& other);
+
+ bool operator==(const Assumptions& rhs) const;
+ bool operator!=(const Assumptions& rhs) const { return !(*this == rhs); }
+
+ size_t serializedSize() const;
+ uint8_t* serialize(uint8_t* cursor) const;
+ const uint8_t* deserialize(const uint8_t* cursor, size_t limit);
+ size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
+};
+
+// A Module can either be asm.js or wasm.
+
+enum ModuleKind
+{
+ Wasm,
+ AsmJS
+};
+
+// Represents the resizable limits of memories and tables.
+
+struct Limits
+{
+ uint32_t initial;
+ Maybe<uint32_t> maximum;
+};
+
+// TableDesc describes a table as well as the offset of the table's base pointer
+// in global memory. Currently, wasm only has "any function" and asm.js only
+// "typed function".
+
+enum class TableKind
+{
+ AnyFunction,
+ TypedFunction
+};
+
+struct TableDesc
+{
+ TableKind kind;
+ bool external;
+ uint32_t globalDataOffset;
+ Limits limits;
+
+ TableDesc() = default;
+ TableDesc(TableKind kind, Limits limits)
+ : kind(kind),
+ external(false),
+ globalDataOffset(UINT32_MAX),
+ limits(limits)
+ {}
+};
+
+typedef Vector<TableDesc, 0, SystemAllocPolicy> TableDescVector;
+
+// ExportArg holds the unboxed operands to the wasm entry trampoline which can
+// be called through an ExportFuncPtr.
+
+struct ExportArg
+{
+ uint64_t lo;
+ uint64_t hi;
+};
+
+// TLS data for a single module instance.
+//
+// Every WebAssembly function expects to be passed a hidden TLS pointer argument
+// in WasmTlsReg. The TLS pointer argument points to a TlsData struct.
+// Compiled functions expect that the TLS pointer does not change for the
+// lifetime of the thread.
+//
+// There is a TlsData per module instance per thread, so inter-module calls need
+// to pass the TLS pointer appropriate for the callee module.
+//
+// After the TlsData struct follows the module's declared TLS variables.
+
+struct TlsData
+{
+ // Pointer to the JSContext that contains this TLS data.
+ JSContext* cx;
+
+ // Pointer to the Instance that contains this TLS data.
+ Instance* instance;
+
+ // Pointer to the global data for this Instance.
+ uint8_t* globalData;
+
+ // Pointer to the base of the default memory (or null if there is none).
+ uint8_t* memoryBase;
+
+ // Stack limit for the current thread. This limit is checked against the
+ // stack pointer in the prologue of functions that allocate stack space. See
+ // `CodeGenerator::generateWasm`.
+ void* stackLimit;
+};
+
+typedef int32_t (*ExportFuncPtr)(ExportArg* args, TlsData* tls);
+
+// FuncImportTls describes the region of wasm global memory allocated in the
+// instance's thread-local storage for a function import. This is accessed
+// directly from JIT code and mutated by Instance as exits become optimized and
+// deoptimized.
+
+struct FuncImportTls
+{
+ // The code to call at an import site: a wasm callee, a thunk into C++, or a
+ // thunk into JIT code.
+ void* code;
+
+ // The callee's TlsData pointer, which must be loaded to WasmTlsReg (along
+ // with any pinned registers) before calling 'code'.
+ TlsData* tls;
+
+ // If 'code' points into a JIT code thunk, the BaselineScript of the callee,
+ // for bidirectional registration purposes.
+ jit::BaselineScript* baselineScript;
+
+ // A GC pointer which keeps the callee alive. For imported wasm functions,
+ // this points to the wasm function's WasmInstanceObject. For all other
+ // imported functions, 'obj' points to the JSFunction.
+ GCPtrObject obj;
+ static_assert(sizeof(GCPtrObject) == sizeof(void*), "for JIT access");
+};
+
+// TableTls describes the region of wasm global memory allocated in the
+// instance's thread-local storage which is accessed directly from JIT code
+// to bounds-check and index the table.
+
+struct TableTls
+{
+ // Length of the table in number of elements (not bytes).
+ uint32_t length;
+
+ // Pointer to the array of elements (of type either ExternalTableElem or
+ // void*).
+ void* base;
+};
+
+// When a table can contain functions from other instances (it is "external"),
+// the internal representation is an array of ExternalTableElem instead of just
+// an array of code pointers.
+
+struct ExternalTableElem
+{
+ // The code to call when calling this element. The table ABI is the system
+ // ABI with the additional ABI requirements that:
+ // - WasmTlsReg and any pinned registers have been loaded appropriately
+ // - if this is a heterogeneous table that requires a signature check,
+ // WasmTableCallSigReg holds the signature id.
+ void* code;
+
+ // The pointer to the callee's instance's TlsData. This must be loaded into
+ // WasmTlsReg before calling 'code'.
+ TlsData* tls;
+};
+
+// CalleeDesc describes how to compile one of the variety of asm.js/wasm calls.
+// This is hoisted into WasmTypes.h for sharing between Ion and Baseline.
+
+class CalleeDesc
+{
+ public:
+ enum Which {
+ // Calls a function defined in the same module by its index.
+ Func,
+
+ // Calls the import identified by the offset of its FuncImportTls in
+ // thread-local data.
+ Import,
+
+ // Calls a WebAssembly table (heterogeneous, index must be bounds
+ // checked, callee instance depends on TableDesc).
+ WasmTable,
+
+ // Calls an asm.js table (homogeneous, masked index, same-instance).
+ AsmJSTable,
+
+ // Call a C++ function identified by SymbolicAddress.
+ Builtin,
+
+ // Like Builtin, but automatically passes Instance* as first argument.
+ BuiltinInstanceMethod
+ };
+
+ private:
+ Which which_;
+ union U {
+ U() {}
+ uint32_t funcIndex_;
+ struct {
+ uint32_t globalDataOffset_;
+ } import;
+ struct {
+ uint32_t globalDataOffset_;
+ bool external_;
+ SigIdDesc sigId_;
+ } table;
+ SymbolicAddress builtin_;
+ } u;
+
+ public:
+ CalleeDesc() {}
+ static CalleeDesc function(uint32_t funcIndex) {
+ CalleeDesc c;
+ c.which_ = Func;
+ c.u.funcIndex_ = funcIndex;
+ return c;
+ }
+ static CalleeDesc import(uint32_t globalDataOffset) {
+ CalleeDesc c;
+ c.which_ = Import;
+ c.u.import.globalDataOffset_ = globalDataOffset;
+ return c;
+ }
+ static CalleeDesc wasmTable(const TableDesc& desc, SigIdDesc sigId) {
+ CalleeDesc c;
+ c.which_ = WasmTable;
+ c.u.table.globalDataOffset_ = desc.globalDataOffset;
+ c.u.table.external_ = desc.external;
+ c.u.table.sigId_ = sigId;
+ return c;
+ }
+ static CalleeDesc asmJSTable(const TableDesc& desc) {
+ CalleeDesc c;
+ c.which_ = AsmJSTable;
+ c.u.table.globalDataOffset_ = desc.globalDataOffset;
+ return c;
+ }
+ static CalleeDesc builtin(SymbolicAddress callee) {
+ CalleeDesc c;
+ c.which_ = Builtin;
+ c.u.builtin_ = callee;
+ return c;
+ }
+ static CalleeDesc builtinInstanceMethod(SymbolicAddress callee) {
+ CalleeDesc c;
+ c.which_ = BuiltinInstanceMethod;
+ c.u.builtin_ = callee;
+ return c;
+ }
+ Which which() const {
+ return which_;
+ }
+ uint32_t funcIndex() const {
+ MOZ_ASSERT(which_ == Func);
+ return u.funcIndex_;
+ }
+ uint32_t importGlobalDataOffset() const {
+ MOZ_ASSERT(which_ == Import);
+ return u.import.globalDataOffset_;
+ }
+ bool isTable() const {
+ return which_ == WasmTable || which_ == AsmJSTable;
+ }
+ uint32_t tableLengthGlobalDataOffset() const {
+ MOZ_ASSERT(isTable());
+ return u.table.globalDataOffset_ + offsetof(TableTls, length);
+ }
+ uint32_t tableBaseGlobalDataOffset() const {
+ MOZ_ASSERT(isTable());
+ return u.table.globalDataOffset_ + offsetof(TableTls, base);
+ }
+ bool wasmTableIsExternal() const {
+ MOZ_ASSERT(which_ == WasmTable);
+ return u.table.external_;
+ }
+ SigIdDesc wasmTableSigId() const {
+ MOZ_ASSERT(which_ == WasmTable);
+ return u.table.sigId_;
+ }
+ SymbolicAddress builtin() const {
+ MOZ_ASSERT(which_ == Builtin || which_ == BuiltinInstanceMethod);
+ return u.builtin_;
+ }
+};
+
+// Because ARM has a fixed-width instruction encoding, ARM can only express a
+// limited subset of immediates (in a single instruction).
+
+extern bool
+IsValidARMImmediate(uint32_t i);
+
+extern uint32_t
+RoundUpToNextValidARMImmediate(uint32_t i);
+
+// The WebAssembly spec hard-codes the virtual page size to be 64KiB and
+// requires the size of linear memory to always be a multiple of 64KiB.
+
+static const unsigned PageSize = 64 * 1024;
+
+// Bounds checks always compare the base of the memory access with the bounds
+// check limit. If the memory access is unaligned, this means that, even if the
+// bounds check succeeds, a few bytes of the access can extend past the end of
+// memory. To guard against this, extra space is included in the guard region to
+// catch the overflow. MaxMemoryAccessSize is a conservative approximation of
+// the maximum guard space needed to catch all unaligned overflows.
+
+static const unsigned MaxMemoryAccessSize = sizeof(Val);
+
+#ifdef JS_CODEGEN_X64
+
+// All other code should use WASM_HUGE_MEMORY instead of JS_CODEGEN_X64 so that
+// it is easy to use the huge-mapping optimization for other 64-bit platforms in
+// the future.
+# define WASM_HUGE_MEMORY
+
+// On WASM_HUGE_MEMORY platforms, every asm.js or WebAssembly memory
+// unconditionally allocates a huge region of virtual memory of size
+// wasm::HugeMappedSize. This allows all memory resizing to work without
+// reallocation and provides enough guard space for all offsets to be folded
+// into memory accesses.
+
+static const uint64_t IndexRange = uint64_t(UINT32_MAX) + 1;
+static const uint64_t OffsetGuardLimit = uint64_t(INT32_MAX) + 1;
+static const uint64_t UnalignedGuardPage = PageSize;
+static const uint64_t HugeMappedSize = IndexRange + OffsetGuardLimit + UnalignedGuardPage;
+
+static_assert(MaxMemoryAccessSize <= UnalignedGuardPage, "rounded up to static page size");
+
+#else // !WASM_HUGE_MEMORY
+
+// On !WASM_HUGE_MEMORY platforms:
+// - To avoid OOM in ArrayBuffer::prepareForAsmJS, asm.js continues to use the
+// original ArrayBuffer allocation which has no guard region at all.
+// - For WebAssembly memories, an additional GuardSize is mapped after the
+// accessible region of the memory to catch folded (base+offset) accesses
+// where `offset < OffsetGuardLimit` as well as the overflow from unaligned
+// accesses, as described above for MaxMemoryAccessSize.
+
+static const size_t OffsetGuardLimit = PageSize - MaxMemoryAccessSize;
+static const size_t GuardSize = PageSize;
+
+// Return whether the given immediate satisfies the constraints of the platform
+// (viz. that, on ARM, IsValidARMImmediate).
+
+extern bool
+IsValidBoundsCheckImmediate(uint32_t i);
+
+// For a given WebAssembly/asm.js max size, return the number of bytes to
+// map which will necessarily be a multiple of the system page size and greater
+// than maxSize. For a returned mappedSize:
+// boundsCheckLimit = mappedSize - GuardSize
+// IsValidBoundsCheckImmediate(boundsCheckLimit)
+
+extern size_t
+ComputeMappedSize(uint32_t maxSize);
+
+#endif // WASM_HUGE_MEMORY
+
+// Metadata for bounds check instructions that are patched at runtime with the
+// appropriate bounds check limit. On WASM_HUGE_MEMORY platforms for wasm (and
+// SIMD/Atomic) bounds checks, no BoundsCheck is created: the signal handler
+// catches everything. On !WASM_HUGE_MEMORY, a BoundsCheck is created for each
+// memory access (except when statically eliminated by optimizations) so that
+// the length can be patched in as an immediate. This requires that the bounds
+// check limit IsValidBoundsCheckImmediate.
+
+class BoundsCheck
+{
+ public:
+ BoundsCheck() = default;
+
+ explicit BoundsCheck(uint32_t cmpOffset)
+ : cmpOffset_(cmpOffset)
+ { }
+
+ uint8_t* patchAt(uint8_t* code) const { return code + cmpOffset_; }
+ void offsetBy(uint32_t offset) { cmpOffset_ += offset; }
+
+ private:
+ uint32_t cmpOffset_;
+};
+
+WASM_DECLARE_POD_VECTOR(BoundsCheck, BoundsCheckVector)
+
+// Metadata for memory accesses. On WASM_HUGE_MEMORY platforms, only
+// (non-SIMD/Atomic) asm.js loads and stores create a MemoryAccess so that the
+// signal handler can implement the semantically-correct wraparound logic; the
+// rest simply redirect to the out-of-bounds stub in the signal handler. On x86,
+// the base address of memory is baked into each memory access instruction so
+// the MemoryAccess records the location of each for patching. On all other
+// platforms, no MemoryAccess is created.
+
+class MemoryAccess
+{
+ uint32_t insnOffset_;
+ uint32_t trapOutOfLineOffset_;
+
+ public:
+ MemoryAccess() = default;
+ explicit MemoryAccess(uint32_t insnOffset, uint32_t trapOutOfLineOffset = UINT32_MAX)
+ : insnOffset_(insnOffset),
+ trapOutOfLineOffset_(trapOutOfLineOffset)
+ {}
+
+ uint32_t insnOffset() const {
+ return insnOffset_;
+ }
+ bool hasTrapOutOfLineCode() const {
+ return trapOutOfLineOffset_ != UINT32_MAX;
+ }
+ uint8_t* trapOutOfLineCode(uint8_t* code) const {
+ MOZ_ASSERT(hasTrapOutOfLineCode());
+ return code + trapOutOfLineOffset_;
+ }
+
+ void offsetBy(uint32_t delta) {
+ insnOffset_ += delta;
+ if (hasTrapOutOfLineCode())
+ trapOutOfLineOffset_ += delta;
+ }
+};
+
+WASM_DECLARE_POD_VECTOR(MemoryAccess, MemoryAccessVector)
+
+// Metadata for the offset of an instruction to patch with the base address of
+// memory. In practice, this is only used for x86 where the offset points to the
+// *end* of the instruction (which is a non-fixed offset from the beginning of
+// the instruction). As part of the move away from code patching, this should be
+// removed.
+
+struct MemoryPatch
+{
+ uint32_t offset;
+
+ MemoryPatch() = default;
+ explicit MemoryPatch(uint32_t offset) : offset(offset) {}
+
+ void offsetBy(uint32_t delta) {
+ offset += delta;
+ }
+};
+
+WASM_DECLARE_POD_VECTOR(MemoryPatch, MemoryPatchVector)
+
+// Constants:
+
+static const unsigned NaN64GlobalDataOffset = 0;
+static const unsigned NaN32GlobalDataOffset = NaN64GlobalDataOffset + sizeof(double);
+static const unsigned InitialGlobalDataBytes = NaN32GlobalDataOffset + sizeof(float);
+
+static const unsigned MaxSigs = 4 * 1024;
+static const unsigned MaxFuncs = 512 * 1024;
+static const unsigned MaxGlobals = 4 * 1024;
+static const unsigned MaxLocals = 64 * 1024;
+static const unsigned MaxImports = 64 * 1024;
+static const unsigned MaxExports = 64 * 1024;
+static const unsigned MaxTables = 4 * 1024;
+static const unsigned MaxTableElems = 1024 * 1024;
+static const unsigned MaxDataSegments = 64 * 1024;
+static const unsigned MaxElemSegments = 64 * 1024;
+static const unsigned MaxArgsPerFunc = 4 * 1024;
+static const unsigned MaxBrTableElems = 4 * 1024 * 1024;
+
+// To be able to assign function indices during compilation while the number of
+// imports is still unknown, asm.js sets a maximum number of imports so it can
+// immediately start handing out function indices starting at the maximum + 1.
+// this means that there is a "hole" between the last import and the first
+// definition, but that's fine.
+
+static const unsigned AsmJSMaxImports = 4 * 1024;
+static const unsigned AsmJSFirstDefFuncIndex = AsmJSMaxImports + 1;
+
+static_assert(AsmJSMaxImports <= MaxImports, "conservative");
+static_assert(AsmJSFirstDefFuncIndex < MaxFuncs, "conservative");
+
+} // namespace wasm
+} // namespace js
+
+#endif // wasm_types_h