diff options
author | Alon Zakai <alonzakai@gmail.com> | 2018-10-15 16:07:24 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-10-15 16:07:24 -0700 |
commit | 021a2b85fb9264d9cb4a21c039682d1f0fddbd1c (patch) | |
tree | 96f39abb25e77769b337aa3d7858c4722c6ba1b4 | |
parent | 66dbc57d32bb2c8c01deefba7a035ebed5a42e2c (diff) | |
download | binaryen-021a2b85fb9264d9cb4a21c039682d1f0fddbd1c.tar.gz binaryen-021a2b85fb9264d9cb4a21c039682d1f0fddbd1c.tar.bz2 binaryen-021a2b85fb9264d9cb4a21c039682d1f0fddbd1c.zip |
Support 4GB Memories (#1702)
This fixes asm2wasm parsing of the max to allow 4GB, and also changes the internal Memory::kMaxValue values to reflect that. We used to use kMaxValue to also represent "no limit", so I split that out into kUnlimitedValue.
-rwxr-xr-x | auto_update_tests.py | 2 | ||||
-rwxr-xr-x | scripts/test/asm2wasm.py | 2 | ||||
-rw-r--r-- | src/asm2wasm.h | 2 | ||||
-rw-r--r-- | src/tools/asm2wasm.cpp | 8 | ||||
-rw-r--r-- | src/tools/fuzzing.h | 2 | ||||
-rw-r--r-- | src/wasm-js.cpp | 4 | ||||
-rw-r--r-- | src/wasm.h | 10 | ||||
-rw-r--r-- | src/wasm/wasm-binary.cpp | 16 | ||||
-rw-r--r-- | src/wasm/wasm-s-parser.cpp | 7 | ||||
-rw-r--r-- | src/wasm/wasm-validator.cpp | 2 | ||||
-rw-r--r-- | test/empty_4GB.asm.js | 4 | ||||
-rw-r--r-- | test/empty_4GB.fromasm | 4 | ||||
-rw-r--r-- | test/empty_4GB.fromasm.clamp | 4 | ||||
-rw-r--r-- | test/empty_4GB.fromasm.clamp.no-opts | 6 | ||||
-rw-r--r-- | test/empty_4GB.fromasm.imprecise | 2 | ||||
-rw-r--r-- | test/empty_4GB.fromasm.imprecise.no-opts | 6 | ||||
-rw-r--r-- | test/empty_4GB.fromasm.no-opts | 6 | ||||
-rw-r--r-- | test/wasm2js/grow-memory-tricky.2asm.js | 2 | ||||
-rw-r--r-- | test/wasm2js/grow_memory.2asm.js | 2 |
19 files changed, 67 insertions, 24 deletions
diff --git a/auto_update_tests.py b/auto_update_tests.py index 4693351b1..d11a8f6aa 100755 --- a/auto_update_tests.py +++ b/auto_update_tests.py @@ -58,6 +58,8 @@ def update_asm_js_tests(): cmd += ['--mem-init=a.mem'] if asm[0] == 'e': cmd += ['--mem-base=1024'] + if '4GB' in asm: + cmd += ['--mem-max=4294967296'] if 'i64' in asm or 'wasm-only' in asm or 'noffi' in asm: cmd += ['--wasm-only'] print ' '.join(cmd) diff --git a/scripts/test/asm2wasm.py b/scripts/test/asm2wasm.py index 63ed79ce6..fa38b9c3b 100755 --- a/scripts/test/asm2wasm.py +++ b/scripts/test/asm2wasm.py @@ -58,6 +58,8 @@ def test_asm2wasm(): cmd += ['--mem-init=a.mem'] if asm[0] == 'e': cmd += ['--mem-base=1024'] + if '4GB' in asm: + cmd += ['--mem-max=4294967296'] if 'i64' in asm or 'wasm-only' in asm or 'noffi' in asm: cmd += ['--wasm-only'] wasm = os.path.join(options.binaryen_test, wasm) diff --git a/src/asm2wasm.h b/src/asm2wasm.h index 44666c1e5..de50eb33d 100644 --- a/src/asm2wasm.h +++ b/src/asm2wasm.h @@ -759,7 +759,7 @@ void Asm2WasmBuilder::processAsm(Ref ast) { EmscriptenGlueGenerator generator(wasm); auto* func = generator.generateMemoryGrowthFunction(); extraSupportFunctions.push_back(func); - wasm.memory.max = Memory::kMaxSize; + wasm.memory.max = Memory::kUnlimitedSize; } // import memory diff --git a/src/tools/asm2wasm.cpp b/src/tools/asm2wasm.cpp index 1cf7947ce..cc9464434 100644 --- a/src/tools/asm2wasm.cpp +++ b/src/tools/asm2wasm.cpp @@ -197,11 +197,11 @@ int main(int argc, const char *argv[]) { // Set the max memory size, if requested const auto &memMax = options.extra.find("mem max"); if (memMax != options.extra.end()) { - int max = atoi(memMax->second.c_str()); - if (max >= 0) { + uint64_t max = strtoull(memMax->second.c_str(), nullptr, 10); + if (max != uint64_t(-1)) { wasm.memory.max = max / Memory::kPageSize; } else { - wasm.memory.max = Memory::kMaxSize; + wasm.memory.max = Memory::kUnlimitedSize; } } // Set the table sizes, if requested @@ -211,7 +211,7 @@ int main(int argc, const char *argv[]) { if (max >= 0) { wasm.table.max = max; } else { - wasm.table.max = Table::kMaxSize; + wasm.table.max = Table::kUnlimitedSize; } } diff --git a/src/tools/fuzzing.h b/src/tools/fuzzing.h index 062fc29a6..fe31290d6 100644 --- a/src/tools/fuzzing.h +++ b/src/tools/fuzzing.h @@ -268,7 +268,7 @@ private: void finalizeTable() { wasm.table.initial = wasm.table.segments[0].data.size(); - wasm.table.max = oneIn(2) ? Address(Table::kMaxSize) : wasm.table.initial; + wasm.table.max = oneIn(2) ? Address(Table::kUnlimitedSize) : wasm.table.initial; } const Name HANG_LIMIT_GLOBAL = "hangLimit"; diff --git a/src/wasm-js.cpp b/src/wasm-js.cpp index a777295d7..381487cd0 100644 --- a/src/wasm-js.cpp +++ b/src/wasm-js.cpp @@ -78,7 +78,7 @@ extern "C" void EMSCRIPTEN_KEEPALIVE load_asm2wasm(char *input) { exit(EXIT_FAILURE); } module->memory.initial = Address(providedMemory / Memory::kPageSize); - module->memory.max = pre.memoryGrowth ? Address(Memory::kMaxSize) : module->memory.initial; + module->memory.max = pre.memoryGrowth ? Address(Memory::kUnlimitedSize) : module->memory.initial; if (wasmJSDebug) std::cerr << "wasming...\n"; asm2wasm = new Asm2WasmBuilder(*module, pre, debug, TrapMode::JS, PassOptions(), true /* runJSFFIPass */, false /* TODO: support optimizing? */, false /* TODO: support asm2wasm-i64? */); @@ -94,7 +94,7 @@ void finalizeModule() { exit(EXIT_FAILURE); } module->memory.initial = Address(providedMemory / Memory::kPageSize); - module->memory.max = module->getExportOrNull(GROW_WASM_MEMORY) ? Address(Memory::kMaxSize) : module->memory.initial; + module->memory.max = module->getExportOrNull(GROW_WASM_MEMORY) ? Address(Memory::kUnlimitedSize) : module->memory.initial; // global mapping is done in js in post.js } diff --git a/src/wasm.h b/src/wasm.h index 57591d811..a0f634d17 100644 --- a/src/wasm.h +++ b/src/wasm.h @@ -662,6 +662,8 @@ public: class Table : public Importable { public: static const Address::address_t kPageSize = 1; + static const Index kUnlimitedSize = Index(-1); + // In wasm32, the maximum table size is limited by a 32-bit pointer: 4GB static const Index kMaxSize = Index(-1); struct Segment { @@ -684,13 +686,15 @@ public: Table() : exists(false), initial(0), max(kMaxSize) { name = Name::fromInt(0); } - bool hasMax() { return max != kMaxSize; } + bool hasMax() { return max != kUnlimitedSize; } }; class Memory : public Importable { public: static const Address::address_t kPageSize = 64 * 1024; - static const Address::address_t kMaxSize = ~Address::address_t(0) / kPageSize; + static const Address::address_t kUnlimitedSize = Address::address_t(-1); + // In wasm32, the maximum memory size is limited by a 32-bit pointer: 4GB + static const Address::address_t kMaxSize = (uint64_t(4) * 1024 * 1024 * 1024) / kPageSize; static const Address::address_t kPageMask = ~(kPageSize - 1); struct Segment { @@ -718,7 +722,7 @@ public: Memory() : initial(0), max(kMaxSize), exists(false), shared(false) { name = Name::fromInt(0); } - bool hasMax() { return max != kMaxSize; } + bool hasMax() { return max != kUnlimitedSize; } }; class Global : public Importable { diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp index 1839c9cbd..fddb9c3a0 100644 --- a/src/wasm/wasm-binary.cpp +++ b/src/wasm/wasm-binary.cpp @@ -143,7 +143,7 @@ void WasmBinaryWriter::writeMemory() { auto start = startSection(BinaryConsts::Section::Memory); o << U32LEB(1); // Define 1 memory writeResizableLimits(wasm->memory.initial, wasm->memory.max, - wasm->memory.max != Memory::kMaxSize, wasm->memory.shared); + wasm->memory.hasMax(), wasm->memory.shared); finishSection(start); } @@ -205,14 +205,14 @@ void WasmBinaryWriter::writeImports() { writeImportHeader(&wasm->memory); o << U32LEB(int32_t(ExternalKind::Memory)); writeResizableLimits(wasm->memory.initial, wasm->memory.max, - wasm->memory.max != Memory::kMaxSize, wasm->memory.shared); + wasm->memory.hasMax(), wasm->memory.shared); } if (wasm->table.imported()) { if (debug) std::cerr << "write one table" << std::endl; writeImportHeader(&wasm->table); o << U32LEB(int32_t(ExternalKind::Table)); o << S32LEB(BinaryConsts::EncodedType::AnyFunc); - writeResizableLimits(wasm->table.initial, wasm->table.max, wasm->table.max != Table::kMaxSize, /*shared=*/false); + writeResizableLimits(wasm->table.initial, wasm->table.max, wasm->table.hasMax(), /*shared=*/false); } finishSection(start); } @@ -418,7 +418,7 @@ void WasmBinaryWriter::writeFunctionTableDeclaration() { auto start = startSection(BinaryConsts::Section::Table); o << U32LEB(1); // Declare 1 table. o << S32LEB(BinaryConsts::EncodedType::AnyFunc); - writeResizableLimits(wasm->table.initial, wasm->table.max, wasm->table.max != Table::kMaxSize, /*shared=*/false); + writeResizableLimits(wasm->table.initial, wasm->table.max, wasm->table.hasMax(), /*shared=*/false); finishSection(start); } @@ -899,7 +899,7 @@ void WasmBinaryBuilder::readMemory() { throwError("Memory cannot be both imported and defined"); } wasm.memory.exists = true; - getResizableLimits(wasm.memory.initial, wasm.memory.max, wasm.memory.shared, Memory::kMaxSize); + getResizableLimits(wasm.memory.initial, wasm.memory.max, wasm.memory.shared, Memory::kUnlimitedSize); } void WasmBinaryBuilder::readSignatures() { @@ -987,7 +987,7 @@ void WasmBinaryBuilder::readImports() { if (elementType != BinaryConsts::EncodedType::AnyFunc) throwError("Imported table type is not AnyFunc"); wasm.table.exists = true; bool is_shared; - getResizableLimits(wasm.table.initial, wasm.table.max, is_shared, Table::kMaxSize); + getResizableLimits(wasm.table.initial, wasm.table.max, is_shared, Table::kUnlimitedSize); if (is_shared) throwError("Tables may not be shared"); break; } @@ -996,7 +996,7 @@ void WasmBinaryBuilder::readImports() { wasm.memory.base = base; wasm.memory.name = Name(std::to_string(i)); wasm.memory.exists = true; - getResizableLimits(wasm.memory.initial, wasm.memory.max, wasm.memory.shared, Memory::kMaxSize); + getResizableLimits(wasm.memory.initial, wasm.memory.max, wasm.memory.shared, Memory::kUnlimitedSize); break; } case ExternalKind::Global: { @@ -1542,7 +1542,7 @@ void WasmBinaryBuilder::readFunctionTableDeclaration() { auto elemType = getS32LEB(); if (elemType != BinaryConsts::EncodedType::AnyFunc) throwError("ElementType must be AnyFunc in MVP"); bool is_shared; - getResizableLimits(wasm.table.initial, wasm.table.max, is_shared, Table::kMaxSize); + getResizableLimits(wasm.table.initial, wasm.table.max, is_shared, Table::kUnlimitedSize); if (is_shared) throwError("Tables may not be shared"); } diff --git a/src/wasm/wasm-s-parser.cpp b/src/wasm/wasm-s-parser.cpp index 7085666bb..ee37f23f3 100644 --- a/src/wasm/wasm-s-parser.cpp +++ b/src/wasm/wasm-s-parser.cpp @@ -1510,7 +1510,10 @@ void SExpressionWasmBuilder::stringToBinary(const char* input, size_t size, std: Index SExpressionWasmBuilder::parseMemoryLimits(Element& s, Index i) { wasm.memory.initial = getCheckedAddress(s[i++], "excessive memory init"); - if (i == s.size()) return i; + if (i == s.size()) { + wasm.memory.max = Memory::kUnlimitedSize; + return i; + } uint64_t max = atoll(s[i++]->c_str()); if (max > Memory::kMaxSize) throw ParseException("total memory must be <= 4GB"); wasm.memory.max = max; @@ -1764,7 +1767,7 @@ void SExpressionWasmBuilder::parseImport(Element& s) { if (j < inner.size() - 1) { wasm.table.max = getCheckedAddress(inner[j++], "excessive table max size"); } else { - wasm.table.max = Table::kMaxSize; + wasm.table.max = Table::kUnlimitedSize; } // ends with the table element type } else if (kind == ExternalKind::Memory) { diff --git a/src/wasm/wasm-validator.cpp b/src/wasm/wasm-validator.cpp index 07c7b6aad..fbd31b920 100644 --- a/src/wasm/wasm-validator.cpp +++ b/src/wasm/wasm-validator.cpp @@ -969,7 +969,7 @@ static void validateGlobals(Module& module, ValidationInfo& info) { static void validateMemory(Module& module, ValidationInfo& info) { auto& curr = module.memory; info.shouldBeFalse(curr.initial > curr.max, "memory", "memory max >= initial"); - info.shouldBeTrue(curr.max <= Memory::kMaxSize, "memory", "max memory must be <= 4GB"); + info.shouldBeTrue(!curr.hasMax() || curr.max <= Memory::kMaxSize, "memory", "max memory must be <= 4GB, or unlimited"); info.shouldBeTrue(!curr.shared || curr.hasMax(), "memory", "shared memory must have max size"); if (curr.shared) info.shouldBeTrue(info.features & Feature::Atomics, "memory", "memory is shared, but atomics are disabled"); for (auto& segment : curr.segments) { diff --git a/test/empty_4GB.asm.js b/test/empty_4GB.asm.js new file mode 100644 index 000000000..94fa6bb38 --- /dev/null +++ b/test/empty_4GB.asm.js @@ -0,0 +1,4 @@ +function EmptyModule() { + 'use asm'; + return {}; +} diff --git a/test/empty_4GB.fromasm b/test/empty_4GB.fromasm new file mode 100644 index 000000000..91ef06fff --- /dev/null +++ b/test/empty_4GB.fromasm @@ -0,0 +1,4 @@ +(module + (import "env" "memory" (memory $memory 256 65536)) + (data (i32.const 1024) "empty_4GB.asm.js") +) diff --git a/test/empty_4GB.fromasm.clamp b/test/empty_4GB.fromasm.clamp new file mode 100644 index 000000000..91ef06fff --- /dev/null +++ b/test/empty_4GB.fromasm.clamp @@ -0,0 +1,4 @@ +(module + (import "env" "memory" (memory $memory 256 65536)) + (data (i32.const 1024) "empty_4GB.asm.js") +) diff --git a/test/empty_4GB.fromasm.clamp.no-opts b/test/empty_4GB.fromasm.clamp.no-opts new file mode 100644 index 000000000..7f68f6564 --- /dev/null +++ b/test/empty_4GB.fromasm.clamp.no-opts @@ -0,0 +1,6 @@ +(module + (import "env" "memory" (memory $memory 256 65536)) + (import "env" "table" (table 0 0 anyfunc)) + (import "env" "memoryBase" (global $memoryBase i32)) + (import "env" "tableBase" (global $tableBase i32)) +) diff --git a/test/empty_4GB.fromasm.imprecise b/test/empty_4GB.fromasm.imprecise new file mode 100644 index 000000000..4427f36e0 --- /dev/null +++ b/test/empty_4GB.fromasm.imprecise @@ -0,0 +1,2 @@ +(module +) diff --git a/test/empty_4GB.fromasm.imprecise.no-opts b/test/empty_4GB.fromasm.imprecise.no-opts new file mode 100644 index 000000000..7f68f6564 --- /dev/null +++ b/test/empty_4GB.fromasm.imprecise.no-opts @@ -0,0 +1,6 @@ +(module + (import "env" "memory" (memory $memory 256 65536)) + (import "env" "table" (table 0 0 anyfunc)) + (import "env" "memoryBase" (global $memoryBase i32)) + (import "env" "tableBase" (global $tableBase i32)) +) diff --git a/test/empty_4GB.fromasm.no-opts b/test/empty_4GB.fromasm.no-opts new file mode 100644 index 000000000..7f68f6564 --- /dev/null +++ b/test/empty_4GB.fromasm.no-opts @@ -0,0 +1,6 @@ +(module + (import "env" "memory" (memory $memory 256 65536)) + (import "env" "table" (table 0 0 anyfunc)) + (import "env" "memoryBase" (global $memoryBase i32)) + (import "env" "tableBase" (global $tableBase i32)) +) diff --git a/test/wasm2js/grow-memory-tricky.2asm.js b/test/wasm2js/grow-memory-tricky.2asm.js index 3d5b9ec76..6d5aae8f1 100644 --- a/test/wasm2js/grow-memory-tricky.2asm.js +++ b/test/wasm2js/grow-memory-tricky.2asm.js @@ -45,7 +45,7 @@ function asmFunc(global, env, buffer) { pagesToAdd = pagesToAdd | 0; var oldPages = __wasm_current_memory() | 0; var newPages = oldPages + pagesToAdd | 0; - if ((oldPages < newPages) && (newPages < 65535)) { + if ((oldPages < newPages) && (newPages < 65536)) { var newBuffer = new ArrayBuffer(Math_imul(newPages, 65536)); var newHEAP8 = new global.Int8Array(newBuffer); newHEAP8.set(HEAP8); diff --git a/test/wasm2js/grow_memory.2asm.js b/test/wasm2js/grow_memory.2asm.js index db41919d2..31e3dd525 100644 --- a/test/wasm2js/grow_memory.2asm.js +++ b/test/wasm2js/grow_memory.2asm.js @@ -34,7 +34,7 @@ function asmFunc(global, env, buffer) { pagesToAdd = pagesToAdd | 0; var oldPages = __wasm_current_memory() | 0; var newPages = oldPages + pagesToAdd | 0; - if ((oldPages < newPages) && (newPages < 65535)) { + if ((oldPages < newPages) && (newPages < 65536)) { var newBuffer = new ArrayBuffer(Math_imul(newPages, 65536)); var newHEAP8 = new global.Int8Array(newBuffer); newHEAP8.set(HEAP8); |