diff --git a/CMakeLists.txt b/CMakeLists.txt index 0279e7d3e43..654cc8e0ca9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -401,6 +401,8 @@ if(MSVC) add_compile_flag("/WX-") add_compile_flag("/D_CRT_SECURE_NO_WARNINGS") add_compile_flag("/D_SCL_SECURE_NO_WARNINGS") + # add warnings specifically to non exhaustive switch on enums + add_compile_flag("/w14062") if(RUN_STATIC_ANALYZER) add_definitions(/analyze) diff --git a/scripts/test/shared.py b/scripts/test/shared.py index a5702ddb4b0..1621a60884d 100644 --- a/scripts/test/shared.py +++ b/scripts/test/shared.py @@ -405,6 +405,8 @@ def get_tests(test_dir, extensions=[], recursive=False): # Unlinkable module accepted 'linking.wast', + 'memory_max.wast', + 'memory_max_i64.wast', # Invalid module accepted 'unreached-invalid.wast', diff --git a/src/binaryen-c.cpp b/src/binaryen-c.cpp index d5f6fbc7e3c..8dcc3c2b516 100644 --- a/src/binaryen-c.cpp +++ b/src/binaryen-c.cpp @@ -487,6 +487,9 @@ BinaryenFeatures BinaryenFeatureBulkMemoryOpt(void) { BinaryenFeatures BinaryenFeatureCallIndirectOverlong(void) { return static_cast(FeatureSet::CallIndirectOverlong); } +BinaryenFeatures BinaryenFeatureCustomPageSizes(void) { + return static_cast(FeatureSet::CustomPageSizes); +} BinaryenFeatures BinaryenFeatureAll(void) { return static_cast(FeatureSet::All); } diff --git a/src/binaryen-c.h b/src/binaryen-c.h index 60e88d78b29..477183a9b3c 100644 --- a/src/binaryen-c.h +++ b/src/binaryen-c.h @@ -230,6 +230,7 @@ BINARYEN_API BinaryenFeatures BinaryenFeatureSharedEverything(void); BINARYEN_API BinaryenFeatures BinaryenFeatureFP16(void); BINARYEN_API BinaryenFeatures BinaryenFeatureBulkMemoryOpt(void); BINARYEN_API BinaryenFeatures BinaryenFeatureCallIndirectOverlong(void); +BINARYEN_API BinaryenFeatures BinaryenFeatureCustomPageSizes(void); BINARYEN_API BinaryenFeatures BinaryenFeatureAll(void); // Modules diff --git a/src/ir/module-utils.cpp b/src/ir/module-utils.cpp index c19ae369eb9..8ca318b2c53 100644 --- a/src/ir/module-utils.cpp +++ b/src/ir/module-utils.cpp @@ -173,6 +173,7 @@ Memory* copyMemory(const Memory* memory, Module& out) { ret->hasExplicitName = memory->hasExplicitName; ret->initial = memory->initial; ret->max = memory->max; + ret->pageSizeLog2 = memory->pageSizeLog2; ret->shared = memory->shared; ret->addressType = memory->addressType; ret->module = memory->module; diff --git a/src/js/binaryen.js-post.js b/src/js/binaryen.js-post.js index 1743c97388e..473c86d972b 100644 --- a/src/js/binaryen.js-post.js +++ b/src/js/binaryen.js-post.js @@ -180,6 +180,7 @@ function initializeConstants() { 'FP16', 'BulkMemoryOpt', 'CallIndirectOverlong', + 'CustomPageSizes', 'All' ].forEach(name => { Module['Features'][name] = Module['_BinaryenFeature' + name](); diff --git a/src/parser/context-decls.cpp b/src/parser/context-decls.cpp index c124689d334..aaf0b174ecc 100644 --- a/src/parser/context-decls.cpp +++ b/src/parser/context-decls.cpp @@ -143,6 +143,7 @@ Result ParseDeclsCtx::addMemoryDecl(Index pos, m->initial = type.limits.initial; m->max = type.limits.max ? *type.limits.max : Memory::kUnlimitedSize; m->shared = type.shared; + m->pageSizeLog2 = type.pageSizeLog2; if (name) { // TODO: if the existing memory is not explicitly named, fix its name // and continue. diff --git a/src/parser/contexts.h b/src/parser/contexts.h index f27a7128738..cd3f7e148c1 100644 --- a/src/parser/contexts.h +++ b/src/parser/contexts.h @@ -49,6 +49,7 @@ struct Limits { struct MemType { Type addressType; Limits limits; + uint8_t pageSizeLog2; bool shared; }; @@ -353,7 +354,9 @@ template struct TypeParserCtx { Result makeLimits(uint64_t, std::optional) { return Ok{}; } LimitsT getLimitsFromData(DataStringT) { return Ok{}; } - MemTypeT makeMemType(Type, LimitsT, bool) { return Ok{}; } + MemTypeT makeMemType(Type, LimitsT, bool, std::optional) { + return Ok{}; + } HeapType getBlockTypeFromResult(const std::vector results) { assert(results.size() == 1); @@ -1052,13 +1055,20 @@ struct ParseDeclsCtx : NullTypeParserCtx, NullInstrParserCtx { data.insert(data.end(), str.begin(), str.end()); } - Limits getLimitsFromData(const std::vector& data) { - uint64_t size = (data.size() + Memory::kPageSize - 1) / Memory::kPageSize; + Limits getLimitsFromData(const std::vector& data, + std::optional pageSizeLog2) { + uint8_t _pageSizeLog2 = pageSizeLog2.value_or(16); + uint64_t size = + (data.size() + (1 << _pageSizeLog2) - 1) / (1 << _pageSizeLog2); return {size, size}; } - MemType makeMemType(Type addressType, Limits limits, bool shared) { - return {addressType, limits, shared}; + MemType makeMemType(Type addressType, + Limits limits, + bool shared, + std::optional pageSize) { + uint8_t pageSizeLog2 = pageSize.value_or(16); + return {addressType, limits, pageSizeLog2, shared}; } Result @@ -1400,8 +1410,12 @@ struct ParseModuleTypesCtx : TypeParserCtx, Type makeTableType(Type addressType, LimitsT, Type type) { return type; } - LimitsT getLimitsFromData(DataStringT) { return Ok{}; } - MemTypeT makeMemType(Type, LimitsT, bool) { return Ok{}; } + LimitsT getLimitsFromData(DataStringT, std::optional) { + return Ok{}; + } + MemTypeT makeMemType(Type, LimitsT, bool, std::optional) { + return Ok{}; + } Result<> addFunc(Name name, const std::vector&, diff --git a/src/parser/parsers.h b/src/parser/parsers.h index 1d1974a9d27..0cd2bf25469 100644 --- a/src/parser/parsers.h +++ b/src/parser/parsers.h @@ -818,7 +818,35 @@ template Result limits64(Ctx& ctx) { return ctx.makeLimits(uint64_t(*n), m); } -// memtype ::= (limits32 | 'i32' limits32 | 'i64' limit64) shared? +// mempagesize? ::= ('(' 'pagesize' u64 ')') ? +template Result> mempagesize(Ctx& ctx) { + if (!ctx.in.takeSExprStart("pagesize"sv)) { + return std::nullopt; // No pagesize specified + } + auto pageSize = ctx.in.takeU64(); + if (!pageSize) { + return ctx.in.err("expected page size"); + } + + if (!Bits::isPowerOf2(*pageSize)) { + return ctx.in.err("page size must be a power of two"); + } + + if (!ctx.in.takeRParen()) { + return ctx.in.err("expected end of mempagesize"); + } + + // return the log2 of the page size, which is the number of trailing zeros + uint8_t pageSizeLog2 = (uint8_t)Bits::ceilLog2(*pageSize); + + if (pageSizeLog2 != 0 && pageSizeLog2 != Memory::kDefaultPageSizeLog2) { + return ctx.in.err("memory page size can only be 1 or 64 KiB"); + } + + return std::make_optional(pageSizeLog2); +} + +// memtype ::= (limits32 | 'i32' limits32 | 'i64' limit64) shared? mempagesize? // note: the index type 'i32' or 'i64' is already parsed to simplify parsing of // memory abbreviations. template Result memtype(Ctx& ctx) { @@ -840,7 +868,9 @@ Result memtypeContinued(Ctx& ctx, Type addressType) { if (ctx.in.takeKeyword("shared"sv)) { shared = true; } - return ctx.makeMemType(addressType, *limits, shared); + auto pageSize = mempagesize(ctx); + CHECK_ERR(pageSize); + return ctx.makeMemType(addressType, *limits, shared, *pageSize); } // memorder ::= '' | 'seqcst' | 'acqrel' @@ -3434,6 +3464,8 @@ template MaybeResult<> memory(Ctx& ctx) { std::optional mtype; std::optional data; + auto mempageSize = mempagesize(ctx); + CHECK_ERR(mempageSize); if (ctx.in.takeSExprStart("data"sv)) { if (import) { return ctx.in.err("imported memories cannot have inline data"); @@ -3443,9 +3475,15 @@ template MaybeResult<> memory(Ctx& ctx) { if (!ctx.in.takeRParen()) { return ctx.in.err("expected end of inline data"); } - mtype = - ctx.makeMemType(addressType, ctx.getLimitsFromData(*datastr), false); + mtype = ctx.makeMemType(addressType, + ctx.getLimitsFromData(*datastr, *mempageSize), + false, + *mempageSize); data = *datastr; + } else if ((*mempageSize).has_value()) { + // If we have a memory page size not within a memtype expression, we expect + // a memory abbreviation. + return ctx.in.err("expected data segment in memory abbreviation"); } else { auto type = memtypeContinued(ctx, addressType); CHECK_ERR(type); diff --git a/src/passes/LLVMMemoryCopyFillLowering.cpp b/src/passes/LLVMMemoryCopyFillLowering.cpp index bf0225daca6..c76ef2634fa 100644 --- a/src/passes/LLVMMemoryCopyFillLowering.cpp +++ b/src/passes/LLVMMemoryCopyFillLowering.cpp @@ -117,14 +117,19 @@ struct LLVMMemoryCopyFillLowering void createMemoryCopyFunc(Module* module) { Builder b(*module); Index dst = 0, src = 1, size = 2, start = 3, end = 4, step = 5, i = 6; - Name memory = module->memories.front()->name; + Name memory_name = module->memories.front()->name; + Address::address32_t memory_page_size = + module->memories.front()->pageSizeLog2; Block* body = b.makeBlock(); // end = memory size in bytes body->list.push_back( b.makeLocalSet(end, - b.makeBinary(BinaryOp::MulInt32, - b.makeMemorySize(memory), - b.makeConst(Memory::kPageSize)))); + memory_page_size == 0 + ? static_cast(b.makeMemorySize(memory_name)) + : static_cast( + b.makeBinary(BinaryOp::ShlInt32, + b.makeMemorySize(memory_name), + b.makeConst(memory_page_size))))); // if dst + size > memsize or src + size > memsize, then trap. body->list.push_back(b.makeIf( b.makeBinary(BinaryOp::OrInt32, @@ -187,9 +192,9 @@ struct LLVMMemoryCopyFillLowering b.makeLocalGet(src, Type::i32), b.makeLocalGet(i, Type::i32)), Type::i32, - memory), + memory_name), Type::i32, - memory), + memory_name), // i += step b.makeLocalSet(i, b.makeBinary(BinaryOp::AddInt32, @@ -203,19 +208,24 @@ struct LLVMMemoryCopyFillLowering void createMemoryFillFunc(Module* module) { Builder b(*module); Index dst = 0, val = 1, size = 2; - Name memory = module->memories.front()->name; + Name memory_name = module->memories.front()->name; + Address::address32_t memory_page_size = + module->memories.front()->pageSizeLog2; Block* body = b.makeBlock(); // if dst + size > memsize in bytes, then trap. - body->list.push_back( - b.makeIf(b.makeBinary(BinaryOp::GtUInt32, - b.makeBinary(BinaryOp::AddInt32, - b.makeLocalGet(dst, Type::i32), - b.makeLocalGet(size, Type::i32)), - b.makeBinary(BinaryOp::MulInt32, - b.makeMemorySize(memory), - b.makeConst(Memory::kPageSize))), - b.makeUnreachable())); + body->list.push_back(b.makeIf( + b.makeBinary(BinaryOp::GtUInt32, + b.makeBinary(BinaryOp::AddInt32, + b.makeLocalGet(dst, Type::i32), + b.makeLocalGet(size, Type::i32)), + memory_page_size == 0 + ? static_cast(b.makeMemorySize(memory_name)) + : static_cast( + b.makeBinary(BinaryOp::ShlInt32, + b.makeMemorySize(memory_name), + b.makeConst(memory_page_size)))), + b.makeUnreachable())); body->list.push_back(b.makeBlock( "out", @@ -241,7 +251,7 @@ struct LLVMMemoryCopyFillLowering b.makeLocalGet(size, Type::i32)), b.makeLocalGet(val, Type::i32), Type::i32, - memory), + memory_name), b.makeBreak("copy", nullptr)})))); module->getFunction(memFillFuncName)->body = body; } diff --git a/src/passes/Memory64Lowering.cpp b/src/passes/Memory64Lowering.cpp index 1506c016eea..76777df50d3 100644 --- a/src/passes/Memory64Lowering.cpp +++ b/src/passes/Memory64Lowering.cpp @@ -293,8 +293,8 @@ struct Memory64Lowering : public WalkerPass> { for (auto& memory : module->memories) { if (memory->is64()) { memory->addressType = Type::i32; - if (memory->hasMax() && memory->max > Memory::kMaxSize32) { - memory->max = Memory::kMaxSize32; + if (memory->hasMax() && memory->max > memory->maxSize32()) { + memory->max = memory->maxSize32(); } } } diff --git a/src/passes/MemoryPacking.cpp b/src/passes/MemoryPacking.cpp index 6c411378b23..6c3b72fbf9c 100644 --- a/src/passes/MemoryPacking.cpp +++ b/src/passes/MemoryPacking.cpp @@ -315,7 +315,7 @@ void MemoryPacking::calculateRanges(Module* module, // Check if we can rule out a trap by it being in bounds. if (auto* c = segment->offset->dynCast()) { auto* memory = module->getMemory(segment->memory); - auto memorySize = memory->initial * Memory::kPageSize; + auto memorySize = memory->initial << memory->pageSizeLog2; Index start = c->value.getUnsigned(); Index size = segment->data.size(); Index end; diff --git a/src/passes/MultiMemoryLowering.cpp b/src/passes/MultiMemoryLowering.cpp index 9397f6cad44..5d0a2f19137 100644 --- a/src/passes/MultiMemoryLowering.cpp +++ b/src/passes/MultiMemoryLowering.cpp @@ -68,9 +68,11 @@ struct MultiMemoryLowering : public Pass { // properties will be set Name module; Name base; - // The initial page size of the combined memory + // The page size of the combined memory + uint8_t pageSizeLog2; + // The initial page count of the combined memory Address totalInitialPages; - // The max page size of the combined memory + // The max page count of the combined memory Address totalMaxPages; // There is no offset for the first memory, so offsetGlobalNames will always // have a size that is one less than the count of memories at the time this @@ -435,6 +437,7 @@ struct MultiMemoryLowering : public Pass { : Builder::MemoryInfo::Memory64; isShared = getFirstMemory().shared; isImported = getFirstMemory().imported(); + pageSizeLog2 = Memory::kDefaultPageSizeLog2; for (auto& memory : wasm->memories) { // We are assuming that each memory is configured the same as the first // and assert if any of the memories does not match this configuration @@ -446,18 +449,25 @@ struct MultiMemoryLowering : public Pass { Fatal() << "MultiMemoryLowering: only the first memory can be imported"; } + // Calculating the page size of the combined memory. + // This corresponds to the smaller granularity among combined memories + pageSizeLog2 = std::min(pageSizeLog2, memory->pageSizeLog2); + // Calculating the total initial and max page size for the combined memory // by totaling the initial and max page sizes for the memories in the // module - totalInitialPages = totalInitialPages + memory->initial; + totalInitialPages = + totalInitialPages + + (memory->initial << (memory->pageSizeLog2 - pageSizeLog2)); if (memory->hasMax()) { - totalMaxPages = totalMaxPages + memory->max; + totalMaxPages = totalMaxPages + + (memory->max << (memory->pageSizeLog2 - pageSizeLog2)); } } // Ensuring valid initial and max page sizes that do not exceed the number // of pages addressable by the pointerType - Address maxSize = - pointerType == Type::i32 ? Memory::kMaxSize32 : Memory::kMaxSize64; + Address maxSize = pointerType == Type::i32 ? 1ull << (32 - pageSizeLog2) + : 1ull << (64 - pageSizeLog2); if (totalMaxPages > maxSize || totalMaxPages == 0) { totalMaxPages = Memory::kUnlimitedSize; } @@ -504,9 +514,10 @@ struct MultiMemoryLowering : public Pass { Name name = Names::getValidGlobalName( *wasm, memory->name.toString() + "_byte_offset"); offsetGlobalNames.push_back(std::move(name)); - addGlobal(name, offsetRunningTotal * Memory::kPageSize); + addGlobal(name, offsetRunningTotal << pageSizeLog2); } - offsetRunningTotal += memory->initial; + offsetRunningTotal += memory->initial + << (memory->pageSizeLog2 - pageSizeLog2); } } @@ -553,13 +564,18 @@ struct MultiMemoryLowering : public Pass { auto function = Builder::makeFunction( functionName, Signature(pointerType, pointerType), {}); function->setLocalName(0, "page_delta"); + auto currPageSizeLog2 = wasm->memories[memIdx]->pageSizeLog2; auto pageSizeConst = [&]() { - return builder.makeConst(Literal(Memory::kPageSize)); + return builder.makeConst(Literal(currPageSizeLog2)); }; auto getOffsetDelta = [&]() { - return builder.makeBinary(Abstract::getBinary(pointerType, Abstract::Mul), - builder.makeLocalGet(0, pointerType), - pageSizeConst()); + if (currPageSizeLog2 == 0) { + return static_cast(builder.makeLocalGet(0, pointerType)); + } + return static_cast( + builder.makeBinary(Abstract::getBinary(pointerType, Abstract::Shl), + builder.makeLocalGet(0, pointerType), + pageSizeConst())); }; auto getMoveSource = [&](Name global) { return builder.makeGlobalGet(global, pointerType); @@ -588,7 +604,14 @@ struct MultiMemoryLowering : public Pass { builder.makeBinary( EqInt32, builder.makeMemoryGrow( - builder.makeLocalGet(0, pointerType), combinedMemory, memoryInfo), + currPageSizeLog2 - pageSizeLog2 == 0 + ? static_cast(builder.makeLocalGet(0, pointerType)) + : static_cast(builder.makeBinary( + Abstract::getBinary(pointerType, Abstract::Shl), + builder.makeLocalGet(0, pointerType), + builder.makeConst(Literal(currPageSizeLog2 - pageSizeLog2)))), + combinedMemory, + memoryInfo), builder.makeConst(-1)), builder.makeReturn(builder.makeConst(-1)))); @@ -609,9 +632,13 @@ struct MultiMemoryLowering : public Pass { // size builder.makeBinary( Abstract::getBinary(pointerType, Abstract::Sub), - builder.makeBinary(Abstract::getBinary(pointerType, Abstract::Mul), - builder.makeLocalGet(sizeLocal, pointerType), - pageSizeConst()), + currPageSizeLog2 == 0 + ? static_cast( + builder.makeLocalGet(sizeLocal, pointerType)) + : static_cast(builder.makeBinary( + Abstract::getBinary(pointerType, Abstract::Shl), + builder.makeLocalGet(sizeLocal, pointerType), + pageSizeConst())), getMoveSource(offsetGlobalName)), combinedMemory, combinedMemory)); @@ -646,11 +673,11 @@ struct MultiMemoryLowering : public Pass { functionName, Signature(Type::none, pointerType), {}); Expression* functionBody; auto pageSizeConst = [&]() { - return builder.makeConst(Literal(Memory::kPageSize)); + return builder.makeConst(Literal(pageSizeLog2)); }; auto getOffsetInPageUnits = [&](Name global) { return builder.makeBinary( - Abstract::getBinary(pointerType, Abstract::DivU), + Abstract::getBinary(pointerType, Abstract::ShrU), builder.makeGlobalGet(global, pointerType), pageSizeConst()); }; @@ -697,6 +724,7 @@ struct MultiMemoryLowering : public Pass { memory->base = base; memory->module = module; } + memory->pageSizeLog2 = pageSizeLog2; wasm->addMemory(std::move(memory)); } diff --git a/src/passes/Print.cpp b/src/passes/Print.cpp index f60642a23c8..9327a067db9 100644 --- a/src/passes/Print.cpp +++ b/src/passes/Print.cpp @@ -3408,6 +3408,11 @@ void PrintSExpression::printMemoryHeader(Memory* curr) { if (curr->shared) { printMedium(o, " shared"); } + if (curr->pageSizeLog2 != Memory::kDefaultPageSizeLog2) { + o << " ("; + printMedium(o, "pagesize") << ' ' << (1 << (curr->pageSizeLog2)); + o << ')'; + } o << ")"; } diff --git a/src/passes/RemoveUnusedModuleElements.cpp b/src/passes/RemoveUnusedModuleElements.cpp index cbb1180231e..969cdd1dcf8 100644 --- a/src/passes/RemoveUnusedModuleElements.cpp +++ b/src/passes/RemoveUnusedModuleElements.cpp @@ -689,7 +689,7 @@ struct RemoveUnusedModuleElements : public Pass { segment->data.size(), segment->offset, memory, - memory->initial * Memory::kPageSize); + memory->initial << memory->pageSizeLog2); } }); ModuleUtils::iterActiveElementSegments( diff --git a/src/shell-interface.h b/src/shell-interface.h index 3a8b6d23314..a89c3e2ce74 100644 --- a/src/shell-interface.h +++ b/src/shell-interface.h @@ -114,7 +114,7 @@ struct ShellExternalInterface : ModuleRunner::ExternalInterface { void init(Module& wasm, ModuleRunner& instance) override { ModuleUtils::iterDefinedMemories(wasm, [&](wasm::Memory* memory) { auto shellMemory = Memory(); - shellMemory.resize(memory->initial * wasm::Memory::kPageSize); + shellMemory.resize(memory->initial << memory->pageSizeLog2); memories[memory->name] = shellMemory; }); ModuleUtils::iterDefinedTables( diff --git a/src/tools/fuzzing/fuzzing.cpp b/src/tools/fuzzing/fuzzing.cpp index b79d9264ab9..6cf5bdfa157 100644 --- a/src/tools/fuzzing/fuzzing.cpp +++ b/src/tools/fuzzing/fuzzing.cpp @@ -771,9 +771,10 @@ void TranslateToFuzzReader::finalizeMemory() { // annoying in the fuzzer). Address ONE_GB = 1024 * 1024 * 1024; if (maxOffset <= ONE_GB) { - memory->initial = std::max( - memory->initial, - Address((maxOffset + Memory::kPageSize - 1) / Memory::kPageSize)); + memory->initial = + std::max(memory->initial, + Address((maxOffset + (1 << memory->pageSizeLog2) - 1) / + (1 << memory->pageSizeLog2))); } } memory->initial = std::max(memory->initial, fuzzParams->USABLE_MEMORY); @@ -787,7 +788,7 @@ void TranslateToFuzzReader::finalizeMemory() { // maximum larger than the initial. // TODO: scan the wasm for grow instructions? memory->max = - std::min(Address(memory->initial + 1), Address(Memory::kMaxSize32)); + std::min(Address(memory->initial + 1), Address(memory->maxSize32())); } if (!preserveImportsAndExports) { diff --git a/src/tools/wasm-split/instrumenter.cpp b/src/tools/wasm-split/instrumenter.cpp index 93971ed941b..5e107245c86 100644 --- a/src/tools/wasm-split/instrumenter.cpp +++ b/src/tools/wasm-split/instrumenter.cpp @@ -81,7 +81,10 @@ void Instrumenter::addSecondaryMemory(size_t numFuncs) { secondaryMemory = Names::getValidMemoryName(*wasm, config.secondaryMemoryName); // Create a memory with enough pages to write into - size_t pages = (numFuncs + Memory::kPageSize - 1) / Memory::kPageSize; + // The memory uses the default page size to avoid issues in case custom page + // sizes are not supported + size_t pages = + (numFuncs + Memory::kDefaultPageSize - 1) / Memory::kDefaultPageSize; auto mem = Builder::makeMemory(secondaryMemory, pages, pages, true); mem->module = config.importNamespace; mem->base = config.secondaryMemoryName; @@ -183,15 +186,20 @@ void Instrumenter::addProfileExport(size_t numFuncs) { const size_t profileSize = 8 + 4 * numFuncs; // Make sure there is a memory with enough pages to write into - size_t pages = (profileSize + Memory::kPageSize - 1) / Memory::kPageSize; if (wasm->memories.empty()) { + size_t pages = + (profileSize + Memory::kDefaultPageSize - 1) / Memory::kDefaultPageSize; wasm->addMemory(Builder::makeMemory("0")); wasm->memories[0]->initial = pages; wasm->memories[0]->max = pages; - } else if (wasm->memories[0]->initial < pages) { - wasm->memories[0]->initial = pages; - if (wasm->memories[0]->max < pages) { - wasm->memories[0]->max = pages; + } else { + size_t pages = (profileSize + (1 << wasm->memories[0]->pageSizeLog2) - 1) / + (1 << wasm->memories[0]->pageSizeLog2); + if (wasm->memories[0]->initial < pages) { + wasm->memories[0]->initial = pages; + if (wasm->memories[0]->max < pages) { + wasm->memories[0]->max = pages; + } } } diff --git a/src/wasm-binary.h b/src/wasm-binary.h index 857ebe0d540..f61e924b16d 100644 --- a/src/wasm-binary.h +++ b/src/wasm-binary.h @@ -454,6 +454,7 @@ extern const char* FP16Feature; extern const char* BulkMemoryOptFeature; extern const char* CallIndirectOverlongFeature; extern const char* CustomDescriptorsFeature; +extern const char* CustomPageSizesFeature; enum Subsection { NameModule = 0, @@ -1252,7 +1253,12 @@ enum MemoryAccess { NaturalAlignment = 0 }; -enum MemoryFlags { HasMaximum = 1 << 0, IsShared = 1 << 1, Is64 = 1 << 2 }; +enum MemoryFlags { + HasMaximum = 1 << 0, + IsShared = 1 << 1, + Is64 = 1 << 2, + HasCustomPageSize = 1 << 3 +}; enum FeaturePrefix { FeatureUsed = '+', FeatureDisallowed = '-' }; @@ -1366,8 +1372,13 @@ class WasmBinaryWriter { void write(); void writeHeader(); int32_t writeU32LEBPlaceholder(); - void writeResizableLimits( - Address initial, Address maximum, bool hasMaximum, bool shared, bool is64); + void writeResizableLimits(Address initial, + Address maximum, + bool hasMaximum, + bool shared, + bool is64, + bool hasPageSize, + Address::address32_t pageSizeLog2); template int32_t startSection(T code); void finishSection(int32_t start); int32_t startSubsection(BinaryConsts::CustomSections::Subsection code); @@ -1622,6 +1633,7 @@ class WasmBinaryReader { Address& max, bool& shared, Type& addressType, + uint8_t& pageSizeLog2, Address defaultIfNoMax); void readImports(); diff --git a/src/wasm-builder.h b/src/wasm-builder.h index 7bee64c2407..13bb16fb51f 100644 --- a/src/wasm-builder.h +++ b/src/wasm-builder.h @@ -110,15 +110,18 @@ class Builder { return seg; } - static std::unique_ptr makeMemory(Name name, - Address initial = 0, - Address max = Memory::kMaxSize32, - bool shared = false, - Type addressType = Type::i32) { + static std::unique_ptr + makeMemory(Name name, + Address initial = 0, + Address max = Memory::kDefaultMaxSize32, + bool shared = false, + uint8_t pageSizeLog2 = Memory::kDefaultPageSizeLog2, + Type addressType = Type::i32) { auto memory = std::make_unique(); memory->name = name; memory->initial = initial; memory->max = max; + memory->pageSizeLog2 = pageSizeLog2; memory->shared = shared; memory->addressType = addressType; return memory; diff --git a/src/wasm-features.h b/src/wasm-features.h index a7c3ce0c4f5..645a379fb38 100644 --- a/src/wasm-features.h +++ b/src/wasm-features.h @@ -55,11 +55,12 @@ struct FeatureSet { // it does nothing. Binaryen always accepts LEB call-indirect encodings. CallIndirectOverlong = 1 << 20, CustomDescriptors = 1 << 21, + CustomPageSizes = 1 << 22, MVP = None, // Keep in sync with llvm default features: // https://github.com/llvm/llvm-project/blob/c7576cb89d6c95f03968076e902d3adfd1996577/clang/lib/Basic/Targets/WebAssembly.cpp#L150-L153 Default = SignExt | MutableGlobals, - All = (1 << 22) - 1, + All = (1 << 23) - 1, }; static std::string toString(Feature f) { @@ -108,6 +109,8 @@ struct FeatureSet { return "call-indirect-overlong"; case CustomDescriptors: return "custom-descriptors"; + case CustomPageSizes: + return "custom-page-sizes"; case MVP: case Default: case All: @@ -168,6 +171,7 @@ struct FeatureSet { bool hasCustomDescriptors() const { return (features & CustomDescriptors) != 0; } + bool hasCustomPageSizes() const { return (features & CustomPageSizes) != 0; } bool hasAll() const { return (features & All) != 0; } void set(FeatureSet f, bool v = true) { diff --git a/src/wasm-interpreter.h b/src/wasm-interpreter.h index 29334d90130..c1095942c41 100644 --- a/src/wasm-interpreter.h +++ b/src/wasm-interpreter.h @@ -3258,6 +3258,8 @@ class ModuleRunnerBase : public ExpressionRunner { // in pages, used to keep track of memorySize throughout the below memops std::unordered_map memorySizes; + std::unordered_map memoryPageSizes; + void initializeMemorySizes() { for (auto& memory : wasm.memories) { memorySizes[memory->name] = memory->initial; @@ -3709,10 +3711,13 @@ class ModuleRunnerBase : public ExpressionRunner { NOTE_EVAL1(flow); auto info = getMemoryInstanceInfo(curr->memory); auto memorySize = info.instance->getMemorySize(info.name); - auto addr = - info.instance->getFinalAddress(curr, flow.getSingleValue(), memorySize); + auto* memory = info.instance->wasm.getMemory(info.name); + auto memoryPageSize = memory->pageSizeLog2; + auto addr = info.instance->getFinalAddress( + curr, flow.getSingleValue(), memorySize, memoryPageSize); if (curr->isAtomic) { - info.instance->checkAtomicAddress(addr, curr->bytes, memorySize); + info.instance->checkAtomicAddress( + addr, curr->bytes, memorySize, memoryPageSize); } auto ret = info.interface()->load(curr, addr, info.name); NOTE_EVAL1(addr); @@ -3731,10 +3736,13 @@ class ModuleRunnerBase : public ExpressionRunner { } auto info = getMemoryInstanceInfo(curr->memory); auto memorySize = info.instance->getMemorySize(info.name); - auto addr = - info.instance->getFinalAddress(curr, ptr.getSingleValue(), memorySize); + auto* memory = info.instance->wasm.getMemory(info.name); + auto memoryPageSize = memory->pageSizeLog2; + auto addr = info.instance->getFinalAddress( + curr, ptr.getSingleValue(), memorySize, memoryPageSize); if (curr->isAtomic) { - info.instance->checkAtomicAddress(addr, curr->bytes, memorySize); + info.instance->checkAtomicAddress( + addr, curr->bytes, memorySize, memoryPageSize); } NOTE_EVAL1(addr); NOTE_EVAL1(value); @@ -3755,12 +3763,14 @@ class ModuleRunnerBase : public ExpressionRunner { NOTE_EVAL1(ptr); auto info = getMemoryInstanceInfo(curr->memory); auto memorySize = info.instance->getMemorySize(info.name); - auto addr = - info.instance->getFinalAddress(curr, ptr.getSingleValue(), memorySize); + auto* memory = info.instance->wasm.getMemory(info.name); + auto memoryPageSize = memory->pageSizeLog2; + auto addr = info.instance->getFinalAddress( + curr, ptr.getSingleValue(), memorySize, memoryPageSize); NOTE_EVAL1(addr); NOTE_EVAL1(value); auto loaded = info.instance->doAtomicLoad( - addr, curr->bytes, curr->type, info.name, memorySize); + addr, curr->bytes, curr->type, info.name, memorySize, memoryPageSize); NOTE_EVAL1(loaded); auto computed = value.getSingleValue(); switch (curr->op) { @@ -3783,7 +3793,7 @@ class ModuleRunnerBase : public ExpressionRunner { break; } info.instance->doAtomicStore( - addr, curr->bytes, computed, info.name, memorySize); + addr, curr->bytes, computed, info.name, memorySize, memoryPageSize); return loaded; } Flow visitAtomicCmpxchg(AtomicCmpxchg* curr) { @@ -3803,18 +3813,24 @@ class ModuleRunnerBase : public ExpressionRunner { } auto info = getMemoryInstanceInfo(curr->memory); auto memorySize = info.instance->getMemorySize(info.name); - auto addr = - info.instance->getFinalAddress(curr, ptr.getSingleValue(), memorySize); + auto* memory = info.instance->wasm.getMemory(info.name); + auto memoryPageSize = memory->pageSizeLog2; + auto addr = info.instance->getFinalAddress( + curr, ptr.getSingleValue(), memorySize, memoryPageSize); expected = Flow(wrapToSmallerSize(expected.getSingleValue(), curr->bytes)); NOTE_EVAL1(addr); NOTE_EVAL1(expected); NOTE_EVAL1(replacement); auto loaded = info.instance->doAtomicLoad( - addr, curr->bytes, curr->type, info.name, memorySize); + addr, curr->bytes, curr->type, info.name, memorySize, memoryPageSize); NOTE_EVAL1(loaded); if (loaded == expected.getSingleValue()) { - info.instance->doAtomicStore( - addr, curr->bytes, replacement.getSingleValue(), info.name, memorySize); + info.instance->doAtomicStore(addr, + curr->bytes, + replacement.getSingleValue(), + info.name, + memorySize, + memoryPageSize); } return loaded; } @@ -3838,10 +3854,12 @@ class ModuleRunnerBase : public ExpressionRunner { auto bytes = curr->expectedType.getByteSize(); auto info = getMemoryInstanceInfo(curr->memory); auto memorySize = info.instance->getMemorySize(info.name); + auto* memory = info.instance->wasm.getMemory(info.name); + auto memoryPageSize = memory->pageSizeLog2; auto addr = info.instance->getFinalAddress( - curr, ptr.getSingleValue(), bytes, memorySize); + curr, ptr.getSingleValue(), bytes, memorySize, memoryPageSize); auto loaded = info.instance->doAtomicLoad( - addr, bytes, curr->expectedType, info.name, memorySize); + addr, bytes, curr->expectedType, info.name, memorySize, memoryPageSize); NOTE_EVAL1(loaded); if (loaded != expected.getSingleValue()) { return Literal(int32_t(1)); // not equal @@ -3870,10 +3888,12 @@ class ModuleRunnerBase : public ExpressionRunner { } auto info = getMemoryInstanceInfo(curr->memory); auto memorySize = info.instance->getMemorySize(info.name); - auto addr = - info.instance->getFinalAddress(curr, ptr.getSingleValue(), 4, memorySize); + auto* memory = info.instance->wasm.getMemory(info.name); + auto memoryPageSize = memory->pageSizeLog2; + auto addr = info.instance->getFinalAddress( + curr, ptr.getSingleValue(), 4, memorySize, memoryPageSize); // Just check TODO actual threads support - info.instance->checkAtomicAddress(addr, 4, memorySize); + info.instance->checkAtomicAddress(addr, 4, memorySize, memoryPageSize); return Literal(int32_t(0)); // none woken up } Flow visitSIMDLoad(SIMDLoad* curr) { @@ -3960,12 +3980,14 @@ class ModuleRunnerBase : public ExpressionRunner { WASM_UNREACHABLE("invalid op"); }; auto memorySize = info.instance->getMemorySize(info.name); + auto* memory = info.instance->wasm.getMemory(info.name); + auto memoryPageSize = memory->pageSizeLog2; auto addressType = curr->ptr->type; auto fillLanes = [&](auto lanes, size_t laneBytes) { for (auto& lane : lanes) { auto ptr = Literal::makeFromInt64(src, addressType); - lane = loadLane( - info.instance->getFinalAddress(curr, ptr, laneBytes, memorySize)); + lane = loadLane(info.instance->getFinalAddress( + curr, ptr, laneBytes, memorySize, memoryPageSize)); src = ptr.add(Literal::makeFromInt32(laneBytes, addressType)).getUnsigned(); } @@ -4000,8 +4022,13 @@ class ModuleRunnerBase : public ExpressionRunner { NOTE_EVAL1(flow); auto info = getMemoryInstanceInfo(curr->memory); auto memorySize = info.instance->getMemorySize(info.name); - Address src = info.instance->getFinalAddress( - curr, flow.getSingleValue(), curr->getMemBytes(), memorySize); + auto* memory = info.instance->wasm.getMemory(info.name); + auto memoryPageSize = memory->pageSizeLog2; + Address src = info.instance->getFinalAddress(curr, + flow.getSingleValue(), + curr->getMemBytes(), + memorySize, + memoryPageSize); auto zero = Literal::makeZero(curr->op == Load32ZeroVec128 ? Type::i32 : Type::i64); if (curr->op == Load32ZeroVec128) { @@ -4025,8 +4052,13 @@ class ModuleRunnerBase : public ExpressionRunner { } auto info = getMemoryInstanceInfo(curr->memory); auto memorySize = info.instance->getMemorySize(info.name); - Address addr = info.instance->getFinalAddress( - curr, ptrFlow.getSingleValue(), curr->getMemBytes(), memorySize); + auto* memory = info.instance->wasm.getMemory(info.name); + auto memoryPageSize = memory->pageSizeLog2; + Address addr = info.instance->getFinalAddress(curr, + ptrFlow.getSingleValue(), + curr->getMemBytes(), + memorySize, + memoryPageSize); Literal vec = vecFlow.getSingleValue(); switch (curr->op) { case Load8LaneVec128: @@ -4100,6 +4132,7 @@ class ModuleRunnerBase : public ExpressionRunner { auto info = getMemoryInstanceInfo(curr->memory); auto memorySize = info.instance->getMemorySize(info.name); auto* memory = info.instance->wasm.getMemory(info.name); + auto memoryPageSize = memory->pageSizeLog2; auto addressType = memory->addressType; auto fail = Literal::makeFromInt64(-1, memory->addressType); Flow ret = Literal::makeFromInt64(memorySize, addressType); @@ -4107,7 +4140,7 @@ class ModuleRunnerBase : public ExpressionRunner { uint64_t maxAddr = addressType == Type::i32 ? std::numeric_limits::max() : std::numeric_limits::max(); - if (delta > maxAddr / Memory::kPageSize) { + if (delta > maxAddr >> memoryPageSize) { // Impossible to grow this much. return fail; } @@ -4119,9 +4152,8 @@ class ModuleRunnerBase : public ExpressionRunner { if (newSize > memory->max) { return fail; } - if (!info.interface()->growMemory(info.name, - memorySize * Memory::kPageSize, - newSize * Memory::kPageSize)) { + if (!info.interface()->growMemory( + info.name, memorySize << memoryPageSize, newSize << memoryPageSize)) { // We failed to grow the memory in practice, even though it was valid // to try to do so. return fail; @@ -4162,15 +4194,17 @@ class ModuleRunnerBase : public ExpressionRunner { } auto info = getMemoryInstanceInfo(curr->memory); auto memorySize = info.instance->getMemorySize(info.name); - if (destVal + sizeVal > memorySize * Memory::kPageSize) { + auto* memory = info.instance->wasm.getMemory(info.name); + auto memoryPageSize = memory->pageSizeLog2; + if (destVal + sizeVal > memorySize << memoryPageSize) { trap("out of bounds memory access in memory.init"); } for (size_t i = 0; i < sizeVal; ++i) { Literal addr(destVal + i); - info.interface()->store8( - info.instance->getFinalAddressWithoutOffset(addr, 1, memorySize), - segment->data[offsetVal + i], - info.name); + info.interface()->store8(info.instance->getFinalAddressWithoutOffset( + addr, 1, memorySize, memoryPageSize), + segment->data[offsetVal + i], + info.name); } return {}; } @@ -4204,8 +4238,12 @@ class ModuleRunnerBase : public ExpressionRunner { auto sourceInfo = getMemoryInstanceInfo(curr->sourceMemory); auto destMemorySize = destInfo.instance->getMemorySize(destInfo.name); auto sourceMemorySize = sourceInfo.instance->getMemorySize(sourceInfo.name); - if (sourceVal + sizeVal > sourceMemorySize * Memory::kPageSize || - destVal + sizeVal > destMemorySize * Memory::kPageSize || + auto* destMemory = destInfo.instance->wasm.getMemory(destInfo.name); + auto* sourceMemory = sourceInfo.instance->wasm.getMemory(sourceInfo.name); + auto destMemoryPageSize = destMemory->pageSizeLog2; + auto sourceMemoryPageSize = sourceMemory->pageSizeLog2; + if (sourceVal + sizeVal > sourceMemorySize << sourceMemoryPageSize || + destVal + sizeVal > destMemorySize << destMemoryPageSize || // FIXME: better/cheaper way to detect wrapping? sourceVal + sizeVal < sourceVal || sourceVal + sizeVal < sizeVal || destVal + sizeVal < destVal || destVal + sizeVal < sizeVal) { @@ -4224,10 +4262,10 @@ class ModuleRunnerBase : public ExpressionRunner { for (int64_t i = start; i != end; i += step) { destInfo.interface()->store8( destInfo.instance->getFinalAddressWithoutOffset( - Literal(destVal + i), 1, destMemorySize), + Literal(destVal + i), 1, destMemorySize, destMemoryPageSize), sourceInfo.interface()->load8s( sourceInfo.instance->getFinalAddressWithoutOffset( - Literal(sourceVal + i), 1, sourceMemorySize), + Literal(sourceVal + i), 1, sourceMemorySize, sourceMemoryPageSize), sourceInfo.name), destInfo.name); } @@ -4255,18 +4293,21 @@ class ModuleRunnerBase : public ExpressionRunner { auto info = getMemoryInstanceInfo(curr->memory); auto memorySize = info.instance->getMemorySize(info.name); + auto* memory = info.instance->wasm.getMemory(info.name); + auto memoryPageSize = memory->pageSizeLog2; // FIXME: cheaper wrapping detection? - if (destVal > memorySize * Memory::kPageSize || - sizeVal > memorySize * Memory::kPageSize || - destVal + sizeVal > memorySize * Memory::kPageSize) { + if (destVal > memorySize << memoryPageSize || + sizeVal > memorySize << memoryPageSize || + destVal + sizeVal > memorySize << memoryPageSize) { trap("out of bounds memory access in memory.fill"); } uint8_t val(value.getSingleValue().geti32()); for (size_t i = 0; i < sizeVal; ++i) { - info.interface()->store8(info.instance->getFinalAddressWithoutOffset( - Literal(destVal + i), 1, memorySize), - val, - info.name); + info.interface()->store8( + info.instance->getFinalAddressWithoutOffset( + Literal(destVal + i), 1, memorySize, memoryPageSize), + val, + info.name); } return {}; } @@ -4668,37 +4709,51 @@ class ModuleRunnerBase : public ExpressionRunner { } template - Address - getFinalAddress(LS* curr, Literal ptr, Index bytes, Address memorySize) { - Address memorySizeBytes = memorySize * Memory::kPageSize; + Address getFinalAddress(LS* curr, + Literal ptr, + Index bytes, + Address memorySize, + Address::address32_t memoryPageSize) { + Address memorySizeBytes = memorySize << memoryPageSize; uint64_t addr = ptr.type == Type::i32 ? ptr.geti32() : ptr.geti64(); trapIfGt(curr->offset, memorySizeBytes, "offset > memory"); trapIfGt(addr, memorySizeBytes - curr->offset, "final > memory"); addr += curr->offset; trapIfGt(bytes, memorySizeBytes, "bytes > memory"); - checkLoadAddress(addr, bytes, memorySize); + checkLoadAddress(addr, bytes, memorySize, memoryPageSize); return addr; } template - Address getFinalAddress(LS* curr, Literal ptr, Address memorySize) { - return getFinalAddress(curr, ptr, curr->bytes, memorySize); + Address getFinalAddress(LS* curr, + Literal ptr, + Address memorySize, + Address::address32_t memoryPageSize) { + return getFinalAddress(curr, ptr, curr->bytes, memorySize, memoryPageSize); } - Address - getFinalAddressWithoutOffset(Literal ptr, Index bytes, Address memorySize) { + Address getFinalAddressWithoutOffset(Literal ptr, + Index bytes, + Address memorySize, + Address::address32_t memoryPageSize) { uint64_t addr = ptr.type == Type::i32 ? ptr.geti32() : ptr.geti64(); - checkLoadAddress(addr, bytes, memorySize); + checkLoadAddress(addr, bytes, memorySize, memoryPageSize); return addr; } - void checkLoadAddress(Address addr, Index bytes, Address memorySize) { - Address memorySizeBytes = memorySize * Memory::kPageSize; + void checkLoadAddress(Address addr, + Index bytes, + Address memorySize, + Address::address32_t memoryPageSize) { + Address memorySizeBytes = memorySize << memoryPageSize; trapIfGt(addr, memorySizeBytes - bytes, "highest > memory"); } - void checkAtomicAddress(Address addr, Index bytes, Address memorySize) { - checkLoadAddress(addr, bytes, memorySize); + void checkAtomicAddress(Address addr, + Index bytes, + Address memorySize, + Address::address32_t memoryPageSize) { + checkLoadAddress(addr, bytes, memorySize, memoryPageSize); // Unaligned atomics trap. if (bytes > 1) { if (addr & (bytes - 1)) { @@ -4707,9 +4762,13 @@ class ModuleRunnerBase : public ExpressionRunner { } } - Literal doAtomicLoad( - Address addr, Index bytes, Type type, Name memoryName, Address memorySize) { - checkAtomicAddress(addr, bytes, memorySize); + Literal doAtomicLoad(Address addr, + Index bytes, + Type type, + Name memoryName, + Address memorySize, + Address::address32_t memoryPageSize) { + checkAtomicAddress(addr, bytes, memorySize, memoryPageSize); Const ptr; ptr.value = Literal(int32_t(addr)); ptr.type = Type::i32; @@ -4730,8 +4789,9 @@ class ModuleRunnerBase : public ExpressionRunner { Index bytes, Literal toStore, Name memoryName, - Address memorySize) { - checkAtomicAddress(addr, bytes, memorySize); + Address memorySize, + Address::address32_t memoryPageSize) { + checkAtomicAddress(addr, bytes, memorySize, memoryPageSize); Const ptr; ptr.value = Literal(int32_t(addr)); ptr.type = Type::i32; diff --git a/src/wasm.h b/src/wasm.h index e3ca5f47997..74c79bb2fdb 100644 --- a/src/wasm.h +++ b/src/wasm.h @@ -2412,29 +2412,28 @@ class DataSegment : public Named { class Memory : public Importable { public: - static const Address::address32_t kPageSize = 64 * 1024; static const Address::address64_t kUnlimitedSize = Address::address64_t(-1); - // In wasm32, the maximum memory size is limited by a 32-bit pointer: 4GB - static const Address::address32_t kMaxSize32 = - (uint64_t(4) * 1024 * 1024 * 1024) / kPageSize; - // in wasm64, the maximum number of pages - static const Address::address64_t kMaxSize64 = 1ull << (64 - 16); + + static const uint8_t kDefaultPageSizeLog2 = 16; + + static const Address::address32_t kDefaultPageSize = 1 + << kDefaultPageSizeLog2; + + static const Address::address32_t kDefaultMaxSize32 = + 1 << (32 - kDefaultPageSizeLog2); Address initial = 0; // sizes are in pages - Address max = kMaxSize32; + Address max = kDefaultMaxSize32; + + uint8_t pageSizeLog2 = kDefaultPageSizeLog2; bool shared = false; Type addressType = Type::i32; bool hasMax() { return max != kUnlimitedSize; } bool is64() { return addressType == Type::i64; } - void clear() { - name = ""; - initial = 0; - max = kMaxSize32; - shared = false; - addressType = Type::i32; - } + Address::address64_t maxSize32() const { return 1ull << (32 - pageSizeLog2); } + Address::address64_t maxSize64() const { return 1ull << (64 - pageSizeLog2); } }; class Global : public Importable { diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp index 08c6e19e298..8469cd3fdb6 100644 --- a/src/wasm/wasm-binary.cpp +++ b/src/wasm/wasm-binary.cpp @@ -111,11 +111,18 @@ int32_t WasmBinaryWriter::writeU32LEBPlaceholder() { return o.writeU32LEBPlaceholder(); } -void WasmBinaryWriter::writeResizableLimits( - Address initial, Address maximum, bool hasMaximum, bool shared, bool is64) { - uint32_t flags = (hasMaximum ? (uint32_t)BinaryConsts::HasMaximum : 0U) | - (shared ? (uint32_t)BinaryConsts::IsShared : 0U) | - (is64 ? (uint32_t)BinaryConsts::Is64 : 0U); +void WasmBinaryWriter::writeResizableLimits(Address initial, + Address maximum, + bool hasMaximum, + bool shared, + bool is64, + bool hasPageSize, + Address::address32_t pageSizeLog2) { + uint32_t flags = + (hasMaximum ? (uint32_t)BinaryConsts::HasMaximum : 0U) | + (shared ? (uint32_t)BinaryConsts::IsShared : 0U) | + (is64 ? (uint32_t)BinaryConsts::Is64 : 0U) | + (hasPageSize ? (uint32_t)BinaryConsts::HasCustomPageSize : 0U); o << U32LEB(flags); if (is64) { o << U64LEB(initial); @@ -128,6 +135,9 @@ void WasmBinaryWriter::writeResizableLimits( o << U32LEB(maximum); } } + if (hasPageSize) { + o << U32LEB(pageSizeLog2); + } } template int32_t WasmBinaryWriter::startSection(T code) { @@ -212,7 +222,9 @@ void WasmBinaryWriter::writeMemories() { memory->max, memory->hasMax(), memory->shared, - memory->is64()); + memory->is64(), + memory->pageSizeLog2 != Memory::kDefaultPageSizeLog2, + memory->pageSizeLog2); }); finishSection(start); } @@ -354,7 +366,9 @@ void WasmBinaryWriter::writeImports() { memory->max, memory->hasMax(), memory->shared, - memory->is64()); + memory->is64(), + memory->pageSizeLog2 != Memory::kDefaultPageSizeLog2, + memory->pageSizeLog2); }); ModuleUtils::iterImportedTables(*wasm, [&](Table* table) { writeImportHeader(table); @@ -364,7 +378,9 @@ void WasmBinaryWriter::writeImports() { table->max, table->hasMax(), /*shared=*/false, - table->is64()); + table->is64(), + /*hasPageSize=*/false, + /*PageSize*/ 1); }); finishSection(start); } @@ -773,7 +789,9 @@ void WasmBinaryWriter::writeTableDeclarations() { table->max, table->hasMax(), /*shared=*/false, - table->is64()); + table->is64(), + /*hasPageSize=*/false, + /*PageSize*/ 1); }); finishSection(start); } @@ -1395,6 +1413,9 @@ void WasmBinaryWriter::writeFeaturesSection() { return BinaryConsts::CustomSections::CallIndirectOverlongFeature; case FeatureSet::CustomDescriptors: return BinaryConsts::CustomSections::CustomDescriptorsFeature; + case FeatureSet::CustomPageSizes: + return BinaryConsts::CustomSections::CustomPageSizesFeature; + case FeatureSet::None: case FeatureSet::Default: case FeatureSet::All: @@ -2546,6 +2567,7 @@ void WasmBinaryReader::readMemories() { memory->max, memory->shared, memory->addressType, + memory->pageSizeLog2, Memory::kUnlimitedSize); wasm.addMemory(std::move(memory)); } @@ -2850,11 +2872,13 @@ void WasmBinaryReader::getResizableLimits(Address& initial, Address& max, bool& shared, Type& addressType, + uint8_t& pageSizeLog2, Address defaultIfNoMax) { auto flags = getU32LEB(); bool hasMax = (flags & BinaryConsts::HasMaximum) != 0; bool isShared = (flags & BinaryConsts::IsShared) != 0; bool is64 = (flags & BinaryConsts::Is64) != 0; + bool hasCustomPageSize = (flags & BinaryConsts::HasCustomPageSize) != 0; initial = is64 ? getU64LEB() : getU32LEB(); if (isShared && !hasMax) { throwError("shared memory must have max size"); @@ -2866,6 +2890,14 @@ void WasmBinaryReader::getResizableLimits(Address& initial, } else { max = defaultIfNoMax; } + if (hasCustomPageSize) { + auto readPageSizeLog2 = getU32LEB(); + if (readPageSizeLog2 != 0 && + readPageSizeLog2 != Memory::kDefaultPageSizeLog2) { + throwError("Memory page size is only allowed to be 1 or 64 KiB"); + } + pageSizeLog2 = (uint8_t)readPageSizeLog2; + } } void WasmBinaryReader::readImports() { @@ -2914,16 +2946,20 @@ void WasmBinaryReader::readImports() { table->module = module; table->base = base; table->type = getType(); - bool is_shared; + uint8_t page_size = 0xff; getResizableLimits(table->initial, table->max, is_shared, table->addressType, + page_size, Table::kUnlimitedSize); if (is_shared) { throwError("Tables may not be shared"); } + if (page_size != 0xff) { + throwError("Tables may not have a custom page size"); + } wasm.addTable(std::move(table)); break; } @@ -2941,6 +2977,7 @@ void WasmBinaryReader::readImports() { memory->max, memory->shared, memory->addressType, + memory->pageSizeLog2, Memory::kUnlimitedSize); wasm.addMemory(std::move(memory)); break; @@ -4876,14 +4913,19 @@ void WasmBinaryReader::readTableDeclarations() { auto table = Builder::makeTable(name, elemType); table->hasExplicitName = isExplicit; bool is_shared; + uint8_t pageSize = 0xff; getResizableLimits(table->initial, table->max, is_shared, table->addressType, + pageSize, Table::kUnlimitedSize); if (is_shared) { throwError("Tables may not be shared"); } + if (pageSize != 0xff) { + throwError("Tables may not specify a custom page size"); + } wasm.addTable(std::move(table)); } } @@ -5253,6 +5295,8 @@ void WasmBinaryReader::readFeatures(size_t payloadLen) { feature = FeatureSet::FP16; } else if (name == BinaryConsts::CustomSections::CustomDescriptorsFeature) { feature = FeatureSet::CustomDescriptors; + } else if (name == BinaryConsts::CustomSections::CustomPageSizesFeature) { + feature = FeatureSet::CustomPageSizes; } else { // Silently ignore unknown features (this may be and old binaryen running // on a new wasm). diff --git a/src/wasm/wasm-validator.cpp b/src/wasm/wasm-validator.cpp index d36ca80f38c..3f0ad078bda 100644 --- a/src/wasm/wasm-validator.cpp +++ b/src/wasm/wasm-validator.cpp @@ -4401,10 +4401,10 @@ static void validateMemories(Module& module, ValidationInfo& info) { "memory", "64-bit memories require memory64 [--enable-memory64]"); } else { - info.shouldBeTrue(memory->initial <= Memory::kMaxSize32, + info.shouldBeTrue(memory->initial <= memory->maxSize32(), "memory", "initial memory must be <= 4GB"); - info.shouldBeTrue(!memory->hasMax() || memory->max <= Memory::kMaxSize32, + info.shouldBeTrue(!memory->hasMax() || memory->max <= memory->maxSize32(), "memory", "max memory must be <= 4GB, or unlimited"); } @@ -4416,6 +4416,18 @@ static void validateMemories(Module& module, ValidationInfo& info) { "memory", "shared memory requires threads [--enable-threads]"); } + + if (memory->pageSizeLog2 != Memory::kDefaultPageSizeLog2) { + info.shouldBeTrue( + module.features.hasCustomPageSizes(), + "memory", + "custom page sizes not enabled [--enable-custom-page-sizes]"); + info.shouldBeEqual( + Address(memory->pageSizeLog2), + Address(0), + "memory", + "custom page size must be 1 Byte or 65536 Bytes (64KiB)"); + } } } diff --git a/src/wasm/wasm.cpp b/src/wasm/wasm.cpp index c95ee8a296f..2d30ddf1608 100644 --- a/src/wasm/wasm.cpp +++ b/src/wasm/wasm.cpp @@ -61,6 +61,7 @@ const char* FP16Feature = "fp16"; const char* BulkMemoryOptFeature = "bulk-memory-opt"; const char* CallIndirectOverlongFeature = "call-indirect-overlong"; const char* CustomDescriptorsFeature = "custom-descriptors"; +const char* CustomPageSizesFeature = "custom-page-sizes"; } // namespace BinaryConsts::CustomSections diff --git a/src/wasm2js.h b/src/wasm2js.h index ddc839e6f60..f0dfd542c17 100644 --- a/src/wasm2js.h +++ b/src/wasm2js.h @@ -477,13 +477,13 @@ Ref Wasm2JSBuilder::processWasm(Module* wasm, Name funcName) { } else { Ref theVar = ValueBuilder::makeVar(); asmFunc[3]->push_back(theVar); - ValueBuilder::appendToVar( - theVar, - BUFFER, - ValueBuilder::makeNew(ValueBuilder::makeCall( - ValueBuilder::makeName("ArrayBuffer"), - ValueBuilder::makeInt(Address::address32_t( - wasm->memories[0]->initial.addr * Memory::kPageSize))))); + ValueBuilder::appendToVar(theVar, + BUFFER, + ValueBuilder::makeNew(ValueBuilder::makeCall( + ValueBuilder::makeName("ArrayBuffer"), + ValueBuilder::makeInt(Address::address32_t( + wasm->memories[0]->initial.addr + << wasm->memories[0]->pageSizeLog2))))); } } @@ -2478,13 +2478,12 @@ Ref Wasm2JSBuilder::processExpression(Expression* curr, void Wasm2JSBuilder::addMemoryFuncs(Ref ast, Module* wasm) { Ref memorySizeFunc = ValueBuilder::makeFunction(WASM_MEMORY_SIZE); - memorySizeFunc[3]->push_back(ValueBuilder::makeReturn( - makeJsCoercion(ValueBuilder::makeBinary( - ValueBuilder::makeDot(ValueBuilder::makeName(BUFFER), - IString("byteLength")), - DIV, - ValueBuilder::makeInt(Memory::kPageSize)), - JsType::JS_INT))); + memorySizeFunc[3]->push_back( + ValueBuilder::makeReturn(ValueBuilder::makeBinary( + ValueBuilder::makeDot(ValueBuilder::makeName(BUFFER), + IString("byteLength")), + RSHIFT, + ValueBuilder::makeInt(wasm->memories[0]->pageSizeLog2)))); ast->push_back(memorySizeFunc); if (!wasm->memories.empty() && @@ -2529,9 +2528,10 @@ void Wasm2JSBuilder::addMemoryGrowFunc(Ref ast, Module* wasm) { LT, ValueBuilder::makeName(IString("newPages"))), IString("&&"), - ValueBuilder::makeBinary(ValueBuilder::makeName(IString("newPages")), - LT, - ValueBuilder::makeInt(Memory::kMaxSize32))), + ValueBuilder::makeBinary( + ValueBuilder::makeName(IString("newPages")), + LT, + ValueBuilder::makeNum(wasm->memories[0]->maxSize32()))), block, NULL)); @@ -2542,9 +2542,10 @@ void Wasm2JSBuilder::addMemoryGrowFunc(Ref ast, Module* wasm) { IString("newBuffer"), ValueBuilder::makeNew(ValueBuilder::makeCall( ARRAY_BUFFER, - ValueBuilder::makeCall(MATH_IMUL, - ValueBuilder::makeName(IString("newPages")), - ValueBuilder::makeInt(Memory::kPageSize))))); + ValueBuilder::makeBinary( + ValueBuilder::makeName(IString("newPages")), + LSHIFT, + ValueBuilder::makeInt(wasm->memories[0]->pageSizeLog2))))); Ref newHEAP8 = ValueBuilder::makeVar(); ValueBuilder::appendToBlock(block, newHEAP8); @@ -2736,7 +2737,8 @@ void Wasm2JSGlue::emitPostES6() { // can be used for conversions, so make sure there's at least one page. if (!wasm.memories.empty() && wasm.memories[0]->imported()) { out << "var mem" << moduleName.str << " = new ArrayBuffer(" - << wasm.memories[0]->initial.addr * Memory::kPageSize << ");\n"; + << (wasm.memories[0]->initial.addr << wasm.memories[0]->pageSizeLog2) + << ");\n"; } // Actually invoke the `asmFunc` generated function, passing in all global diff --git a/test/binaryen.js/kitchen-sink.js b/test/binaryen.js/kitchen-sink.js index 556749059b7..b51b8784f19 100644 --- a/test/binaryen.js/kitchen-sink.js +++ b/test/binaryen.js/kitchen-sink.js @@ -100,6 +100,7 @@ function test_features() { console.log("Features.ExtendedConst: " + binaryen.Features.ExtendedConst); console.log("Features.Strings: " + binaryen.Features.Strings); console.log("Features.MultiMemory: " + binaryen.Features.MultiMemory); + console.log("Features.CustomPageSizes: " + binaryen.Features.CustomPageSizes); console.log("Features.All: " + binaryen.Features.All); } diff --git a/test/binaryen.js/kitchen-sink.js.txt b/test/binaryen.js/kitchen-sink.js.txt index 03714168237..0715e061ed1 100644 --- a/test/binaryen.js/kitchen-sink.js.txt +++ b/test/binaryen.js/kitchen-sink.js.txt @@ -33,7 +33,8 @@ Features.RelaxedSIMD: 4096 Features.ExtendedConst: 8192 Features.Strings: 16384 Features.MultiMemory: 32768 -Features.All: 4194303 +Features.CustomPageSizes: 4194304 +Features.All: 8388607 InvalidId: 0 BlockId: 1 IfId: 2 diff --git a/test/example/c-api-kitchen-sink.c b/test/example/c-api-kitchen-sink.c index 85937929d7e..467941e4aa8 100644 --- a/test/example/c-api-kitchen-sink.c +++ b/test/example/c-api-kitchen-sink.c @@ -374,6 +374,8 @@ void test_features() { printf("BinaryenFeatureRelaxedSIMD: %d\n", BinaryenFeatureRelaxedSIMD()); printf("BinaryenFeatureExtendedConst: %d\n", BinaryenFeatureExtendedConst()); printf("BinaryenFeatureStrings: %d\n", BinaryenFeatureStrings()); + printf("BinaryenFeatureCustomPageSizes: %d\n", + BinaryenFeatureCustomPageSizes()); printf("BinaryenFeatureAll: %d\n", BinaryenFeatureAll()); } diff --git a/test/example/c-api-kitchen-sink.txt b/test/example/c-api-kitchen-sink.txt index 1b798654d1a..8bdfdb1a476 100644 --- a/test/example/c-api-kitchen-sink.txt +++ b/test/example/c-api-kitchen-sink.txt @@ -47,7 +47,8 @@ BinaryenFeatureMemory64: 2048 BinaryenFeatureRelaxedSIMD: 4096 BinaryenFeatureExtendedConst: 8192 BinaryenFeatureStrings: 16384 -BinaryenFeatureAll: 4194303 +BinaryenFeatureCustomPageSizes: 4194304 +BinaryenFeatureAll: 8388607 (f32.neg (f32.const -33.61199951171875) ) diff --git a/test/lit/passes/memory-copy-fill-lowering.wast b/test/lit/passes/memory-copy-fill-lowering.wast index 990c2fa30de..adc19e7e732 100644 --- a/test/lit/passes/memory-copy-fill-lowering.wast +++ b/test/lit/passes/memory-copy-fill-lowering.wast @@ -37,9 +37,9 @@ ;; CHECK-NEXT: (local $step i32) ;; CHECK-NEXT: (local $i i32) ;; CHECK-NEXT: (local.set $end -;; CHECK-NEXT: (i32.mul +;; CHECK-NEXT: (i32.shl ;; CHECK-NEXT: (memory.size) -;; CHECK-NEXT: (i32.const 65536) +;; CHECK-NEXT: (i32.const 16) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (if @@ -135,9 +135,9 @@ ;; CHECK-NEXT: (local.get $dst) ;; CHECK-NEXT: (local.get $size) ;; CHECK-NEXT: ) -;; CHECK-NEXT: (i32.mul +;; CHECK-NEXT: (i32.shl ;; CHECK-NEXT: (memory.size) -;; CHECK-NEXT: (i32.const 65536) +;; CHECK-NEXT: (i32.const 16) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (then diff --git a/test/lit/passes/multi-memory-lowering.wast b/test/lit/passes/multi-memory-lowering.wast index 9b7f5363503..b46b97ee721 100644 --- a/test/lit/passes/multi-memory-lowering.wast +++ b/test/lit/passes/multi-memory-lowering.wast @@ -839,9 +839,9 @@ ;; CHECK: (func $memory1_size (result i32) ;; CHECK-NEXT: (return -;; CHECK-NEXT: (i32.div_u +;; CHECK-NEXT: (i32.shr_u ;; CHECK-NEXT: (global.get $memory2_byte_offset) -;; CHECK-NEXT: (i32.const 65536) +;; CHECK-NEXT: (i32.const 16) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) @@ -849,13 +849,13 @@ ;; CHECK: (func $memory2_size (result i32) ;; CHECK-NEXT: (return ;; CHECK-NEXT: (i32.sub -;; CHECK-NEXT: (i32.div_u +;; CHECK-NEXT: (i32.shr_u ;; CHECK-NEXT: (global.get $memory3_byte_offset) -;; CHECK-NEXT: (i32.const 65536) +;; CHECK-NEXT: (i32.const 16) ;; CHECK-NEXT: ) -;; CHECK-NEXT: (i32.div_u +;; CHECK-NEXT: (i32.shr_u ;; CHECK-NEXT: (global.get $memory2_byte_offset) -;; CHECK-NEXT: (i32.const 65536) +;; CHECK-NEXT: (i32.const 16) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) @@ -865,9 +865,9 @@ ;; CHECK-NEXT: (return ;; CHECK-NEXT: (i32.sub ;; CHECK-NEXT: (memory.size) -;; CHECK-NEXT: (i32.div_u +;; CHECK-NEXT: (i32.shr_u ;; CHECK-NEXT: (global.get $memory3_byte_offset) -;; CHECK-NEXT: (i32.const 65536) +;; CHECK-NEXT: (i32.const 16) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) @@ -898,16 +898,16 @@ ;; CHECK-NEXT: (memory.copy ;; CHECK-NEXT: (i32.add ;; CHECK-NEXT: (global.get $memory2_byte_offset) -;; CHECK-NEXT: (i32.mul +;; CHECK-NEXT: (i32.shl ;; CHECK-NEXT: (local.get $page_delta) -;; CHECK-NEXT: (i32.const 65536) +;; CHECK-NEXT: (i32.const 16) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (global.get $memory2_byte_offset) ;; CHECK-NEXT: (i32.sub -;; CHECK-NEXT: (i32.mul +;; CHECK-NEXT: (i32.shl ;; CHECK-NEXT: (local.get $memory_size) -;; CHECK-NEXT: (i32.const 65536) +;; CHECK-NEXT: (i32.const 16) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (global.get $memory2_byte_offset) ;; CHECK-NEXT: ) @@ -915,18 +915,18 @@ ;; CHECK-NEXT: (global.set $memory2_byte_offset ;; CHECK-NEXT: (i32.add ;; CHECK-NEXT: (global.get $memory2_byte_offset) -;; CHECK-NEXT: (i32.mul +;; CHECK-NEXT: (i32.shl ;; CHECK-NEXT: (local.get $page_delta) -;; CHECK-NEXT: (i32.const 65536) +;; CHECK-NEXT: (i32.const 16) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (global.set $memory3_byte_offset ;; CHECK-NEXT: (i32.add ;; CHECK-NEXT: (global.get $memory3_byte_offset) -;; CHECK-NEXT: (i32.mul +;; CHECK-NEXT: (i32.shl ;; CHECK-NEXT: (local.get $page_delta) -;; CHECK-NEXT: (i32.const 65536) +;; CHECK-NEXT: (i32.const 16) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) @@ -958,16 +958,16 @@ ;; CHECK-NEXT: (memory.copy ;; CHECK-NEXT: (i32.add ;; CHECK-NEXT: (global.get $memory3_byte_offset) -;; CHECK-NEXT: (i32.mul +;; CHECK-NEXT: (i32.shl ;; CHECK-NEXT: (local.get $page_delta) -;; CHECK-NEXT: (i32.const 65536) +;; CHECK-NEXT: (i32.const 16) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (global.get $memory3_byte_offset) ;; CHECK-NEXT: (i32.sub -;; CHECK-NEXT: (i32.mul +;; CHECK-NEXT: (i32.shl ;; CHECK-NEXT: (local.get $memory_size) -;; CHECK-NEXT: (i32.const 65536) +;; CHECK-NEXT: (i32.const 16) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (global.get $memory3_byte_offset) ;; CHECK-NEXT: ) @@ -975,9 +975,9 @@ ;; CHECK-NEXT: (global.set $memory3_byte_offset ;; CHECK-NEXT: (i32.add ;; CHECK-NEXT: (global.get $memory3_byte_offset) -;; CHECK-NEXT: (i32.mul +;; CHECK-NEXT: (i32.shl ;; CHECK-NEXT: (local.get $page_delta) -;; CHECK-NEXT: (i32.const 65536) +;; CHECK-NEXT: (i32.const 16) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) @@ -1007,9 +1007,9 @@ ;; BOUNDS: (func $memory1_size (result i32) ;; BOUNDS-NEXT: (return -;; BOUNDS-NEXT: (i32.div_u +;; BOUNDS-NEXT: (i32.shr_u ;; BOUNDS-NEXT: (global.get $memory2_byte_offset) -;; BOUNDS-NEXT: (i32.const 65536) +;; BOUNDS-NEXT: (i32.const 16) ;; BOUNDS-NEXT: ) ;; BOUNDS-NEXT: ) ;; BOUNDS-NEXT: ) @@ -1017,13 +1017,13 @@ ;; BOUNDS: (func $memory2_size (result i32) ;; BOUNDS-NEXT: (return ;; BOUNDS-NEXT: (i32.sub -;; BOUNDS-NEXT: (i32.div_u +;; BOUNDS-NEXT: (i32.shr_u ;; BOUNDS-NEXT: (global.get $memory3_byte_offset) -;; BOUNDS-NEXT: (i32.const 65536) +;; BOUNDS-NEXT: (i32.const 16) ;; BOUNDS-NEXT: ) -;; BOUNDS-NEXT: (i32.div_u +;; BOUNDS-NEXT: (i32.shr_u ;; BOUNDS-NEXT: (global.get $memory2_byte_offset) -;; BOUNDS-NEXT: (i32.const 65536) +;; BOUNDS-NEXT: (i32.const 16) ;; BOUNDS-NEXT: ) ;; BOUNDS-NEXT: ) ;; BOUNDS-NEXT: ) @@ -1033,9 +1033,9 @@ ;; BOUNDS-NEXT: (return ;; BOUNDS-NEXT: (i32.sub ;; BOUNDS-NEXT: (memory.size) -;; BOUNDS-NEXT: (i32.div_u +;; BOUNDS-NEXT: (i32.shr_u ;; BOUNDS-NEXT: (global.get $memory3_byte_offset) -;; BOUNDS-NEXT: (i32.const 65536) +;; BOUNDS-NEXT: (i32.const 16) ;; BOUNDS-NEXT: ) ;; BOUNDS-NEXT: ) ;; BOUNDS-NEXT: ) @@ -1066,16 +1066,16 @@ ;; BOUNDS-NEXT: (memory.copy ;; BOUNDS-NEXT: (i32.add ;; BOUNDS-NEXT: (global.get $memory2_byte_offset) -;; BOUNDS-NEXT: (i32.mul +;; BOUNDS-NEXT: (i32.shl ;; BOUNDS-NEXT: (local.get $page_delta) -;; BOUNDS-NEXT: (i32.const 65536) +;; BOUNDS-NEXT: (i32.const 16) ;; BOUNDS-NEXT: ) ;; BOUNDS-NEXT: ) ;; BOUNDS-NEXT: (global.get $memory2_byte_offset) ;; BOUNDS-NEXT: (i32.sub -;; BOUNDS-NEXT: (i32.mul +;; BOUNDS-NEXT: (i32.shl ;; BOUNDS-NEXT: (local.get $memory_size) -;; BOUNDS-NEXT: (i32.const 65536) +;; BOUNDS-NEXT: (i32.const 16) ;; BOUNDS-NEXT: ) ;; BOUNDS-NEXT: (global.get $memory2_byte_offset) ;; BOUNDS-NEXT: ) @@ -1083,18 +1083,18 @@ ;; BOUNDS-NEXT: (global.set $memory2_byte_offset ;; BOUNDS-NEXT: (i32.add ;; BOUNDS-NEXT: (global.get $memory2_byte_offset) -;; BOUNDS-NEXT: (i32.mul +;; BOUNDS-NEXT: (i32.shl ;; BOUNDS-NEXT: (local.get $page_delta) -;; BOUNDS-NEXT: (i32.const 65536) +;; BOUNDS-NEXT: (i32.const 16) ;; BOUNDS-NEXT: ) ;; BOUNDS-NEXT: ) ;; BOUNDS-NEXT: ) ;; BOUNDS-NEXT: (global.set $memory3_byte_offset ;; BOUNDS-NEXT: (i32.add ;; BOUNDS-NEXT: (global.get $memory3_byte_offset) -;; BOUNDS-NEXT: (i32.mul +;; BOUNDS-NEXT: (i32.shl ;; BOUNDS-NEXT: (local.get $page_delta) -;; BOUNDS-NEXT: (i32.const 65536) +;; BOUNDS-NEXT: (i32.const 16) ;; BOUNDS-NEXT: ) ;; BOUNDS-NEXT: ) ;; BOUNDS-NEXT: ) @@ -1126,16 +1126,16 @@ ;; BOUNDS-NEXT: (memory.copy ;; BOUNDS-NEXT: (i32.add ;; BOUNDS-NEXT: (global.get $memory3_byte_offset) -;; BOUNDS-NEXT: (i32.mul +;; BOUNDS-NEXT: (i32.shl ;; BOUNDS-NEXT: (local.get $page_delta) -;; BOUNDS-NEXT: (i32.const 65536) +;; BOUNDS-NEXT: (i32.const 16) ;; BOUNDS-NEXT: ) ;; BOUNDS-NEXT: ) ;; BOUNDS-NEXT: (global.get $memory3_byte_offset) ;; BOUNDS-NEXT: (i32.sub -;; BOUNDS-NEXT: (i32.mul +;; BOUNDS-NEXT: (i32.shl ;; BOUNDS-NEXT: (local.get $memory_size) -;; BOUNDS-NEXT: (i32.const 65536) +;; BOUNDS-NEXT: (i32.const 16) ;; BOUNDS-NEXT: ) ;; BOUNDS-NEXT: (global.get $memory3_byte_offset) ;; BOUNDS-NEXT: ) @@ -1143,9 +1143,9 @@ ;; BOUNDS-NEXT: (global.set $memory3_byte_offset ;; BOUNDS-NEXT: (i32.add ;; BOUNDS-NEXT: (global.get $memory3_byte_offset) -;; BOUNDS-NEXT: (i32.mul +;; BOUNDS-NEXT: (i32.shl ;; BOUNDS-NEXT: (local.get $page_delta) -;; BOUNDS-NEXT: (i32.const 65536) +;; BOUNDS-NEXT: (i32.const 16) ;; BOUNDS-NEXT: ) ;; BOUNDS-NEXT: ) ;; BOUNDS-NEXT: ) diff --git a/test/lit/wasm-split/multi-memory-lowering-export.wast b/test/lit/wasm-split/multi-memory-lowering-export.wast index 152aaf72310..4411ba78e39 100644 --- a/test/lit/wasm-split/multi-memory-lowering-export.wast +++ b/test/lit/wasm-split/multi-memory-lowering-export.wast @@ -20,9 +20,9 @@ ;; CHECK: (func $memory1_size (type $0) (result i32) ;; CHECK-NEXT: (return -;; CHECK-NEXT: (i32.div_u +;; CHECK-NEXT: (i32.shr_u ;; CHECK-NEXT: (global.get $memory2_byte_offset) -;; CHECK-NEXT: (i32.const 65536) +;; CHECK-NEXT: (i32.const 16) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) @@ -31,9 +31,9 @@ ;; CHECK-NEXT: (return ;; CHECK-NEXT: (i32.sub ;; CHECK-NEXT: (memory.size) -;; CHECK-NEXT: (i32.div_u +;; CHECK-NEXT: (i32.shr_u ;; CHECK-NEXT: (global.get $memory2_byte_offset) -;; CHECK-NEXT: (i32.const 65536) +;; CHECK-NEXT: (i32.const 16) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) @@ -64,16 +64,16 @@ ;; CHECK-NEXT: (memory.copy ;; CHECK-NEXT: (i32.add ;; CHECK-NEXT: (global.get $memory2_byte_offset) -;; CHECK-NEXT: (i32.mul +;; CHECK-NEXT: (i32.shl ;; CHECK-NEXT: (local.get $page_delta) -;; CHECK-NEXT: (i32.const 65536) +;; CHECK-NEXT: (i32.const 16) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (global.get $memory2_byte_offset) ;; CHECK-NEXT: (i32.sub -;; CHECK-NEXT: (i32.mul +;; CHECK-NEXT: (i32.shl ;; CHECK-NEXT: (local.get $memory_size) -;; CHECK-NEXT: (i32.const 65536) +;; CHECK-NEXT: (i32.const 16) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (global.get $memory2_byte_offset) ;; CHECK-NEXT: ) @@ -81,9 +81,9 @@ ;; CHECK-NEXT: (global.set $memory2_byte_offset ;; CHECK-NEXT: (i32.add ;; CHECK-NEXT: (global.get $memory2_byte_offset) -;; CHECK-NEXT: (i32.mul +;; CHECK-NEXT: (i32.shl ;; CHECK-NEXT: (local.get $page_delta) -;; CHECK-NEXT: (i32.const 65536) +;; CHECK-NEXT: (i32.const 16) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) diff --git a/test/lit/wasm-split/multi-memory-lowering-import.wast b/test/lit/wasm-split/multi-memory-lowering-import.wast index 37ccfa16cb1..f8d505d89fc 100644 --- a/test/lit/wasm-split/multi-memory-lowering-import.wast +++ b/test/lit/wasm-split/multi-memory-lowering-import.wast @@ -17,9 +17,9 @@ ;; CHECK: (func $memory1_size (type $0) (result i32) ;; CHECK-NEXT: (return -;; CHECK-NEXT: (i32.div_u +;; CHECK-NEXT: (i32.shr_u ;; CHECK-NEXT: (global.get $memory2_byte_offset) -;; CHECK-NEXT: (i32.const 65536) +;; CHECK-NEXT: (i32.const 16) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) @@ -28,9 +28,9 @@ ;; CHECK-NEXT: (return ;; CHECK-NEXT: (i32.sub ;; CHECK-NEXT: (memory.size) -;; CHECK-NEXT: (i32.div_u +;; CHECK-NEXT: (i32.shr_u ;; CHECK-NEXT: (global.get $memory2_byte_offset) -;; CHECK-NEXT: (i32.const 65536) +;; CHECK-NEXT: (i32.const 16) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) @@ -61,16 +61,16 @@ ;; CHECK-NEXT: (memory.copy ;; CHECK-NEXT: (i32.add ;; CHECK-NEXT: (global.get $memory2_byte_offset) -;; CHECK-NEXT: (i32.mul +;; CHECK-NEXT: (i32.shl ;; CHECK-NEXT: (local.get $page_delta) -;; CHECK-NEXT: (i32.const 65536) +;; CHECK-NEXT: (i32.const 16) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (global.get $memory2_byte_offset) ;; CHECK-NEXT: (i32.sub -;; CHECK-NEXT: (i32.mul +;; CHECK-NEXT: (i32.shl ;; CHECK-NEXT: (local.get $memory_size) -;; CHECK-NEXT: (i32.const 65536) +;; CHECK-NEXT: (i32.const 16) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (global.get $memory2_byte_offset) ;; CHECK-NEXT: ) @@ -78,9 +78,9 @@ ;; CHECK-NEXT: (global.set $memory2_byte_offset ;; CHECK-NEXT: (i32.add ;; CHECK-NEXT: (global.get $memory2_byte_offset) -;; CHECK-NEXT: (i32.mul +;; CHECK-NEXT: (i32.shl ;; CHECK-NEXT: (local.get $page_delta) -;; CHECK-NEXT: (i32.const 65536) +;; CHECK-NEXT: (i32.const 16) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) diff --git a/test/passes/strip-target-features_roundtrip_print-features_all-features.txt b/test/passes/strip-target-features_roundtrip_print-features_all-features.txt index 34976434e9d..9d0ebbaed4f 100644 --- a/test/passes/strip-target-features_roundtrip_print-features_all-features.txt +++ b/test/passes/strip-target-features_roundtrip_print-features_all-features.txt @@ -20,6 +20,7 @@ --enable-bulk-memory-opt --enable-call-indirect-overlong --enable-custom-descriptors +--enable-custom-page-sizes (module (type $0 (func (result v128 externref))) (func $foo (type $0) (result v128 externref) diff --git a/test/spec/custom-page-sizes-invalid.wast b/test/spec/custom-page-sizes-invalid.wast new file mode 100644 index 00000000000..5fb121a0aaa --- /dev/null +++ b/test/spec/custom-page-sizes-invalid.wast @@ -0,0 +1,115 @@ +;; Page size that is not a power of two. +(assert_malformed + (module quote "(memory 0 (pagesize 3))") + "invalid custom page size" +) + +(assert_malformed + (module quote "(memory 0 (pagesize 0))") + "invalid custom page size" +) + +;; Power-of-two page sizes that are not 1 or 64KiB. +(assert_invalid + (module (memory 0 (pagesize 2))) + "invalid custom page size" +) +(assert_invalid + (module (memory 0 (pagesize 4))) + "invalid custom page size" +) +(assert_invalid + (module (memory 0 (pagesize 8))) + "invalid custom page size" +) +(assert_invalid + (module (memory 0 (pagesize 16))) + "invalid custom page size" +) +(assert_invalid + (module (memory 0 (pagesize 32))) + "invalid custom page size" +) +(assert_invalid + (module (memory 0 (pagesize 64))) + "invalid custom page size" +) +(assert_invalid + (module (memory 0 (pagesize 128))) + "invalid custom page size" +) +(assert_invalid + (module (memory 0 (pagesize 256))) + "invalid custom page size" +) +(assert_invalid + (module (memory 0 (pagesize 512))) + "invalid custom page size" +) +(assert_invalid + (module (memory 0 (pagesize 1024))) + "invalid custom page size" +) +(assert_invalid + (module (memory 0 (pagesize 2048))) + "invalid custom page size" +) +(assert_invalid + (module (memory 0 (pagesize 4096))) + "invalid custom page size" +) +(assert_invalid + (module (memory 0 (pagesize 8192))) + "invalid custom page size" +) +(assert_invalid + (module (memory 0 (pagesize 16384))) + "invalid custom page size" +) +(assert_invalid + (module (memory 0 (pagesize 32768))) + "invalid custom page size" +) + +;; Power-of-two page size that is larger than 64KiB. +(assert_invalid + (module (memory 0 (pagesize 0x20000))) + "invalid custom page size" +) + +;; Power of two page size that cannot fit in a u64 to exercise checks against +;; shift overflow. +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\05\04\01" ;; Memory section + + ;; memory 0 + "\08" ;; flags w/ custom page size + "\00" ;; minimum = 0 + "\41" ;; pagesize = 2**65 + ) + "invalid custom page size" +) + +;; Importing a memory with the wrong page size. + +(module $m + (memory (export "small-pages-memory") 0 (pagesize 1)) + (memory (export "large-pages-memory") 0 (pagesize 65536)) +) +(register "m" $m) + +;; (assert_unlinkable +;; (module +;; (memory (import "m" "small-pages-memory") 0 (pagesize 65536)) +;; ) +;; "memory types incompatible" +;; ) + +;; (assert_unlinkable +;; (module +;; (memory (import "m" "large-pages-memory") 0 (pagesize 1)) +;; ) +;; "memory types incompatible" +;; ) diff --git a/test/spec/custom-page-sizes.wast b/test/spec/custom-page-sizes.wast new file mode 100644 index 00000000000..332051daacb --- /dev/null +++ b/test/spec/custom-page-sizes.wast @@ -0,0 +1,138 @@ +;; Check all the valid custom page sizes. +(module (memory 1 (pagesize 1))) +(module (memory 1 (pagesize 65536))) + +;; Check them all again with maximums specified. +(module (memory 1 2 (pagesize 1))) +(module (memory 1 2 (pagesize 65536))) + +;; Check the behavior of memories with page size 1. +(module + (memory 0 (pagesize 1)) + (func (export "size") (result i32) + memory.size + ) + (func (export "grow") (param i32) (result i32) + (memory.grow (local.get 0)) + ) + (func (export "load") (param i32) (result i32) + (i32.load8_u (local.get 0)) + ) + (func (export "store") (param i32 i32) + (i32.store8 (local.get 0) (local.get 1)) + ) +) + +(assert_return (invoke "size") (i32.const 0)) +(assert_trap (invoke "load" (i32.const 0)) "out of bounds memory access") + +(assert_return (invoke "grow" (i32.const 65536)) (i32.const 0)) +(assert_return (invoke "size") (i32.const 65536)) +(assert_return (invoke "load" (i32.const 65535)) (i32.const 0)) +(assert_return (invoke "store" (i32.const 65535) (i32.const 1))) +(assert_return (invoke "load" (i32.const 65535)) (i32.const 1)) +(assert_trap (invoke "load" (i32.const 65536)) "out of bounds memory access") + +(assert_return (invoke "grow" (i32.const 65536)) (i32.const 65536)) +(assert_return (invoke "size") (i32.const 131072)) +(assert_return (invoke "load" (i32.const 131071)) (i32.const 0)) +(assert_return (invoke "store" (i32.const 131071) (i32.const 1))) +(assert_return (invoke "load" (i32.const 131071)) (i32.const 1)) +(assert_trap (invoke "load" (i32.const 131072)) "out of bounds memory access") + +;; Although smaller page sizes let us get to memories larger than 2**16 pages, +;; we can't do that with the default page size, even if we explicitly state it +;; as a custom page size. +(module + (memory 0 (pagesize 65536)) + (func (export "size") (result i32) + memory.size + ) + (func (export "grow") (param i32) (result i32) + (memory.grow (local.get 0)) + ) +) +(assert_return (invoke "size") (i32.const 0)) +(assert_return (invoke "grow" (i32.const 65537)) (i32.const -1)) +(assert_return (invoke "size") (i32.const 0)) + +;; Can copy between memories of different page sizes. +(module + (memory $small 10 (pagesize 1)) + (memory $large 1 (pagesize 65536)) + + (data (memory $small) (i32.const 0) "\11\22\33\44") + (data (memory $large) (i32.const 0) "\55\66\77\88") + + (func (export "copy-small-to-large") (param i32 i32 i32) + (memory.copy $large $small (local.get 0) (local.get 1) (local.get 2)) + ) + + (func (export "copy-large-to-small") (param i32 i32 i32) + (memory.copy $small $large (local.get 0) (local.get 1) (local.get 2)) + ) + + (func (export "load8-small") (param i32) (result i32) + (i32.load8_u $small (local.get 0)) + ) + + (func (export "load8-large") (param i32) (result i32) + (i32.load8_u $large (local.get 0)) + ) +) + +(assert_return (invoke "copy-small-to-large" (i32.const 6) (i32.const 0) (i32.const 2))) +(assert_return (invoke "load8-large" (i32.const 6)) (i32.const 0x11)) +(assert_return (invoke "load8-large" (i32.const 7)) (i32.const 0x22)) + +(assert_return (invoke "copy-large-to-small" (i32.const 4) (i32.const 1) (i32.const 3))) +(assert_return (invoke "load8-small" (i32.const 4)) (i32.const 0x66)) +(assert_return (invoke "load8-small" (i32.const 5)) (i32.const 0x77)) +(assert_return (invoke "load8-small" (i32.const 6)) (i32.const 0x88)) + +;; Can link together modules that export and import memories with custom page +;; sizes. + +(module $m + (memory (export "small-pages-memory") 0 (pagesize 1)) + (memory (export "large-pages-memory") 0 (pagesize 65536)) +) +(register "m" $m) + +(module + (memory (import "m" "small-pages-memory") 0 (pagesize 1)) +) + +(module + (memory (import "m" "large-pages-memory") 0 (pagesize 65536)) +) + +;; Inline data segments + +;; pagesize 0 +(assert_malformed (module quote "(memory (pagesize 0) (data))") "invalid custom page size") + +;; pagesize 1 +(module + (memory (pagesize 1) (data "xyz")) + (func (export "size") (result i32) + memory.size) + (func (export "grow") (param i32) (result i32) + (memory.grow (local.get 0))) + (func (export "load") (param i32) (result i32) + (i32.load8_u (local.get 0)))) + +(assert_return (invoke "size") (i32.const 3)) +(assert_return (invoke "load" (i32.const 0)) (i32.const 120)) +(assert_return (invoke "load" (i32.const 1)) (i32.const 121)) +(assert_return (invoke "load" (i32.const 2)) (i32.const 122)) +(assert_trap (invoke "load" (i32.const 3)) "out of bounds memory access") +(assert_return (invoke "grow" (i32.const 1)) (i32.const -1)) + +;; pagesize 65536 +(module + (memory (pagesize 65536) (data "xyz")) + (func (export "size") (result i32) + memory.size)) + +(assert_return (invoke "size") (i32.const 1)) diff --git a/test/spec/memory_max.wast b/test/spec/memory_max.wast new file mode 100644 index 00000000000..dfc89b6696e --- /dev/null +++ b/test/spec/memory_max.wast @@ -0,0 +1,37 @@ +;; Maximum memory sizes. +;; +;; These modules are valid, but instantiating them is unnecessary +;; and would only allocate very large memories and slow down running +;; the spec tests. Therefore, add a missing import so that it cannot +;; be instantiated and use `assert_unlinkable`. This approach +;; enforces that the module itself is still valid, but that its +;; instantiation fails early (hopefully before any memories are +;; actually allocated). + +;; i32 (pagesize 1) +(assert_unlinkable + (module + (import "test" "unknown" (func)) + (memory 0xFFFF_FFFF (pagesize 1))) + "unknown import") + +;; i32 (default pagesize) +(assert_unlinkable + (module + (import "test" "unknown" (func)) + (memory 65536 (pagesize 65536))) + "unknown import") + +;; Memory size just over the maximum. + +;; i32 (pagesize 1) +(assert_invalid + (module + (memory 0x1_0000_0000 (pagesize 1))) + "memory size must be at most") + +;; i32 (default pagesize) +(assert_invalid + (module + (memory 65537 (pagesize 65536))) + "memory size must be at most") diff --git a/test/spec/memory_max_i64.wast b/test/spec/memory_max_i64.wast new file mode 100644 index 00000000000..607642decf8 --- /dev/null +++ b/test/spec/memory_max_i64.wast @@ -0,0 +1,39 @@ +;; Maximum memory sizes. +;; +;; These modules are valid, but instantiating them is unnecessary +;; and would only allocate very large memories and slow down running +;; the spec tests. Therefore, add a missing import so that it cannot +;; be instantiated and use `assert_unlinkable`. This approach +;; enforces that the module itself is still valid, but that its +;; instantiation fails early (hopefully before any memories are +;; actually allocated). + +;; i64 (pagesize 1) +(assert_unlinkable + (module + (import "test" "import" (func)) + (memory i64 0xFFFF_FFFF_FFFF_FFFF (pagesize 1))) + "unknown import") + +;; i64 (default pagesize) +(assert_unlinkable + (module + (import "test" "unknown" (func)) + (memory i64 0x1_0000_0000_0000 (pagesize 65536))) + "unknown import") + +;; Memory size just over the maximum. +;; +;; These are malformed (for pagesize 1) +;; or invalid (for other pagesizes). + +;; i64 (pagesize 1) +(assert_malformed + (module quote "(memory i64 0x1_0000_0000_0000_0000 (pagesize 1))") + "constant out of range") + +;; i64 (default pagesize) +(assert_invalid + (module + (memory i64 0x1_0000_0000_0001 (pagesize 65536))) + "memory size must be at most") diff --git a/test/unit/test_features.py b/test/unit/test_features.py index 2647ff84717..95867d3a86d 100644 --- a/test/unit/test_features.py +++ b/test/unit/test_features.py @@ -453,4 +453,5 @@ def test_emit_all_features(self): '--enable-bulk-memory-opt', '--enable-call-indirect-overlong', '--enable-custom-descriptors', + '--enable-custom-page-sizes', ], p2.stdout.splitlines()) diff --git a/test/wasm2js/atomics_32.2asm.js b/test/wasm2js/atomics_32.2asm.js index a97bf17ed26..03ac931053b 100644 --- a/test/wasm2js/atomics_32.2asm.js +++ b/test/wasm2js/atomics_32.2asm.js @@ -143,7 +143,7 @@ function asmFunc(imports) { bufferView = HEAPU8; function __wasm_memory_size() { - return buffer.byteLength / 65536 | 0; + return buffer.byteLength >> 16; } return { diff --git a/test/wasm2js/atomics_32.2asm.js.opt b/test/wasm2js/atomics_32.2asm.js.opt index deb8ce56186..04da6fac687 100644 --- a/test/wasm2js/atomics_32.2asm.js.opt +++ b/test/wasm2js/atomics_32.2asm.js.opt @@ -139,7 +139,7 @@ function asmFunc(imports) { bufferView = HEAPU8; function __wasm_memory_size() { - return buffer.byteLength / 65536 | 0; + return buffer.byteLength >> 16; } return { diff --git a/test/wasm2js/bulk-memory.2asm.js b/test/wasm2js/bulk-memory.2asm.js index 4f25b9bd8ba..b1a25080bf6 100644 --- a/test/wasm2js/bulk-memory.2asm.js +++ b/test/wasm2js/bulk-memory.2asm.js @@ -61,7 +61,7 @@ function asmFunc(imports) { bufferView = HEAPU8; function __wasm_memory_size() { - return buffer.byteLength / 65536 | 0; + return buffer.byteLength >> 16; } function __wasm_memory_grow(pagesToAdd) { @@ -69,7 +69,7 @@ function asmFunc(imports) { var oldPages = __wasm_memory_size() | 0; var newPages = oldPages + pagesToAdd | 0; if ((oldPages < newPages) && (newPages < 65536)) { - var newBuffer = new ArrayBuffer(Math_imul(newPages, 65536)); + var newBuffer = new ArrayBuffer(newPages << 16); var newHEAP8 = new Int8Array(newBuffer); newHEAP8.set(HEAP8); HEAP8 = new Int8Array(newBuffer); @@ -162,7 +162,7 @@ function asmFunc(imports) { bufferView = HEAPU8; initActiveSegments(imports); function __wasm_memory_size() { - return buffer.byteLength / 65536 | 0; + return buffer.byteLength >> 16; } return { @@ -239,7 +239,7 @@ function asmFunc(imports) { bufferView = HEAPU8; function __wasm_memory_size() { - return buffer.byteLength / 65536 | 0; + return buffer.byteLength >> 16; } function __wasm_memory_grow(pagesToAdd) { @@ -247,7 +247,7 @@ function asmFunc(imports) { var oldPages = __wasm_memory_size() | 0; var newPages = oldPages + pagesToAdd | 0; if ((oldPages < newPages) && (newPages < 65536)) { - var newBuffer = new ArrayBuffer(Math_imul(newPages, 65536)); + var newBuffer = new ArrayBuffer(newPages << 16); var newHEAP8 = new Int8Array(newBuffer); newHEAP8.set(HEAP8); HEAP8 = new Int8Array(newBuffer); @@ -351,7 +351,7 @@ function asmFunc(imports) { bufferView = HEAPU8; initActiveSegments(imports); function __wasm_memory_size() { - return buffer.byteLength / 65536 | 0; + return buffer.byteLength >> 16; } function __wasm_memory_grow(pagesToAdd) { @@ -359,7 +359,7 @@ function asmFunc(imports) { var oldPages = __wasm_memory_size() | 0; var newPages = oldPages + pagesToAdd | 0; if ((oldPages < newPages) && (newPages < 65536)) { - var newBuffer = new ArrayBuffer(Math_imul(newPages, 65536)); + var newBuffer = new ArrayBuffer(newPages << 16); var newHEAP8 = new Int8Array(newBuffer); newHEAP8.set(HEAP8); HEAP8 = new Int8Array(newBuffer); diff --git a/test/wasm2js/bulk-memory.2asm.js.opt b/test/wasm2js/bulk-memory.2asm.js.opt index 50ddecee8b2..be6ac9d195a 100644 --- a/test/wasm2js/bulk-memory.2asm.js.opt +++ b/test/wasm2js/bulk-memory.2asm.js.opt @@ -61,7 +61,7 @@ function asmFunc(imports) { bufferView = HEAPU8; function __wasm_memory_size() { - return buffer.byteLength / 65536 | 0; + return buffer.byteLength >> 16; } function __wasm_memory_grow(pagesToAdd) { @@ -69,7 +69,7 @@ function asmFunc(imports) { var oldPages = __wasm_memory_size() | 0; var newPages = oldPages + pagesToAdd | 0; if ((oldPages < newPages) && (newPages < 65536)) { - var newBuffer = new ArrayBuffer(Math_imul(newPages, 65536)); + var newBuffer = new ArrayBuffer(newPages << 16); var newHEAP8 = new Int8Array(newBuffer); newHEAP8.set(HEAP8); HEAP8 = new Int8Array(newBuffer); @@ -162,7 +162,7 @@ function asmFunc(imports) { bufferView = HEAPU8; initActiveSegments(imports); function __wasm_memory_size() { - return buffer.byteLength / 65536 | 0; + return buffer.byteLength >> 16; } return { @@ -239,7 +239,7 @@ function asmFunc(imports) { bufferView = HEAPU8; function __wasm_memory_size() { - return buffer.byteLength / 65536 | 0; + return buffer.byteLength >> 16; } function __wasm_memory_grow(pagesToAdd) { @@ -247,7 +247,7 @@ function asmFunc(imports) { var oldPages = __wasm_memory_size() | 0; var newPages = oldPages + pagesToAdd | 0; if ((oldPages < newPages) && (newPages < 65536)) { - var newBuffer = new ArrayBuffer(Math_imul(newPages, 65536)); + var newBuffer = new ArrayBuffer(newPages << 16); var newHEAP8 = new Int8Array(newBuffer); newHEAP8.set(HEAP8); HEAP8 = new Int8Array(newBuffer); @@ -304,7 +304,7 @@ function asmFunc(imports) { } function __wasm_memory_size() { - return buffer.byteLength / 65536 | 0; + return buffer.byteLength >> 16; } function __wasm_memory_grow(pagesToAdd) { @@ -312,7 +312,7 @@ function asmFunc(imports) { var oldPages = __wasm_memory_size() | 0; var newPages = oldPages + pagesToAdd | 0; if ((oldPages < newPages) && (newPages < 65536)) { - var newBuffer = new ArrayBuffer(Math_imul(newPages, 65536)); + var newBuffer = new ArrayBuffer(newPages << 16); var newHEAP8 = new Int8Array(newBuffer); newHEAP8.set(HEAP8); HEAP8 = new Int8Array(newBuffer); diff --git a/test/wasm2js/deterministic.2asm.js b/test/wasm2js/deterministic.2asm.js index 33cb04a846e..e4c12bc8f78 100644 --- a/test/wasm2js/deterministic.2asm.js +++ b/test/wasm2js/deterministic.2asm.js @@ -34,7 +34,7 @@ function asmFunc(imports) { bufferView = HEAPU8; function __wasm_memory_size() { - return buffer.byteLength / 65536 | 0; + return buffer.byteLength >> 16; } return { diff --git a/test/wasm2js/deterministic.2asm.js.opt b/test/wasm2js/deterministic.2asm.js.opt index 12ebdd8599d..2c25650a716 100644 --- a/test/wasm2js/deterministic.2asm.js.opt +++ b/test/wasm2js/deterministic.2asm.js.opt @@ -33,7 +33,7 @@ function asmFunc(imports) { bufferView = HEAPU8; function __wasm_memory_size() { - return buffer.byteLength / 65536 | 0; + return buffer.byteLength >> 16; } return { diff --git a/test/wasm2js/dynamicLibrary.2asm.js b/test/wasm2js/dynamicLibrary.2asm.js index bb6f4dcaff6..cc4103f0774 100644 --- a/test/wasm2js/dynamicLibrary.2asm.js +++ b/test/wasm2js/dynamicLibrary.2asm.js @@ -77,7 +77,7 @@ function asmFunc(imports) { FUNCTION_TABLE[import$tableBase + 0] = foo; FUNCTION_TABLE[import$tableBase + 1] = bar; function __wasm_memory_size() { - return buffer.byteLength / 65536 | 0; + return buffer.byteLength >> 16; } return { diff --git a/test/wasm2js/dynamicLibrary.2asm.js.opt b/test/wasm2js/dynamicLibrary.2asm.js.opt index 543f755f37d..d0c8c7b2900 100644 --- a/test/wasm2js/dynamicLibrary.2asm.js.opt +++ b/test/wasm2js/dynamicLibrary.2asm.js.opt @@ -69,7 +69,7 @@ function asmFunc(imports) { FUNCTION_TABLE[import$tableBase + 0] = foo; FUNCTION_TABLE[import$tableBase + 1] = foo; function __wasm_memory_size() { - return buffer.byteLength / 65536 | 0; + return buffer.byteLength >> 16; } return { diff --git a/test/wasm2js/emscripten-grow-no.2asm.js b/test/wasm2js/emscripten-grow-no.2asm.js index 79963d6624c..e633392e300 100644 --- a/test/wasm2js/emscripten-grow-no.2asm.js +++ b/test/wasm2js/emscripten-grow-no.2asm.js @@ -52,7 +52,7 @@ function asmFunc(imports) { bufferView = HEAPU8; initActiveSegments(imports); function __wasm_memory_size() { - return buffer.byteLength / 65536 | 0; + return buffer.byteLength >> 16; } return { diff --git a/test/wasm2js/emscripten-grow-no.2asm.js.opt b/test/wasm2js/emscripten-grow-no.2asm.js.opt index 79963d6624c..e633392e300 100644 --- a/test/wasm2js/emscripten-grow-no.2asm.js.opt +++ b/test/wasm2js/emscripten-grow-no.2asm.js.opt @@ -52,7 +52,7 @@ function asmFunc(imports) { bufferView = HEAPU8; initActiveSegments(imports); function __wasm_memory_size() { - return buffer.byteLength / 65536 | 0; + return buffer.byteLength >> 16; } return { diff --git a/test/wasm2js/emscripten-grow-yes.2asm.js b/test/wasm2js/emscripten-grow-yes.2asm.js index 6ab56747baf..7df826d54ba 100644 --- a/test/wasm2js/emscripten-grow-yes.2asm.js +++ b/test/wasm2js/emscripten-grow-yes.2asm.js @@ -57,7 +57,7 @@ function asmFunc(imports) { bufferView = HEAPU8; initActiveSegments(imports); function __wasm_memory_size() { - return buffer.byteLength / 65536 | 0; + return buffer.byteLength >> 16; } function __wasm_memory_grow(pagesToAdd) { @@ -65,7 +65,7 @@ function asmFunc(imports) { var oldPages = __wasm_memory_size() | 0; var newPages = oldPages + pagesToAdd | 0; if ((oldPages < newPages) && (newPages < 65536)) { - var newBuffer = new ArrayBuffer(Math_imul(newPages, 65536)); + var newBuffer = new ArrayBuffer(newPages << 16); var newHEAP8 = new Int8Array(newBuffer); newHEAP8.set(HEAP8); HEAP8 = new Int8Array(newBuffer); diff --git a/test/wasm2js/emscripten-grow-yes.2asm.js.opt b/test/wasm2js/emscripten-grow-yes.2asm.js.opt index 6ab56747baf..7df826d54ba 100644 --- a/test/wasm2js/emscripten-grow-yes.2asm.js.opt +++ b/test/wasm2js/emscripten-grow-yes.2asm.js.opt @@ -57,7 +57,7 @@ function asmFunc(imports) { bufferView = HEAPU8; initActiveSegments(imports); function __wasm_memory_size() { - return buffer.byteLength / 65536 | 0; + return buffer.byteLength >> 16; } function __wasm_memory_grow(pagesToAdd) { @@ -65,7 +65,7 @@ function asmFunc(imports) { var oldPages = __wasm_memory_size() | 0; var newPages = oldPages + pagesToAdd | 0; if ((oldPages < newPages) && (newPages < 65536)) { - var newBuffer = new ArrayBuffer(Math_imul(newPages, 65536)); + var newBuffer = new ArrayBuffer(newPages << 16); var newHEAP8 = new Int8Array(newBuffer); newHEAP8.set(HEAP8); HEAP8 = new Int8Array(newBuffer); diff --git a/test/wasm2js/emscripten.2asm.js b/test/wasm2js/emscripten.2asm.js index 24a556b25d7..acf66476190 100644 --- a/test/wasm2js/emscripten.2asm.js +++ b/test/wasm2js/emscripten.2asm.js @@ -218,7 +218,7 @@ function asmFunc(imports) { FUNCTION_TABLE[2] = bar; FUNCTION_TABLE[3] = tabled; function __wasm_memory_size() { - return buffer.byteLength / 65536 | 0; + return buffer.byteLength >> 16; } return { diff --git a/test/wasm2js/emscripten.2asm.js.opt b/test/wasm2js/emscripten.2asm.js.opt index caef2389639..749a7edaef3 100644 --- a/test/wasm2js/emscripten.2asm.js.opt +++ b/test/wasm2js/emscripten.2asm.js.opt @@ -212,7 +212,7 @@ function asmFunc(imports) { FUNCTION_TABLE[2] = bar; FUNCTION_TABLE[3] = internal; function __wasm_memory_size() { - return buffer.byteLength / 65536 | 0; + return buffer.byteLength >> 16; } return { diff --git a/test/wasm2js/endianness.2asm.js b/test/wasm2js/endianness.2asm.js index 5e29d67c345..e9f0c8c9396 100644 --- a/test/wasm2js/endianness.2asm.js +++ b/test/wasm2js/endianness.2asm.js @@ -652,7 +652,7 @@ function asmFunc(imports) { bufferView = HEAPU8; function __wasm_memory_size() { - return buffer.byteLength / 65536 | 0; + return buffer.byteLength >> 16; } function __wasm_memory_grow(pagesToAdd) { @@ -660,7 +660,7 @@ function asmFunc(imports) { var oldPages = __wasm_memory_size() | 0; var newPages = oldPages + pagesToAdd | 0; if ((oldPages < newPages) && (newPages < 65536)) { - var newBuffer = new ArrayBuffer(Math_imul(newPages, 65536)); + var newBuffer = new ArrayBuffer(newPages << 16); var newHEAP8 = new Int8Array(newBuffer); newHEAP8.set(HEAP8); HEAP8 = new Int8Array(newBuffer); diff --git a/test/wasm2js/grow-memory-tricky.2asm.js b/test/wasm2js/grow-memory-tricky.2asm.js index 0dc0e1ee110..d75a80c5df5 100644 --- a/test/wasm2js/grow-memory-tricky.2asm.js +++ b/test/wasm2js/grow-memory-tricky.2asm.js @@ -36,7 +36,7 @@ function asmFunc(imports) { } function __wasm_memory_size() { - return buffer.byteLength / 65536 | 0; + return buffer.byteLength >> 16; } function __wasm_memory_grow(pagesToAdd) { @@ -44,7 +44,7 @@ function asmFunc(imports) { var oldPages = __wasm_memory_size() | 0; var newPages = oldPages + pagesToAdd | 0; if ((oldPages < newPages) && (newPages < 65536)) { - var newBuffer = new ArrayBuffer(Math_imul(newPages, 65536)); + var newBuffer = new ArrayBuffer(newPages << 16); var newHEAP8 = new Int8Array(newBuffer); newHEAP8.set(HEAP8); HEAP8 = new Int8Array(newBuffer); diff --git a/test/wasm2js/grow-memory-tricky.2asm.js.opt b/test/wasm2js/grow-memory-tricky.2asm.js.opt index d02bfcaa656..abc0b7ef835 100644 --- a/test/wasm2js/grow-memory-tricky.2asm.js.opt +++ b/test/wasm2js/grow-memory-tricky.2asm.js.opt @@ -26,7 +26,7 @@ function asmFunc(imports) { } function __wasm_memory_size() { - return buffer.byteLength / 65536 | 0; + return buffer.byteLength >> 16; } function __wasm_memory_grow(pagesToAdd) { @@ -34,7 +34,7 @@ function asmFunc(imports) { var oldPages = __wasm_memory_size() | 0; var newPages = oldPages + pagesToAdd | 0; if ((oldPages < newPages) && (newPages < 65536)) { - var newBuffer = new ArrayBuffer(Math_imul(newPages, 65536)); + var newBuffer = new ArrayBuffer(newPages << 16); var newHEAP8 = new Int8Array(newBuffer); newHEAP8.set(HEAP8); HEAP8 = new Int8Array(newBuffer); diff --git a/test/wasm2js/grow_memory.2asm.js b/test/wasm2js/grow_memory.2asm.js index 83a9b998de3..47cb08ca085 100644 --- a/test/wasm2js/grow_memory.2asm.js +++ b/test/wasm2js/grow_memory.2asm.js @@ -29,7 +29,7 @@ function asmFunc(imports) { } function __wasm_memory_size() { - return buffer.byteLength / 65536 | 0; + return buffer.byteLength >> 16; } function __wasm_memory_grow(pagesToAdd) { @@ -37,7 +37,7 @@ function asmFunc(imports) { var oldPages = __wasm_memory_size() | 0; var newPages = oldPages + pagesToAdd | 0; if ((oldPages < newPages) && (newPages < 65536)) { - var newBuffer = new ArrayBuffer(Math_imul(newPages, 65536)); + var newBuffer = new ArrayBuffer(newPages << 16); var newHEAP8 = new Int8Array(newBuffer); newHEAP8.set(HEAP8); HEAP8 = new Int8Array(newBuffer); diff --git a/test/wasm2js/left-to-right.2asm.js b/test/wasm2js/left-to-right.2asm.js index 6553cbc11d5..63bc9225796 100644 --- a/test/wasm2js/left-to-right.2asm.js +++ b/test/wasm2js/left-to-right.2asm.js @@ -2083,7 +2083,7 @@ function asmFunc(imports) { bufferView = HEAPU8; var FUNCTION_TABLE = [i32_t0, i32_t1, i64_t0, i64_t1, f32_t0, f32_t1, f64_t0, f64_t1]; function __wasm_memory_size() { - return buffer.byteLength / 65536 | 0; + return buffer.byteLength >> 16; } function __wasm_memory_grow(pagesToAdd) { @@ -2091,7 +2091,7 @@ function asmFunc(imports) { var oldPages = __wasm_memory_size() | 0; var newPages = oldPages + pagesToAdd | 0; if ((oldPages < newPages) && (newPages < 65536)) { - var newBuffer = new ArrayBuffer(Math_imul(newPages, 65536)); + var newBuffer = new ArrayBuffer(newPages << 16); var newHEAP8 = new Int8Array(newBuffer); newHEAP8.set(HEAP8); HEAP8 = new Int8Array(newBuffer); diff --git a/test/wasm2js/minified-memory.2asm.js b/test/wasm2js/minified-memory.2asm.js index 1d3e8c942f8..d2aded1bfdb 100644 --- a/test/wasm2js/minified-memory.2asm.js +++ b/test/wasm2js/minified-memory.2asm.js @@ -27,7 +27,7 @@ function asmFunc(imports) { } function __wasm_memory_size() { - return buffer.byteLength / 65536 | 0; + return buffer.byteLength >> 16; } function __wasm_memory_grow(pagesToAdd) { @@ -35,7 +35,7 @@ function asmFunc(imports) { var oldPages = __wasm_memory_size() | 0; var newPages = oldPages + pagesToAdd | 0; if ((oldPages < newPages) && (newPages < 65536)) { - var newBuffer = new ArrayBuffer(Math_imul(newPages, 65536)); + var newBuffer = new ArrayBuffer(newPages << 16); var newHEAP8 = new Int8Array(newBuffer); newHEAP8.set(HEAP8); HEAP8 = new Int8Array(newBuffer); diff --git a/test/wasm2js/minified-memory.2asm.js.opt b/test/wasm2js/minified-memory.2asm.js.opt index 6d1dfa47d7f..a7c31ca6858 100644 --- a/test/wasm2js/minified-memory.2asm.js.opt +++ b/test/wasm2js/minified-memory.2asm.js.opt @@ -27,7 +27,7 @@ function asmFunc(imports) { } function __wasm_memory_size() { - return buffer.byteLength / 65536 | 0; + return buffer.byteLength >> 16; } function __wasm_memory_grow(pagesToAdd) { @@ -35,7 +35,7 @@ function asmFunc(imports) { var oldPages = __wasm_memory_size() | 0; var newPages = oldPages + pagesToAdd | 0; if ((oldPages < newPages) && (newPages < 65536)) { - var newBuffer = new ArrayBuffer(Math_imul(newPages, 65536)); + var newBuffer = new ArrayBuffer(newPages << 16); var newHEAP8 = new Int8Array(newBuffer); newHEAP8.set(HEAP8); HEAP8 = new Int8Array(newBuffer); diff --git a/test/wasm2js/reinterpret_scratch.2asm.js b/test/wasm2js/reinterpret_scratch.2asm.js index 547206072a9..22969331eae 100644 --- a/test/wasm2js/reinterpret_scratch.2asm.js +++ b/test/wasm2js/reinterpret_scratch.2asm.js @@ -50,7 +50,7 @@ function asmFunc(imports) { bufferView = HEAPU8; function __wasm_memory_size() { - return buffer.byteLength / 65536 | 0; + return buffer.byteLength >> 16; } return { diff --git a/test/wasm2js/reinterpret_scratch.2asm.js.opt b/test/wasm2js/reinterpret_scratch.2asm.js.opt index bc077cdce1f..63901569ec6 100644 --- a/test/wasm2js/reinterpret_scratch.2asm.js.opt +++ b/test/wasm2js/reinterpret_scratch.2asm.js.opt @@ -45,7 +45,7 @@ function asmFunc(imports) { bufferView = HEAPU8; function __wasm_memory_size() { - return buffer.byteLength / 65536 | 0; + return buffer.byteLength >> 16; } return { diff --git a/test/wasm2js/start_func.2asm.js b/test/wasm2js/start_func.2asm.js index 0ca4c66dfb8..2eb744f319a 100644 --- a/test/wasm2js/start_func.2asm.js +++ b/test/wasm2js/start_func.2asm.js @@ -25,7 +25,7 @@ function asmFunc(imports) { foo(); function __wasm_memory_size() { - return buffer.byteLength / 65536 | 0; + return buffer.byteLength >> 16; } function __wasm_memory_grow(pagesToAdd) { @@ -33,7 +33,7 @@ function asmFunc(imports) { var oldPages = __wasm_memory_size() | 0; var newPages = oldPages + pagesToAdd | 0; if ((oldPages < newPages) && (newPages < 65536)) { - var newBuffer = new ArrayBuffer(Math_imul(newPages, 65536)); + var newBuffer = new ArrayBuffer(newPages << 16); var newHEAP8 = new Int8Array(newBuffer); newHEAP8.set(HEAP8); HEAP8 = new Int8Array(newBuffer); diff --git a/test/wasm2js/start_func.2asm.js.opt b/test/wasm2js/start_func.2asm.js.opt index 6a002727208..3b7e05608fa 100644 --- a/test/wasm2js/start_func.2asm.js.opt +++ b/test/wasm2js/start_func.2asm.js.opt @@ -25,7 +25,7 @@ function asmFunc(imports) { foo(); function __wasm_memory_size() { - return buffer.byteLength / 65536 | 0; + return buffer.byteLength >> 16; } function __wasm_memory_grow(pagesToAdd) { @@ -33,7 +33,7 @@ function asmFunc(imports) { var oldPages = __wasm_memory_size() | 0; var newPages = oldPages + pagesToAdd | 0; if ((oldPages < newPages) && (newPages < 65536)) { - var newBuffer = new ArrayBuffer(Math_imul(newPages, 65536)); + var newBuffer = new ArrayBuffer(newPages << 16); var newHEAP8 = new Int8Array(newBuffer); newHEAP8.set(HEAP8); HEAP8 = new Int8Array(newBuffer); diff --git a/test/wasm2js/traps.2asm.js b/test/wasm2js/traps.2asm.js index f7a469ebe2c..54f62705314 100644 --- a/test/wasm2js/traps.2asm.js +++ b/test/wasm2js/traps.2asm.js @@ -1660,7 +1660,7 @@ function asmFunc(imports) { } function __wasm_memory_size() { - return buffer.byteLength / 65536 | 0; + return buffer.byteLength >> 16; } function __wasm_memory_grow(pagesToAdd) { @@ -1668,7 +1668,7 @@ function asmFunc(imports) { var oldPages = __wasm_memory_size() | 0; var newPages = oldPages + pagesToAdd | 0; if ((oldPages < newPages) && (newPages < 65536)) { - var newBuffer = new ArrayBuffer(Math_imul(newPages, 65536)); + var newBuffer = new ArrayBuffer(newPages << 16); var newHEAP8 = new Int8Array(newBuffer); newHEAP8.set(HEAP8); HEAP8 = new Int8Array(newBuffer); diff --git a/test/wasm2js/unaligned.2asm.js b/test/wasm2js/unaligned.2asm.js index bf33c2d789a..d7f03e1b471 100644 --- a/test/wasm2js/unaligned.2asm.js +++ b/test/wasm2js/unaligned.2asm.js @@ -160,7 +160,7 @@ function asmFunc(imports) { bufferView = HEAPU8; function __wasm_memory_size() { - return buffer.byteLength / 65536 | 0; + return buffer.byteLength >> 16; } return { diff --git a/test/wasm2js/unaligned.2asm.js.opt b/test/wasm2js/unaligned.2asm.js.opt index 552868ded2b..ad5f96a4971 100644 --- a/test/wasm2js/unaligned.2asm.js.opt +++ b/test/wasm2js/unaligned.2asm.js.opt @@ -109,7 +109,7 @@ function asmFunc(imports) { bufferView = HEAPU8; function __wasm_memory_size() { - return buffer.byteLength / 65536 | 0; + return buffer.byteLength >> 16; } return {