summaryrefslogtreecommitdiff
path: root/js/src/wasm/WasmTypes.cpp
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.cpp
parent49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff)
downloaduxp-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz
Add m-esr52 at 52.6.0
Diffstat (limited to 'js/src/wasm/WasmTypes.cpp')
-rw-r--r--js/src/wasm/WasmTypes.cpp727
1 files changed, 727 insertions, 0 deletions
diff --git a/js/src/wasm/WasmTypes.cpp b/js/src/wasm/WasmTypes.cpp
new file mode 100644
index 0000000000..5add17d06e
--- /dev/null
+++ b/js/src/wasm/WasmTypes.cpp
@@ -0,0 +1,727 @@
+/* -*- 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.
+ */
+
+#include "wasm/WasmTypes.h"
+
+#include "mozilla/MathAlgorithms.h"
+
+#include "fdlibm.h"
+
+#include "jslibmath.h"
+#include "jsmath.h"
+
+#include "jit/MacroAssembler.h"
+#include "js/Conversions.h"
+#include "vm/Interpreter.h"
+#include "wasm/WasmInstance.h"
+#include "wasm/WasmSerialize.h"
+#include "wasm/WasmSignalHandlers.h"
+
+#include "vm/Stack-inl.h"
+
+using namespace js;
+using namespace js::jit;
+using namespace js::wasm;
+
+using mozilla::IsNaN;
+using mozilla::IsPowerOfTwo;
+
+void
+Val::writePayload(uint8_t* dst) const
+{
+ switch (type_) {
+ case ValType::I32:
+ case ValType::F32:
+ memcpy(dst, &u.i32_, sizeof(u.i32_));
+ return;
+ case ValType::I64:
+ case ValType::F64:
+ memcpy(dst, &u.i64_, sizeof(u.i64_));
+ return;
+ case ValType::I8x16:
+ case ValType::I16x8:
+ case ValType::I32x4:
+ case ValType::F32x4:
+ case ValType::B8x16:
+ case ValType::B16x8:
+ case ValType::B32x4:
+ memcpy(dst, &u, jit::Simd128DataSize);
+ return;
+ }
+}
+
+#if defined(JS_CODEGEN_ARM)
+extern "C" {
+
+extern MOZ_EXPORT int64_t
+__aeabi_idivmod(int, int);
+
+extern MOZ_EXPORT int64_t
+__aeabi_uidivmod(int, int);
+
+}
+#endif
+
+static void
+WasmReportOverRecursed()
+{
+ ReportOverRecursed(JSRuntime::innermostWasmActivation()->cx());
+}
+
+static bool
+WasmHandleExecutionInterrupt()
+{
+ WasmActivation* activation = JSRuntime::innermostWasmActivation();
+ bool success = CheckForInterrupt(activation->cx());
+
+ // Preserve the invariant that having a non-null resumePC means that we are
+ // handling an interrupt. Note that resumePC has already been copied onto
+ // the stack by the interrupt stub, so we can clear it before returning
+ // to the stub.
+ activation->setResumePC(nullptr);
+
+ return success;
+}
+
+static void
+WasmReportTrap(int32_t trapIndex)
+{
+ JSContext* cx = JSRuntime::innermostWasmActivation()->cx();
+
+ MOZ_ASSERT(trapIndex < int32_t(Trap::Limit) && trapIndex >= 0);
+ Trap trap = Trap(trapIndex);
+
+ unsigned errorNumber;
+ switch (trap) {
+ case Trap::Unreachable:
+ errorNumber = JSMSG_WASM_UNREACHABLE;
+ break;
+ case Trap::IntegerOverflow:
+ errorNumber = JSMSG_WASM_INTEGER_OVERFLOW;
+ break;
+ case Trap::InvalidConversionToInteger:
+ errorNumber = JSMSG_WASM_INVALID_CONVERSION;
+ break;
+ case Trap::IntegerDivideByZero:
+ errorNumber = JSMSG_WASM_INT_DIVIDE_BY_ZERO;
+ break;
+ case Trap::IndirectCallToNull:
+ errorNumber = JSMSG_WASM_IND_CALL_TO_NULL;
+ break;
+ case Trap::IndirectCallBadSig:
+ errorNumber = JSMSG_WASM_IND_CALL_BAD_SIG;
+ break;
+ case Trap::ImpreciseSimdConversion:
+ errorNumber = JSMSG_SIMD_FAILED_CONVERSION;
+ break;
+ case Trap::OutOfBounds:
+ errorNumber = JSMSG_WASM_OUT_OF_BOUNDS;
+ break;
+ case Trap::StackOverflow:
+ errorNumber = JSMSG_OVER_RECURSED;
+ break;
+ default:
+ MOZ_CRASH("unexpected trap");
+ }
+
+ JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, errorNumber);
+}
+
+static void
+WasmReportOutOfBounds()
+{
+ JSContext* cx = JSRuntime::innermostWasmActivation()->cx();
+ JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_WASM_OUT_OF_BOUNDS);
+}
+
+static void
+WasmReportUnalignedAccess()
+{
+ JSContext* cx = JSRuntime::innermostWasmActivation()->cx();
+ JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_WASM_UNALIGNED_ACCESS);
+}
+
+static int32_t
+CoerceInPlace_ToInt32(MutableHandleValue val)
+{
+ JSContext* cx = JSRuntime::innermostWasmActivation()->cx();
+
+ int32_t i32;
+ if (!ToInt32(cx, val, &i32))
+ return false;
+ val.set(Int32Value(i32));
+
+ return true;
+}
+
+static int32_t
+CoerceInPlace_ToNumber(MutableHandleValue val)
+{
+ JSContext* cx = JSRuntime::innermostWasmActivation()->cx();
+
+ double dbl;
+ if (!ToNumber(cx, val, &dbl))
+ return false;
+ val.set(DoubleValue(dbl));
+
+ return true;
+}
+
+static int64_t
+DivI64(uint32_t x_hi, uint32_t x_lo, uint32_t y_hi, uint32_t y_lo)
+{
+ int64_t x = ((uint64_t)x_hi << 32) + x_lo;
+ int64_t y = ((uint64_t)y_hi << 32) + y_lo;
+ MOZ_ASSERT(x != INT64_MIN || y != -1);
+ MOZ_ASSERT(y != 0);
+ return x / y;
+}
+
+static int64_t
+UDivI64(uint32_t x_hi, uint32_t x_lo, uint32_t y_hi, uint32_t y_lo)
+{
+ uint64_t x = ((uint64_t)x_hi << 32) + x_lo;
+ uint64_t y = ((uint64_t)y_hi << 32) + y_lo;
+ MOZ_ASSERT(y != 0);
+ return x / y;
+}
+
+static int64_t
+ModI64(uint32_t x_hi, uint32_t x_lo, uint32_t y_hi, uint32_t y_lo)
+{
+ int64_t x = ((uint64_t)x_hi << 32) + x_lo;
+ int64_t y = ((uint64_t)y_hi << 32) + y_lo;
+ MOZ_ASSERT(x != INT64_MIN || y != -1);
+ MOZ_ASSERT(y != 0);
+ return x % y;
+}
+
+static int64_t
+UModI64(uint32_t x_hi, uint32_t x_lo, uint32_t y_hi, uint32_t y_lo)
+{
+ uint64_t x = ((uint64_t)x_hi << 32) + x_lo;
+ uint64_t y = ((uint64_t)y_hi << 32) + y_lo;
+ MOZ_ASSERT(y != 0);
+ return x % y;
+}
+
+static int64_t
+TruncateDoubleToInt64(double input)
+{
+ // Note: INT64_MAX is not representable in double. It is actually
+ // INT64_MAX + 1. Therefore also sending the failure value.
+ if (input >= double(INT64_MAX) || input < double(INT64_MIN) || IsNaN(input))
+ return 0x8000000000000000;
+ return int64_t(input);
+}
+
+static uint64_t
+TruncateDoubleToUint64(double input)
+{
+ // Note: UINT64_MAX is not representable in double. It is actually UINT64_MAX + 1.
+ // Therefore also sending the failure value.
+ if (input >= double(UINT64_MAX) || input <= -1.0 || IsNaN(input))
+ return 0x8000000000000000;
+ return uint64_t(input);
+}
+
+static double
+Int64ToFloatingPoint(int32_t x_hi, uint32_t x_lo)
+{
+ int64_t x = int64_t((uint64_t(x_hi) << 32)) + int64_t(x_lo);
+ return double(x);
+}
+
+static double
+Uint64ToFloatingPoint(int32_t x_hi, uint32_t x_lo)
+{
+ uint64_t x = (uint64_t(x_hi) << 32) + uint64_t(x_lo);
+ return double(x);
+}
+
+template <class F>
+static inline void*
+FuncCast(F* pf, ABIFunctionType type)
+{
+ void *pv = JS_FUNC_TO_DATA_PTR(void*, pf);
+#ifdef JS_SIMULATOR
+ pv = Simulator::RedirectNativeFunction(pv, type);
+#endif
+ return pv;
+}
+
+void*
+wasm::AddressOf(SymbolicAddress imm, ExclusiveContext* cx)
+{
+ switch (imm) {
+ case SymbolicAddress::Context:
+ return cx->contextAddressForJit();
+ case SymbolicAddress::InterruptUint32:
+ return cx->runtimeAddressOfInterruptUint32();
+ case SymbolicAddress::ReportOverRecursed:
+ return FuncCast(WasmReportOverRecursed, Args_General0);
+ case SymbolicAddress::HandleExecutionInterrupt:
+ return FuncCast(WasmHandleExecutionInterrupt, Args_General0);
+ case SymbolicAddress::ReportTrap:
+ return FuncCast(WasmReportTrap, Args_General1);
+ case SymbolicAddress::ReportOutOfBounds:
+ return FuncCast(WasmReportOutOfBounds, Args_General0);
+ case SymbolicAddress::ReportUnalignedAccess:
+ return FuncCast(WasmReportUnalignedAccess, Args_General0);
+ case SymbolicAddress::CallImport_Void:
+ return FuncCast(Instance::callImport_void, Args_General4);
+ case SymbolicAddress::CallImport_I32:
+ return FuncCast(Instance::callImport_i32, Args_General4);
+ case SymbolicAddress::CallImport_I64:
+ return FuncCast(Instance::callImport_i64, Args_General4);
+ case SymbolicAddress::CallImport_F64:
+ return FuncCast(Instance::callImport_f64, Args_General4);
+ case SymbolicAddress::CoerceInPlace_ToInt32:
+ return FuncCast(CoerceInPlace_ToInt32, Args_General1);
+ case SymbolicAddress::CoerceInPlace_ToNumber:
+ return FuncCast(CoerceInPlace_ToNumber, Args_General1);
+ case SymbolicAddress::ToInt32:
+ return FuncCast<int32_t (double)>(JS::ToInt32, Args_Int_Double);
+ case SymbolicAddress::DivI64:
+ return FuncCast(DivI64, Args_General4);
+ case SymbolicAddress::UDivI64:
+ return FuncCast(UDivI64, Args_General4);
+ case SymbolicAddress::ModI64:
+ return FuncCast(ModI64, Args_General4);
+ case SymbolicAddress::UModI64:
+ return FuncCast(UModI64, Args_General4);
+ case SymbolicAddress::TruncateDoubleToUint64:
+ return FuncCast(TruncateDoubleToUint64, Args_Int64_Double);
+ case SymbolicAddress::TruncateDoubleToInt64:
+ return FuncCast(TruncateDoubleToInt64, Args_Int64_Double);
+ case SymbolicAddress::Uint64ToFloatingPoint:
+ return FuncCast(Uint64ToFloatingPoint, Args_Double_IntInt);
+ case SymbolicAddress::Int64ToFloatingPoint:
+ return FuncCast(Int64ToFloatingPoint, Args_Double_IntInt);
+#if defined(JS_CODEGEN_ARM)
+ case SymbolicAddress::aeabi_idivmod:
+ return FuncCast(__aeabi_idivmod, Args_General2);
+ case SymbolicAddress::aeabi_uidivmod:
+ return FuncCast(__aeabi_uidivmod, Args_General2);
+ case SymbolicAddress::AtomicCmpXchg:
+ return FuncCast(atomics_cmpxchg_asm_callout, Args_General5);
+ case SymbolicAddress::AtomicXchg:
+ return FuncCast(atomics_xchg_asm_callout, Args_General4);
+ case SymbolicAddress::AtomicFetchAdd:
+ return FuncCast(atomics_add_asm_callout, Args_General4);
+ case SymbolicAddress::AtomicFetchSub:
+ return FuncCast(atomics_sub_asm_callout, Args_General4);
+ case SymbolicAddress::AtomicFetchAnd:
+ return FuncCast(atomics_and_asm_callout, Args_General4);
+ case SymbolicAddress::AtomicFetchOr:
+ return FuncCast(atomics_or_asm_callout, Args_General4);
+ case SymbolicAddress::AtomicFetchXor:
+ return FuncCast(atomics_xor_asm_callout, Args_General4);
+#endif
+ case SymbolicAddress::ModD:
+ return FuncCast(NumberMod, Args_Double_DoubleDouble);
+ case SymbolicAddress::SinD:
+ return FuncCast<double (double)>(sin, Args_Double_Double);
+ case SymbolicAddress::CosD:
+ return FuncCast<double (double)>(cos, Args_Double_Double);
+ case SymbolicAddress::TanD:
+ return FuncCast<double (double)>(tan, Args_Double_Double);
+ case SymbolicAddress::ASinD:
+ return FuncCast<double (double)>(fdlibm::asin, Args_Double_Double);
+ case SymbolicAddress::ACosD:
+ return FuncCast<double (double)>(fdlibm::acos, Args_Double_Double);
+ case SymbolicAddress::ATanD:
+ return FuncCast<double (double)>(fdlibm::atan, Args_Double_Double);
+ case SymbolicAddress::CeilD:
+ return FuncCast<double (double)>(fdlibm::ceil, Args_Double_Double);
+ case SymbolicAddress::CeilF:
+ return FuncCast<float (float)>(fdlibm::ceilf, Args_Float32_Float32);
+ case SymbolicAddress::FloorD:
+ return FuncCast<double (double)>(fdlibm::floor, Args_Double_Double);
+ case SymbolicAddress::FloorF:
+ return FuncCast<float (float)>(fdlibm::floorf, Args_Float32_Float32);
+ case SymbolicAddress::TruncD:
+ return FuncCast<double (double)>(fdlibm::trunc, Args_Double_Double);
+ case SymbolicAddress::TruncF:
+ return FuncCast<float (float)>(fdlibm::truncf, Args_Float32_Float32);
+ case SymbolicAddress::NearbyIntD:
+ return FuncCast<double (double)>(fdlibm::nearbyint, Args_Double_Double);
+ case SymbolicAddress::NearbyIntF:
+ return FuncCast<float (float)>(fdlibm::nearbyintf, Args_Float32_Float32);
+ case SymbolicAddress::ExpD:
+ return FuncCast<double (double)>(fdlibm::exp, Args_Double_Double);
+ case SymbolicAddress::LogD:
+ return FuncCast<double (double)>(fdlibm::log, Args_Double_Double);
+ case SymbolicAddress::PowD:
+ return FuncCast(ecmaPow, Args_Double_DoubleDouble);
+ case SymbolicAddress::ATan2D:
+ return FuncCast(ecmaAtan2, Args_Double_DoubleDouble);
+ case SymbolicAddress::GrowMemory:
+ return FuncCast<uint32_t (Instance*, uint32_t)>(Instance::growMemory_i32, Args_General2);
+ case SymbolicAddress::CurrentMemory:
+ return FuncCast<uint32_t (Instance*)>(Instance::currentMemory_i32, Args_General1);
+ case SymbolicAddress::Limit:
+ break;
+ }
+
+ MOZ_CRASH("Bad SymbolicAddress");
+}
+
+static uint32_t
+GetCPUID()
+{
+ enum Arch {
+ X86 = 0x1,
+ X64 = 0x2,
+ ARM = 0x3,
+ MIPS = 0x4,
+ MIPS64 = 0x5,
+ ARCH_BITS = 3
+ };
+
+#if defined(JS_CODEGEN_X86)
+ MOZ_ASSERT(uint32_t(jit::CPUInfo::GetSSEVersion()) <= (UINT32_MAX >> ARCH_BITS));
+ return X86 | (uint32_t(jit::CPUInfo::GetSSEVersion()) << ARCH_BITS);
+#elif defined(JS_CODEGEN_X64)
+ MOZ_ASSERT(uint32_t(jit::CPUInfo::GetSSEVersion()) <= (UINT32_MAX >> ARCH_BITS));
+ return X64 | (uint32_t(jit::CPUInfo::GetSSEVersion()) << ARCH_BITS);
+#elif defined(JS_CODEGEN_ARM)
+ MOZ_ASSERT(jit::GetARMFlags() <= (UINT32_MAX >> ARCH_BITS));
+ return ARM | (jit::GetARMFlags() << ARCH_BITS);
+#elif defined(JS_CODEGEN_ARM64)
+ MOZ_CRASH("not enabled");
+#elif defined(JS_CODEGEN_MIPS32)
+ MOZ_ASSERT(jit::GetMIPSFlags() <= (UINT32_MAX >> ARCH_BITS));
+ return MIPS | (jit::GetMIPSFlags() << ARCH_BITS);
+#elif defined(JS_CODEGEN_MIPS64)
+ MOZ_ASSERT(jit::GetMIPSFlags() <= (UINT32_MAX >> ARCH_BITS));
+ return MIPS64 | (jit::GetMIPSFlags() << ARCH_BITS);
+#elif defined(JS_CODEGEN_NONE)
+ return 0;
+#else
+# error "unknown architecture"
+#endif
+}
+
+size_t
+Sig::serializedSize() const
+{
+ return sizeof(ret_) +
+ SerializedPodVectorSize(args_);
+}
+
+uint8_t*
+Sig::serialize(uint8_t* cursor) const
+{
+ cursor = WriteScalar<ExprType>(cursor, ret_);
+ cursor = SerializePodVector(cursor, args_);
+ return cursor;
+}
+
+const uint8_t*
+Sig::deserialize(const uint8_t* cursor)
+{
+ (cursor = ReadScalar<ExprType>(cursor, &ret_)) &&
+ (cursor = DeserializePodVector(cursor, &args_));
+ return cursor;
+}
+
+size_t
+Sig::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const
+{
+ return args_.sizeOfExcludingThis(mallocSizeOf);
+}
+
+typedef uint32_t ImmediateType; // for 32/64 consistency
+static const unsigned sTotalBits = sizeof(ImmediateType) * 8;
+static const unsigned sTagBits = 1;
+static const unsigned sReturnBit = 1;
+static const unsigned sLengthBits = 4;
+static const unsigned sTypeBits = 2;
+static const unsigned sMaxTypes = (sTotalBits - sTagBits - sReturnBit - sLengthBits) / sTypeBits;
+
+static bool
+IsImmediateType(ValType vt)
+{
+ switch (vt) {
+ case ValType::I32:
+ case ValType::I64:
+ case ValType::F32:
+ case ValType::F64:
+ return true;
+ case ValType::I8x16:
+ case ValType::I16x8:
+ case ValType::I32x4:
+ case ValType::F32x4:
+ case ValType::B8x16:
+ case ValType::B16x8:
+ case ValType::B32x4:
+ return false;
+ }
+ MOZ_CRASH("bad ValType");
+}
+
+static unsigned
+EncodeImmediateType(ValType vt)
+{
+ static_assert(3 < (1 << sTypeBits), "fits");
+ switch (vt) {
+ case ValType::I32:
+ return 0;
+ case ValType::I64:
+ return 1;
+ case ValType::F32:
+ return 2;
+ case ValType::F64:
+ return 3;
+ case ValType::I8x16:
+ case ValType::I16x8:
+ case ValType::I32x4:
+ case ValType::F32x4:
+ case ValType::B8x16:
+ case ValType::B16x8:
+ case ValType::B32x4:
+ break;
+ }
+ MOZ_CRASH("bad ValType");
+}
+
+/* static */ bool
+SigIdDesc::isGlobal(const Sig& sig)
+{
+ unsigned numTypes = (sig.ret() == ExprType::Void ? 0 : 1) +
+ (sig.args().length());
+ if (numTypes > sMaxTypes)
+ return true;
+
+ if (sig.ret() != ExprType::Void && !IsImmediateType(NonVoidToValType(sig.ret())))
+ return true;
+
+ for (ValType v : sig.args()) {
+ if (!IsImmediateType(v))
+ return true;
+ }
+
+ return false;
+}
+
+/* static */ SigIdDesc
+SigIdDesc::global(const Sig& sig, uint32_t globalDataOffset)
+{
+ MOZ_ASSERT(isGlobal(sig));
+ return SigIdDesc(Kind::Global, globalDataOffset);
+}
+
+static ImmediateType
+LengthToBits(uint32_t length)
+{
+ static_assert(sMaxTypes <= ((1 << sLengthBits) - 1), "fits");
+ MOZ_ASSERT(length <= sMaxTypes);
+ return length;
+}
+
+/* static */ SigIdDesc
+SigIdDesc::immediate(const Sig& sig)
+{
+ ImmediateType immediate = ImmediateBit;
+ uint32_t shift = sTagBits;
+
+ if (sig.ret() != ExprType::Void) {
+ immediate |= (1 << shift);
+ shift += sReturnBit;
+
+ immediate |= EncodeImmediateType(NonVoidToValType(sig.ret())) << shift;
+ shift += sTypeBits;
+ } else {
+ shift += sReturnBit;
+ }
+
+ immediate |= LengthToBits(sig.args().length()) << shift;
+ shift += sLengthBits;
+
+ for (ValType argType : sig.args()) {
+ immediate |= EncodeImmediateType(argType) << shift;
+ shift += sTypeBits;
+ }
+
+ MOZ_ASSERT(shift <= sTotalBits);
+ return SigIdDesc(Kind::Immediate, immediate);
+}
+
+size_t
+SigWithId::serializedSize() const
+{
+ return Sig::serializedSize() +
+ sizeof(id);
+}
+
+uint8_t*
+SigWithId::serialize(uint8_t* cursor) const
+{
+ cursor = Sig::serialize(cursor);
+ cursor = WriteBytes(cursor, &id, sizeof(id));
+ return cursor;
+}
+
+const uint8_t*
+SigWithId::deserialize(const uint8_t* cursor)
+{
+ (cursor = Sig::deserialize(cursor)) &&
+ (cursor = ReadBytes(cursor, &id, sizeof(id)));
+ return cursor;
+}
+
+size_t
+SigWithId::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const
+{
+ return Sig::sizeOfExcludingThis(mallocSizeOf);
+}
+
+Assumptions::Assumptions(JS::BuildIdCharVector&& buildId)
+ : cpuId(GetCPUID()),
+ buildId(Move(buildId))
+{}
+
+Assumptions::Assumptions()
+ : cpuId(GetCPUID()),
+ buildId()
+{}
+
+bool
+Assumptions::initBuildIdFromContext(ExclusiveContext* cx)
+{
+ if (!cx->buildIdOp() || !cx->buildIdOp()(&buildId)) {
+ ReportOutOfMemory(cx);
+ return false;
+ }
+ return true;
+}
+
+bool
+Assumptions::clone(const Assumptions& other)
+{
+ cpuId = other.cpuId;
+ return buildId.appendAll(other.buildId);
+}
+
+bool
+Assumptions::operator==(const Assumptions& rhs) const
+{
+ return cpuId == rhs.cpuId &&
+ buildId.length() == rhs.buildId.length() &&
+ PodEqual(buildId.begin(), rhs.buildId.begin(), buildId.length());
+}
+
+size_t
+Assumptions::serializedSize() const
+{
+ return sizeof(uint32_t) +
+ SerializedPodVectorSize(buildId);
+}
+
+uint8_t*
+Assumptions::serialize(uint8_t* cursor) const
+{
+ // The format of serialized Assumptions must never change in a way that
+ // would cause old cache files written with by an old build-id to match the
+ // assumptions of a different build-id.
+
+ cursor = WriteScalar<uint32_t>(cursor, cpuId);
+ cursor = SerializePodVector(cursor, buildId);
+ return cursor;
+}
+
+const uint8_t*
+Assumptions::deserialize(const uint8_t* cursor, size_t remain)
+{
+ (cursor = ReadScalarChecked<uint32_t>(cursor, &remain, &cpuId)) &&
+ (cursor = DeserializePodVectorChecked(cursor, &remain, &buildId));
+ return cursor;
+}
+
+size_t
+Assumptions::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const
+{
+ return buildId.sizeOfExcludingThis(mallocSizeOf);
+}
+
+// Heap length on ARM should fit in an ARM immediate. We approximate the set
+// of valid ARM immediates with the predicate:
+// 2^n for n in [16, 24)
+// or
+// 2^24 * n for n >= 1.
+bool
+wasm::IsValidARMImmediate(uint32_t i)
+{
+ bool valid = (IsPowerOfTwo(i) ||
+ (i & 0x00ffffff) == 0);
+
+ MOZ_ASSERT_IF(valid, i % PageSize == 0);
+
+ return valid;
+}
+
+uint32_t
+wasm::RoundUpToNextValidARMImmediate(uint32_t i)
+{
+ MOZ_ASSERT(i <= 0xff000000);
+
+ if (i <= 16 * 1024 * 1024)
+ i = i ? mozilla::RoundUpPow2(i) : 0;
+ else
+ i = (i + 0x00ffffff) & ~0x00ffffff;
+
+ MOZ_ASSERT(IsValidARMImmediate(i));
+
+ return i;
+}
+
+#ifndef WASM_HUGE_MEMORY
+
+bool
+wasm::IsValidBoundsCheckImmediate(uint32_t i)
+{
+#ifdef JS_CODEGEN_ARM
+ return IsValidARMImmediate(i);
+#else
+ return true;
+#endif
+}
+
+size_t
+wasm::ComputeMappedSize(uint32_t maxSize)
+{
+ MOZ_ASSERT(maxSize % PageSize == 0);
+
+ // It is the bounds-check limit, not the mapped size, that gets baked into
+ // code. Thus round up the maxSize to the next valid immediate value
+ // *before* adding in the guard page.
+
+# ifdef JS_CODEGEN_ARM
+ uint32_t boundsCheckLimit = RoundUpToNextValidARMImmediate(maxSize);
+# else
+ uint32_t boundsCheckLimit = maxSize;
+# endif
+ MOZ_ASSERT(IsValidBoundsCheckImmediate(boundsCheckLimit));
+
+ MOZ_ASSERT(boundsCheckLimit % gc::SystemPageSize() == 0);
+ MOZ_ASSERT(GuardSize % gc::SystemPageSize() == 0);
+ return boundsCheckLimit + GuardSize;
+}
+
+#endif // WASM_HUGE_MEMORY