commit 5fca0dd3b2149782d64aaacd4e8d4f83fa556341
parent e5f94075691d21bf86086589e65fb7b8e6ac7c0d
Author: Andy Wingo <wingo@igalia.com>
Date: Mon, 8 Dec 2025 16:02:51 +0000
Bug 1977854 - Add PageSize to wasm::Pages. r=rhunt,bvisness
No more public constructors for wasm::Pages; instead there are static
methods. Leads to lots of PageSize::Standard everywhere; with custom
page sizes, these sizes will come from the memory descriptor.
Differential Revision: https://phabricator.services.mozilla.com/D257724
Diffstat:
15 files changed, 217 insertions(+), 118 deletions(-)
diff --git a/js/src/builtin/TestingFunctions.cpp b/js/src/builtin/TestingFunctions.cpp
@@ -974,14 +974,22 @@ static bool WasmMaxMemoryPages(JSContext* cx, unsigned argc, Value* vp) {
if (!ls) {
return false;
}
+ wasm::PageSize pageSize = wasm::PageSize::Standard;
+ if (argc > 1 && args.get(1).isInt32()) {
+ uint32_t pageSizeBytes = args.get(1).toInt32();
+ if (pageSizeBytes != PageSizeInBytes(wasm::PageSize::Standard)) {
+ JS_ReportErrorASCII(cx, "bad page size");
+ return false;
+ }
+ }
if (StringEqualsLiteral(ls, "i32")) {
- args.rval().setInt32(
- int32_t(wasm::MaxMemoryPages(wasm::AddressType::I32).value()));
+ wasm::Pages pages = wasm::MaxMemoryPages(wasm::AddressType::I32, pageSize);
+ args.rval().setInt32(pages.pageCount());
return true;
}
if (StringEqualsLiteral(ls, "i64")) {
- args.rval().setInt32(
- int32_t(wasm::MaxMemoryPages(wasm::AddressType::I64).value()));
+ wasm::Pages pages = wasm::MaxMemoryPages(wasm::AddressType::I64, pageSize);
+ args.rval().setNumber(pages.pageCount());
return true;
}
JS_ReportErrorASCII(cx, "bad address type");
diff --git a/js/src/vm/ArrayBufferObject.cpp b/js/src/vm/ArrayBufferObject.cpp
@@ -817,7 +817,8 @@ bool ArrayBufferObject::resizeImpl(JSContext* cx, const CallArgs& args) {
return false;
}
- Pages newPages = Pages::fromByteLengthExact(newByteLength);
+ Pages newPages =
+ Pages::fromByteLengthExact(newByteLength, wasm::PageSize::Standard);
MOZ_RELEASE_ASSERT(WasmArrayBufferSourceMaxPages(obj).isSome());
Rooted<ArrayBufferObject*> res(
cx,
@@ -1602,10 +1603,15 @@ void WasmArrayRawBuffer::discard(size_t byteOffset, size_t byteLen) {
/* static */
WasmArrayRawBuffer* WasmArrayRawBuffer::AllocateWasm(
- AddressType addressType, Pages initialPages, Pages clampedMaxPages,
- const Maybe<Pages>& sourceMaxPages, const Maybe<size_t>& mapped) {
+ AddressType addressType, wasm::PageSize pageSize, Pages initialPages,
+ Pages clampedMaxPages, const Maybe<Pages>& sourceMaxPages,
+ const Maybe<size_t>& mapped) {
// Prior code has asserted that initial pages is within our implementation
// limits (wasm::MaxMemoryPages) and we can assume it is a valid size_t.
+ MOZ_RELEASE_ASSERT(initialPages.pageSize() == pageSize);
+ MOZ_RELEASE_ASSERT(clampedMaxPages.pageSize() == pageSize);
+ MOZ_RELEASE_ASSERT(!sourceMaxPages.isSome() ||
+ (pageSize == sourceMaxPages->pageSize()));
MOZ_ASSERT(initialPages.hasByteLength());
size_t numBytes = initialPages.byteLength();
@@ -1637,8 +1643,9 @@ WasmArrayRawBuffer* WasmArrayRawBuffer::AllocateWasm(
uint8_t* base = reinterpret_cast<uint8_t*>(data) + gc::SystemPageSize();
uint8_t* header = base - sizeof(WasmArrayRawBuffer);
- auto rawBuf = new (header) WasmArrayRawBuffer(
- addressType, base, clampedMaxPages, sourceMaxPages, mappedSize, numBytes);
+ auto rawBuf = new (header)
+ WasmArrayRawBuffer(addressType, pageSize, base, clampedMaxPages,
+ sourceMaxPages, mappedSize, numBytes);
return rawBuf;
}
@@ -1667,6 +1674,7 @@ template <typename ObjT, typename RawbufT>
static ArrayBufferObjectMaybeShared* CreateSpecificWasmBuffer(
JSContext* cx, const wasm::MemoryDesc& memory) {
bool useHugeMemory = wasm::IsHugeMemoryEnabled(memory.addressType());
+ wasm::PageSize pageSize = memory.pageSize();
Pages initialPages = memory.initialPages();
Maybe<Pages> sourceMaxPages = memory.maximumPages();
Pages clampedMaxPages = wasm::ClampedMaxPages(
@@ -1681,9 +1689,9 @@ static ArrayBufferObjectMaybeShared* CreateSpecificWasmBuffer(
}
#endif
- RawbufT* buffer =
- RawbufT::AllocateWasm(memory.limits.addressType, initialPages,
- clampedMaxPages, sourceMaxPages, mappedSize);
+ RawbufT* buffer = RawbufT::AllocateWasm(
+ memory.limits.addressType, memory.pageSize(), initialPages,
+ clampedMaxPages, sourceMaxPages, mappedSize);
if (!buffer) {
if (useHugeMemory) {
WarnNumberASCII(cx, JSMSG_WASM_HUGE_MEMORY_FAILED);
@@ -1698,24 +1706,33 @@ static ArrayBufferObjectMaybeShared* CreateSpecificWasmBuffer(
// If we fail, and have a sourceMaxPages, try to reserve the biggest
// chunk in the range [initialPages, clampedMaxPages) using log backoff.
if (!sourceMaxPages) {
- wasm::Log(cx, "new Memory({initial=%" PRIu64 " pages}) failed",
- initialPages.value());
+ wasm::Log(cx,
+ "new Memory({initial=%" PRIu64
+ " pages, "
+ "pageSize=%" PRIu32 " bytes}) failed",
+ initialPages.pageCount(),
+ wasm::PageSizeInBytes(initialPages.pageSize()));
ReportOutOfMemory(cx);
return nullptr;
}
- uint64_t cur = clampedMaxPages.value() / 2;
- for (; Pages(cur) > initialPages; cur /= 2) {
- buffer = RawbufT::AllocateWasm(memory.limits.addressType, initialPages,
- Pages(cur), sourceMaxPages, mappedSize);
+ uint64_t cur = clampedMaxPages.pageCount() / 2;
+ for (; cur > initialPages.pageCount(); cur /= 2) {
+ buffer = RawbufT::AllocateWasm(
+ memory.limits.addressType, pageSize, initialPages,
+ Pages::fromPageCount(cur, pageSize), sourceMaxPages, mappedSize);
if (buffer) {
break;
}
}
if (!buffer) {
- wasm::Log(cx, "new Memory({initial=%" PRIu64 " pages}) failed",
- initialPages.value());
+ wasm::Log(cx,
+ "new Memory({initial=%" PRIu64
+ " pages, "
+ "pageSize=%" PRIu32 " bytes}) failed",
+ initialPages.pageCount(),
+ wasm::PageSizeInBytes(initialPages.pageSize()));
ReportOutOfMemory(cx);
return nullptr;
}
@@ -1750,19 +1767,25 @@ static ArrayBufferObjectMaybeShared* CreateSpecificWasmBuffer(
if (useHugeMemory) {
wasm::Log(cx,
"new Memory({initial:%" PRIu64 " pages, maximum:%" PRIu64
- " pages}) succeeded",
- initialPages.value(), sourceMaxPages->value());
+ " pages, pageSize:%" PRIu32 " bytes}) succeeded",
+ initialPages.pageCount(), sourceMaxPages->pageCount(),
+ wasm::PageSizeInBytes(initialPages.pageSize()));
} else {
wasm::Log(cx,
"new Memory({initial:%" PRIu64 " pages, maximum:%" PRIu64
- " pages}) succeeded "
+ " pages, pageSize:%" PRIu32
+ " bytes}) succeeded "
"with internal maximum of %" PRIu64 " pages",
- initialPages.value(), sourceMaxPages->value(),
- object->wasmClampedMaxPages().value());
+ initialPages.pageCount(), sourceMaxPages->pageCount(),
+ wasm::PageSizeInBytes(initialPages.pageSize()),
+ object->wasmClampedMaxPages().pageCount());
}
} else {
- wasm::Log(cx, "new Memory({initial:%" PRIu64 " pages}) succeeded",
- initialPages.value());
+ wasm::Log(cx,
+ "new Memory({initial:%" PRIu64 " pages, pageSize:%" PRIu32
+ " bytes}) succeeded",
+ initialPages.pageCount(),
+ wasm::PageSizeInBytes(initialPages.pageSize()));
}
return object;
@@ -1770,8 +1793,9 @@ static ArrayBufferObjectMaybeShared* CreateSpecificWasmBuffer(
ArrayBufferObjectMaybeShared* js::CreateWasmBuffer(
JSContext* cx, const wasm::MemoryDesc& memory) {
- MOZ_RELEASE_ASSERT(memory.initialPages() <=
- wasm::MaxMemoryPages(memory.addressType()));
+ MOZ_RELEASE_ASSERT(
+ memory.initialPages() <=
+ wasm::MaxMemoryPages(memory.addressType(), memory.pageSize()));
MOZ_RELEASE_ASSERT(cx->wasm().haveSignalHandlers);
if (memory.isShared()) {
@@ -1960,7 +1984,7 @@ Pages ArrayBufferObject::wasmPages() const {
return contents().wasmBuffer()->pages();
}
MOZ_ASSERT(isPreparedForAsmJS());
- return Pages::fromByteLengthExact(byteLength());
+ return Pages::fromByteLengthExact(byteLength(), wasm::PageSize::Standard);
}
Pages ArrayBufferObject::wasmClampedMaxPages() const {
@@ -1968,7 +1992,7 @@ Pages ArrayBufferObject::wasmClampedMaxPages() const {
return contents().wasmBuffer()->clampedMaxPages();
}
MOZ_ASSERT(isPreparedForAsmJS());
- return Pages::fromByteLengthExact(byteLength());
+ return Pages::fromByteLengthExact(byteLength(), wasm::PageSize::Standard);
}
Maybe<Pages> ArrayBufferObject::wasmSourceMaxPages() const {
@@ -1976,7 +2000,8 @@ Maybe<Pages> ArrayBufferObject::wasmSourceMaxPages() const {
return contents().wasmBuffer()->sourceMaxPages();
}
MOZ_ASSERT(isPreparedForAsmJS());
- return Some<Pages>(Pages::fromByteLengthExact(byteLength()));
+ return Some<Pages>(
+ Pages::fromByteLengthExact(byteLength(), wasm::PageSize::Standard));
}
size_t js::WasmArrayBufferMappedSize(const ArrayBufferObjectMaybeShared* buf) {
@@ -2042,7 +2067,7 @@ ArrayBufferObject* ArrayBufferObject::wasmGrowToPagesInPlace(
if (newPages > oldBuf->wasmClampedMaxPages()) {
return nullptr;
}
- MOZ_ASSERT(newPages <= wasm::MaxMemoryPages(t) &&
+ MOZ_ASSERT(newPages <= wasm::MaxMemoryPages(t, newPages.pageSize()) &&
newPages.byteLength() <= ArrayBufferObject::ByteLengthLimit);
if (oldBuf->is<ResizableArrayBufferObject>()) {
@@ -2121,7 +2146,7 @@ ArrayBufferObject* ArrayBufferObject::wasmMovingGrowToPages(
if (newPages > oldBuf->wasmClampedMaxPages()) {
return nullptr;
}
- MOZ_ASSERT(newPages <= wasm::MaxMemoryPages(t) &&
+ MOZ_ASSERT(newPages <= wasm::MaxMemoryPages(t, newPages.pageSize()) &&
newPages.byteLength() <= ArrayBufferObject::ByteLengthLimit);
// We have checked against the clamped maximum and so we know we can convert
@@ -2140,9 +2165,10 @@ ArrayBufferObject* ArrayBufferObject::wasmMovingGrowToPages(
Pages clampedMaxPages =
wasm::ClampedMaxPages(t, newPages, Nothing(), /* hugeMemory */ false);
- WasmArrayRawBuffer* newRawBuf =
- WasmArrayRawBuffer::AllocateWasm(oldBuf->wasmAddressType(), newPages,
- clampedMaxPages, Nothing(), Nothing());
+ wasm::PageSize pageSize = wasm::PageSize::Standard;
+ WasmArrayRawBuffer* newRawBuf = WasmArrayRawBuffer::AllocateWasm(
+ oldBuf->wasmAddressType(), pageSize, newPages, clampedMaxPages, Nothing(),
+ Nothing());
if (!newRawBuf) {
return nullptr;
}
diff --git a/js/src/vm/ArrayBufferObject.h b/js/src/vm/ArrayBufferObject.h
@@ -987,17 +987,19 @@ class MutableWrappedPtrOperations<InnerViewTable, Wrapper>
class WasmArrayRawBuffer {
wasm::AddressType addressType_;
+ wasm::PageSize pageSize_;
wasm::Pages clampedMaxPages_;
mozilla::Maybe<wasm::Pages> sourceMaxPages_;
size_t mappedSize_; // See comment on mappedSize().
size_t length_;
protected:
- WasmArrayRawBuffer(wasm::AddressType addressType, uint8_t* buffer,
- wasm::Pages clampedMaxPages,
+ WasmArrayRawBuffer(wasm::AddressType addressType, wasm::PageSize pageSize,
+ uint8_t* buffer, wasm::Pages clampedMaxPages,
const mozilla::Maybe<wasm::Pages>& sourceMaxPages,
size_t mappedSize, size_t length)
: addressType_(addressType),
+ pageSize_(pageSize),
clampedMaxPages_(clampedMaxPages),
sourceMaxPages_(sourceMaxPages),
mappedSize_(mappedSize),
@@ -1005,12 +1007,15 @@ class WasmArrayRawBuffer {
// Assert that this WasmArrayRawBuffer was allocated in the correct place
// relative to its data.
MOZ_ASSERT(buffer == dataPointer());
+ MOZ_ASSERT(pageSize == clampedMaxPages.pageSize());
+ MOZ_ASSERT_IF(sourceMaxPages.isSome(),
+ (pageSize == sourceMaxPages->pageSize()));
}
public:
static WasmArrayRawBuffer* AllocateWasm(
- wasm::AddressType addressType, wasm::Pages initialPages,
- wasm::Pages clampedMaxPages,
+ wasm::AddressType addressType, wasm::PageSize pageSize,
+ wasm::Pages initialPages, wasm::Pages clampedMaxPages,
const mozilla::Maybe<wasm::Pages>& sourceMaxPages,
const mozilla::Maybe<size_t>& mappedSize);
static void Release(void* mem);
@@ -1031,6 +1036,7 @@ class WasmArrayRawBuffer {
}
wasm::AddressType addressType() const { return addressType_; }
+ wasm::PageSize pageSize() const { return pageSize_; }
uint8_t* basePointer() { return dataPointer() - gc::SystemPageSize(); }
@@ -1055,7 +1061,7 @@ class WasmArrayRawBuffer {
size_t byteLength() const { return length_; }
wasm::Pages pages() const {
- return wasm::Pages::fromByteLengthExact(length_);
+ return wasm::Pages::fromByteLengthExact(length_, wasm::PageSize::Standard);
}
/*
diff --git a/js/src/vm/SharedArrayObject.cpp b/js/src/vm/SharedArrayObject.cpp
@@ -96,12 +96,16 @@ SharedArrayRawBuffer* SharedArrayRawBuffer::Allocate(bool isGrowable,
}
WasmSharedArrayRawBuffer* WasmSharedArrayRawBuffer::AllocateWasm(
- wasm::AddressType addressType, Pages initialPages,
+ wasm::AddressType addressType, wasm::PageSize pageSize, Pages initialPages,
wasm::Pages clampedMaxPages,
const mozilla::Maybe<wasm::Pages>& sourceMaxPages,
const mozilla::Maybe<size_t>& mappedSize) {
// Prior code has asserted that initial pages is within our implementation
// limits (wasm::MaxMemoryPages()) and we can assume it is a valid size_t.
+ MOZ_RELEASE_ASSERT(initialPages.pageSize() == pageSize);
+ MOZ_RELEASE_ASSERT(clampedMaxPages.pageSize() == pageSize);
+ MOZ_RELEASE_ASSERT(!sourceMaxPages.isSome() ||
+ (pageSize == sourceMaxPages->pageSize()));
MOZ_ASSERT(initialPages.hasByteLength());
size_t length = initialPages.byteLength();
@@ -130,7 +134,8 @@ WasmSharedArrayRawBuffer* WasmSharedArrayRawBuffer::AllocateWasm(
uint8_t* base = buffer - sizeof(WasmSharedArrayRawBuffer);
return new (base) WasmSharedArrayRawBuffer(
buffer, length, addressType, clampedMaxPages,
- sourceMaxPages.valueOr(Pages(0)), computedMappedSize);
+ sourceMaxPages.valueOr(Pages::fromPageCount(0, pageSize)),
+ computedMappedSize);
}
bool WasmSharedArrayRawBuffer::wasmGrowToPagesInPlace(const Lock&,
@@ -142,7 +147,7 @@ bool WasmSharedArrayRawBuffer::wasmGrowToPagesInPlace(const Lock&,
if (newPages > clampedMaxPages_) {
return false;
}
- MOZ_ASSERT(newPages <= wasm::MaxMemoryPages(t) &&
+ MOZ_ASSERT(newPages <= wasm::MaxMemoryPages(t, newPages.pageSize()) &&
newPages.byteLength() <= ArrayBufferObject::ByteLengthLimit);
// We have checked against the clamped maximum and so we know we can convert
@@ -426,7 +431,8 @@ bool SharedArrayBufferObject::growImpl(JSContext* cx, const CallArgs& args) {
return false;
}
- Pages newPages = Pages::fromByteLengthExact(newByteLength);
+ Pages newPages =
+ Pages::fromByteLengthExact(newByteLength, wasm::PageSize::Standard);
return buffer->rawWasmBufferObject()->wasmGrowToPagesInPlace(
*lock, buffer->wasmAddressType(), newPages);
}
diff --git a/js/src/vm/SharedArrayObject.h b/js/src/vm/SharedArrayObject.h
@@ -167,8 +167,8 @@ class WasmSharedArrayRawBuffer : public SharedArrayRawBuffer {
};
static WasmSharedArrayRawBuffer* AllocateWasm(
- wasm::AddressType addressType, wasm::Pages initialPages,
- wasm::Pages clampedMaxPages,
+ wasm::AddressType addressType, wasm::PageSize pageSize,
+ wasm::Pages initialPages, wasm::Pages clampedMaxPages,
const mozilla::Maybe<wasm::Pages>& sourceMaxPages,
const mozilla::Maybe<size_t>& mappedSize);
@@ -185,7 +185,7 @@ class WasmSharedArrayRawBuffer : public SharedArrayRawBuffer {
wasm::AddressType wasmAddressType() const { return addressType_; }
wasm::Pages volatileWasmPages() const {
- return wasm::Pages::fromByteLengthExact(length_);
+ return wasm::Pages::fromByteLengthExact(length_, wasm::PageSize::Standard);
}
wasm::Pages wasmClampedMaxPages() const { return clampedMaxPages_; }
diff --git a/js/src/wasm/AsmJS.cpp b/js/src/wasm/AsmJS.cpp
@@ -7428,7 +7428,7 @@ bool js::IsValidAsmJSHeapLength(size_t length) {
}
// The heap length is limited by what a wasm memory32 can handle.
- if (length > MaxMemoryBytes(AddressType::I32)) {
+ if (length > MaxMemoryBytes(AddressType::I32, wasm::PageSize::Standard)) {
return false;
}
diff --git a/js/src/wasm/WasmBCMemory.cpp b/js/src/wasm/WasmBCMemory.cpp
@@ -422,8 +422,8 @@ void BaseCompiler::prepareMemoryAccess(MemoryAccessDesc* access,
// bits are in the bound.
if (!codeMeta_.memories[access->memoryIndex()]
.boundsCheckLimitIsAlways32Bits() &&
- MaxMemoryBytes(
- codeMeta_.memories[access->memoryIndex()].addressType()) >=
+ MaxMemoryBytes(codeMeta_.memories[access->memoryIndex()].addressType(),
+ codeMeta_.memories[access->memoryIndex()].pageSize()) >=
0x100000000) {
boundsCheck4GBOrLargerAccess(access->memoryIndex(), instance, ptr, &ok);
} else {
diff --git a/js/src/wasm/WasmDump.cpp b/js/src/wasm/WasmDump.cpp
@@ -744,9 +744,13 @@ void wasm::DumpMemoryDesc(const MemoryDesc& memDesc, StructuredPrinter& out,
if (memDesc.addressType() == AddressType::I64) {
out.printf("i64 ");
}
- out.printf("%" PRIu64, memDesc.initialPages().value());
+ out.printf("%" PRIu64, memDesc.initialPages().pageCount());
if (memDesc.maximumPages().isSome()) {
- out.printf(" %" PRIu64, memDesc.maximumPages().value().value());
+ out.printf(" %" PRIu64, memDesc.maximumPages().value().pageCount());
+ }
+ if (memDesc.initialPages().pageSize() != PageSize::Standard) {
+ out.printf("(pagesize %" PRIu32 ")",
+ static_cast<uint32_t>(memDesc.initialPages().pageSize()));
}
out.printf(")");
}
diff --git a/js/src/wasm/WasmInstance.cpp b/js/src/wasm/WasmInstance.cpp
@@ -614,9 +614,10 @@ static int32_t PerformWake(Instance* instance, PtrT byteOffset, int32_t count,
Pages pages = instance->memory(memoryIndex)->volatilePages();
#ifdef JS_64BIT
// Ensure that the memory size is no more than 4GiB.
- MOZ_ASSERT(pages <= Pages(MaxMemory32PagesValidation));
+ MOZ_ASSERT(pages <= Pages::fromPageCount(MaxMemory32PagesValidation,
+ pages.pageSize()));
#endif
- return uint32_t(pages.value());
+ return uint32_t(pages.pageCount());
}
/* static */ uint64_t Instance::memorySize_m64(Instance* instance,
@@ -630,9 +631,10 @@ static int32_t PerformWake(Instance* instance, PtrT byteOffset, int32_t count,
Pages pages = instance->memory(memoryIndex)->volatilePages();
#ifdef JS_64BIT
- MOZ_ASSERT(pages <= Pages(MaxMemory64PagesValidation));
+ MOZ_ASSERT(pages <= Pages::fromPageCount(MaxMemory64PagesValidation,
+ pages.pageSize()));
#endif
- return pages.value();
+ return pages.pageCount();
}
template <typename PointerT, typename CopyFuncT, typename IndexT>
diff --git a/js/src/wasm/WasmIonCompile.cpp b/js/src/wasm/WasmIonCompile.cpp
@@ -1579,6 +1579,8 @@ class FunctionCompiler {
}
MWasmLoadInstance* needBoundsCheck(uint32_t memoryIndex) {
+ MOZ_RELEASE_ASSERT(codeMeta().memories[memoryIndex].pageSize() ==
+ PageSize::Standard);
#ifdef JS_64BIT
// For 32-bit base pointers:
//
@@ -1592,7 +1594,8 @@ class FunctionCompiler {
bool mem32LimitIs64Bits =
isMem32(memoryIndex) &&
!codeMeta().memories[memoryIndex].boundsCheckLimitIsAlways32Bits() &&
- MaxMemoryBytes(codeMeta().memories[memoryIndex].addressType()) >=
+ MaxMemoryBytes(codeMeta().memories[memoryIndex].addressType(),
+ codeMeta().memories[memoryIndex].pageSize()) >=
0x100000000;
#else
// On 32-bit platforms we have no more than 2GB memory and the limit for a
diff --git a/js/src/wasm/WasmJS.cpp b/js/src/wasm/WasmJS.cpp
@@ -996,7 +996,7 @@ static JSObject* MemoryTypeToObject(JSContext* cx, bool shared,
if (maxPages) {
RootedId maximumId(cx, NameToId(cx->names().maximum));
RootedValue maximumValue(cx);
- if (!CreateAddressValue(cx, maxPages.value().value(), addressType,
+ if (!CreateAddressValue(cx, maxPages.value().pageCount(), addressType,
&maximumValue)) {
ReportOutOfMemory(cx);
return nullptr;
@@ -1009,7 +1009,8 @@ static JSObject* MemoryTypeToObject(JSContext* cx, bool shared,
RootedId minimumId(cx, NameToId(cx->names().minimum));
RootedValue minimumValue(cx);
- if (!CreateAddressValue(cx, minPages.value(), addressType, &minimumValue)) {
+ if (!CreateAddressValue(cx, minPages.pageCount(), addressType,
+ &minimumValue)) {
ReportOutOfMemory(cx);
return nullptr;
}
@@ -2177,7 +2178,8 @@ bool WasmMemoryObject::construct(JSContext* cx, unsigned argc, Value* vp) {
return false;
}
- if (Pages(limits.initial) > MaxMemoryPages(limits.addressType)) {
+ if (Pages::fromPageCount(limits.initial, PageSize::Standard) >
+ MaxMemoryPages(limits.addressType, PageSize::Standard)) {
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
JSMSG_WASM_MEM_IMP_LIMIT);
return false;
@@ -2601,7 +2603,8 @@ size_t WasmMemoryObject::boundsCheckLimit() const {
MOZ_ASSERT(mappedSize % wasm::StandardPageSizeBytes == 0);
MOZ_ASSERT(mappedSize >= wasm::GuardSize);
size_t limit = mappedSize - wasm::GuardSize;
- MOZ_ASSERT(limit <= MaxMemoryBoundsCheckLimit(addressType()));
+ MOZ_ASSERT(limit <= MaxMemoryBoundsCheckLimit(addressType(),
+ wasm::PageSize::Standard));
return limit;
}
@@ -2632,7 +2635,7 @@ uint64_t WasmMemoryObject::growShared(Handle<WasmMemoryObject*> memory,
Pages oldNumPages = rawBuf->volatileWasmPages();
Pages newPages = oldNumPages;
- if (!newPages.checkedIncrement(Pages(delta))) {
+ if (!newPages.checkedIncrement(delta)) {
return uint64_t(int64_t(-1));
}
@@ -2642,7 +2645,7 @@ uint64_t WasmMemoryObject::growShared(Handle<WasmMemoryObject*> memory,
// New buffer objects will be created lazily in all agents (including in
// this agent) by bufferGetterImpl, above, so no more work to do here.
- return oldNumPages.value();
+ return oldNumPages.pageCount();
}
/* static */
@@ -2658,13 +2661,14 @@ uint64_t WasmMemoryObject::grow(Handle<WasmMemoryObject*> memory,
#if !defined(JS_64BIT)
// TODO (large ArrayBuffer): See more information at the definition of
// MaxMemoryBytes().
- MOZ_ASSERT(MaxMemoryBytes(memory->addressType()) <= UINT32_MAX,
- "Avoid 32-bit overflows");
+ MOZ_ASSERT(
+ MaxMemoryBytes(memory->addressType(), PageSize::Standard) <= UINT32_MAX,
+ "Avoid 32-bit overflows");
#endif
Pages oldNumPages = oldBuf->wasmPages();
Pages newPages = oldNumPages;
- if (!newPages.checkedIncrement(Pages(delta))) {
+ if (!newPages.checkedIncrement(delta)) {
return uint64_t(int64_t(-1));
}
@@ -2692,7 +2696,7 @@ uint64_t WasmMemoryObject::grow(Handle<WasmMemoryObject*> memory,
}
}
- return oldNumPages.value();
+ return oldNumPages.pageCount();
}
/* static */
diff --git a/js/src/wasm/WasmMemory.cpp b/js/src/wasm/WasmMemory.cpp
@@ -313,36 +313,39 @@ static const size_t MinOffsetGuardLimit = OffsetGuardLimit;
static_assert(MaxInlineMemoryCopyLength < MinOffsetGuardLimit, "precondition");
static_assert(MaxInlineMemoryFillLength < MinOffsetGuardLimit, "precondition");
-wasm::Pages wasm::MaxMemoryPages(AddressType t) {
+wasm::Pages wasm::MaxMemoryPages(AddressType t, PageSize pageSize) {
#ifdef JS_64BIT
MOZ_ASSERT_IF(t == AddressType::I64, !IsHugeMemoryEnabled(t));
size_t desired = MaxMemoryPagesValidation(t);
- constexpr size_t actual =
- ArrayBufferObject::ByteLengthLimit / StandardPageSizeBytes;
- return wasm::Pages(std::min(desired, actual));
+ size_t actual =
+ ArrayBufferObject::ByteLengthLimit / PageSizeInBytes(pageSize);
+ return wasm::Pages::fromPageCount(std::min(desired, actual), pageSize);
#else
// On 32-bit systems, the heap limit must be representable in the nonnegative
// range of an int32_t, which means the maximum heap size as observed by wasm
// code is one wasm page less than 2GB.
- static_assert(ArrayBufferObject::ByteLengthLimit >=
- INT32_MAX / StandardPageSizeBytes);
- return wasm::Pages(INT32_MAX / StandardPageSizeBytes);
+ MOZ_ASSERT(ArrayBufferObject::ByteLengthLimit >=
+ INT32_MAX / PageSizeInBytes(pageSize));
+ return wasm::Pages::fromPageCount(INT32_MAX / PageSizeInBytes(pageSize),
+ pageSize);
#endif
}
-size_t wasm::MaxMemoryBoundsCheckLimit(AddressType t) {
- return MaxMemoryBytes(t);
+size_t wasm::MaxMemoryBoundsCheckLimit(AddressType t, PageSize pageSize) {
+ return MaxMemoryBytes(t, pageSize);
}
Pages wasm::ClampedMaxPages(AddressType t, Pages initialPages,
const mozilla::Maybe<Pages>& sourceMaxPages,
bool useHugeMemory) {
- Pages clampedMaxPages;
+ PageSize pageSize = initialPages.pageSize();
+ Pages clampedMaxPages = Pages::forPageSize(pageSize);
if (sourceMaxPages.isSome()) {
// There is a specified maximum, clamp it to the implementation limit of
// maximum pages
- clampedMaxPages = std::min(*sourceMaxPages, wasm::MaxMemoryPages(t));
+ clampedMaxPages =
+ std::min(*sourceMaxPages, wasm::MaxMemoryPages(t, pageSize));
#ifndef JS_64BIT
static_assert(sizeof(uintptr_t) == 4, "assuming not 64 bit implies 32 bit");
@@ -352,8 +355,7 @@ Pages wasm::ClampedMaxPages(AddressType t, Pages initialPages,
// "a lot of memory". Maintain the invariant that initialPages <=
// clampedMaxPages.
static const uint64_t OneGib = 1 << 30;
- static const Pages OneGibPages =
- Pages(OneGib / wasm::StandardPageSizeBytes);
+ const Pages OneGibPages = Pages::fromByteLengthExact(OneGib, pageSize);
Pages clampedPages = std::max(OneGibPages, initialPages);
clampedMaxPages = std::min(clampedPages, clampedMaxPages);
@@ -361,13 +363,13 @@ Pages wasm::ClampedMaxPages(AddressType t, Pages initialPages,
} else {
// There is not a specified maximum, fill it in with the implementation
// limit of maximum pages
- clampedMaxPages = wasm::MaxMemoryPages(t);
+ clampedMaxPages = wasm::MaxMemoryPages(t, pageSize);
}
// Double-check our invariants
MOZ_RELEASE_ASSERT(sourceMaxPages.isNothing() ||
clampedMaxPages <= *sourceMaxPages);
- MOZ_RELEASE_ASSERT(clampedMaxPages <= wasm::MaxMemoryPages(t));
+ MOZ_RELEASE_ASSERT(clampedMaxPages <= wasm::MaxMemoryPages(t, pageSize));
MOZ_RELEASE_ASSERT(initialPages <= clampedMaxPages);
return clampedMaxPages;
diff --git a/js/src/wasm/WasmMemory.h b/js/src/wasm/WasmMemory.h
@@ -66,13 +66,16 @@ static_assert(StandardPageSizeBytes == 64 * 1024);
static_assert((StandardPageSizeBytes * MaxMemory64PagesValidation) <=
(uint64_t(1) << 53) - 1);
-// Pages is a typed unit representing a multiple of wasm::StandardPageSizeBytes.
+// Pages is a typed unit representing a multiple of the page size, which
+// defaults to wasm::StandardPageSizeBytes. The page size can be customized
+// only if the custom page sizes proposal is enabled.
+//
// We generally use pages as the unit of length when representing linear memory
// lengths so as to avoid overflow when the specified initial or maximum pages
// would overflow the native word size.
//
-// Modules may specify pages up to 2^48 inclusive and so Pages is 64-bit on all
-// platforms.
+// Modules may specify pages up to 2^48 (or 2^64 - 1 with tiny pages) inclusive
+// and so Pages is 64-bit on all platforms.
//
// We represent byte lengths using the native word size, as it is assumed that
// consumers of this API will only need byte lengths once it is time to
@@ -83,74 +86,103 @@ struct Pages {
private:
// Pages are specified by limit fields, which in general may be up to 2^48,
// so we must use uint64_t here.
- uint64_t value_;
+ uint64_t pageCount_;
+ PageSize pageSize_;
+
+ constexpr Pages(uint64_t pageCount, PageSize pageSize)
+ : pageCount_(pageCount), pageSize_(pageSize) {}
public:
- constexpr Pages() : value_(0) {}
- constexpr explicit Pages(uint64_t value) : value_(value) {}
+ static constexpr Pages fromPageCount(uint64_t pageCount, PageSize pageSize) {
+ return Pages(pageCount, pageSize);
+ }
- // Get the wrapped page value. Only use this if you must, prefer to use or
- // add new APIs to Page.
- uint64_t value() const { return value_; }
+ static constexpr Pages forPageSize(PageSize pageSize) {
+ return Pages(0, pageSize);
+ }
+
+ static constexpr bool byteLengthIsMultipleOfPageSize(size_t byteLength,
+ PageSize pageSize) {
+ return byteLength % PageSizeInBytes(pageSize) == 0;
+ }
// Converts from a byte length to pages, assuming that the length is an
// exact multiple of the page size.
- static Pages fromByteLengthExact(size_t byteLength) {
- MOZ_ASSERT(byteLength % StandardPageSizeBytes == 0);
- return Pages(byteLength / StandardPageSizeBytes);
+ static constexpr Pages fromByteLengthExact(size_t byteLength,
+ PageSize pageSize) {
+ MOZ_RELEASE_ASSERT(byteLengthIsMultipleOfPageSize(byteLength, pageSize));
+ return Pages(byteLength / PageSizeInBytes(pageSize), pageSize);
+ }
+
+ Pages& operator=(const Pages& other) {
+ MOZ_RELEASE_ASSERT(other.pageSize_ == pageSize_);
+ pageCount_ = other.pageCount_;
+ return *this;
}
+ // Get the wrapped page count and size. Only use this if you must, prefer to
+ // use or add new APIs to Page.
+ uint64_t pageCount() const { return pageCount_; }
+ PageSize pageSize() const { return pageSize_; }
+
// Return whether the page length may overflow when converted to a byte
// length in the native word size.
bool hasByteLength() const {
- mozilla::CheckedInt<size_t> length(value_);
- length *= StandardPageSizeBytes;
+ mozilla::CheckedInt<size_t> length(pageCount_);
+ length *= PageSizeInBytes(pageSize_);
return length.isValid();
}
// Converts from pages to byte length in the native word size. Users must
// check for overflow, or be assured else-how that overflow cannot happen.
size_t byteLength() const {
- mozilla::CheckedInt<size_t> length(value_);
- length *= StandardPageSizeBytes;
+ mozilla::CheckedInt<size_t> length(pageCount_);
+ length *= PageSizeInBytes(pageSize_);
return length.value();
}
// Return the byteLength for a 64-bits memory.
uint64_t byteLength64() const {
- mozilla::CheckedInt<uint64_t> length(value_);
- length *= StandardPageSizeBytes;
+ mozilla::CheckedInt<uint64_t> length(pageCount_);
+ length *= PageSizeInBytes(pageSize_);
return length.value();
}
// Increment this pages by delta and return whether the resulting value
// did not overflow. If there is no overflow, then this is set to the
// resulting value.
- bool checkedIncrement(Pages delta) {
- mozilla::CheckedInt<uint64_t> newValue = value_;
- newValue += delta.value_;
+ bool checkedIncrement(uint64_t delta) {
+ mozilla::CheckedInt<uint64_t> newValue = pageCount_;
+ newValue += delta;
if (!newValue.isValid()) {
return false;
}
- value_ = newValue.value();
+ pageCount_ = newValue.value();
return true;
}
// Implement pass-through comparison operators so that Pages can be compared.
- constexpr auto operator<=>(const Pages& other) const = default;
+ constexpr auto operator<=>(const Pages& other) const {
+ MOZ_RELEASE_ASSERT(other.pageSize_ == pageSize_);
+ return pageCount_ <=> other.pageCount_;
+ };
+ constexpr auto operator==(const Pages& other) const {
+ MOZ_RELEASE_ASSERT(other.pageSize_ == pageSize_);
+ return pageCount_ == other.pageCount_;
+ };
};
// The largest number of pages the application can request.
-extern Pages MaxMemoryPages(AddressType t);
+extern Pages MaxMemoryPages(AddressType t, PageSize pageSize);
// The byte value of MaxMemoryPages(t).
-static inline size_t MaxMemoryBytes(AddressType t) {
- return MaxMemoryPages(t).byteLength();
+static inline size_t MaxMemoryBytes(AddressType t, PageSize pageSize) {
+ return MaxMemoryPages(t, pageSize).byteLength();
}
// A value representing the largest valid value for boundsCheckLimit.
-extern size_t MaxMemoryBoundsCheckLimit(AddressType t);
+extern size_t MaxMemoryBoundsCheckLimit(AddressType t, PageSize pageSize);
static inline uint64_t MaxMemoryPagesValidation(AddressType addressType) {
return addressType == AddressType::I32 ? MaxMemory32PagesValidation
diff --git a/js/src/wasm/WasmModule.cpp b/js/src/wasm/WasmModule.cpp
@@ -509,7 +509,8 @@ bool Module::instantiateMemories(
}
if (!CheckLimits(cx, desc.initialPages(), desc.maximumPages(),
- /* defaultMax */ MaxMemoryPages(desc.addressType()),
+ /* defaultMax */
+ MaxMemoryPages(desc.addressType(), desc.pageSize()),
/* actualLength */
memory->volatilePages(), memory->sourceMaxPages(),
codeMeta().isAsmJS(), "Memory")) {
@@ -522,7 +523,8 @@ bool Module::instantiateMemories(
} else {
MOZ_ASSERT(!codeMeta().isAsmJS());
- if (desc.initialPages() > MaxMemoryPages(desc.addressType())) {
+ if (desc.initialPages() >
+ MaxMemoryPages(desc.addressType(), desc.pageSize())) {
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
JSMSG_WASM_MEM_IMP_LIMIT);
return false;
diff --git a/js/src/wasm/WasmModuleTypes.h b/js/src/wasm/WasmModuleTypes.h
@@ -834,20 +834,24 @@ struct MemoryDesc {
AddressType addressType() const { return limits.addressType; }
+ PageSize pageSize() const { return PageSize::Standard; }
+
// The initial length of this memory in pages.
- Pages initialPages() const { return Pages(limits.initial); }
+ Pages initialPages() const {
+ return Pages::fromPageCount(limits.initial, pageSize());
+ }
// The maximum length of this memory in pages.
mozilla::Maybe<Pages> maximumPages() const {
- return limits.maximum.map([](uint64_t x) { return Pages(x); });
+ return limits.maximum.map(
+ [&](uint64_t x) { return Pages::fromPageCount(x, pageSize()); });
}
- // The initial length of this memory in bytes.
uint64_t initialLength() const {
// See static_assert after MemoryDesc for why this is safe for memory32.
MOZ_ASSERT_IF(addressType() == AddressType::I64,
- limits.initial <= UINT64_MAX / StandardPageSizeBytes);
- return limits.initial * StandardPageSizeBytes;
+ limits.initial <= UINT64_MAX / PageSizeInBytes(pageSize()));
+ return initialPages().byteLength();
}
MemoryDesc() = default;