summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlon Zakai <alonzakai@gmail.com>2018-10-15 16:07:24 -0700
committerGitHub <noreply@github.com>2018-10-15 16:07:24 -0700
commit021a2b85fb9264d9cb4a21c039682d1f0fddbd1c (patch)
tree96f39abb25e77769b337aa3d7858c4722c6ba1b4
parent66dbc57d32bb2c8c01deefba7a035ebed5a42e2c (diff)
downloadbinaryen-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-xauto_update_tests.py2
-rwxr-xr-xscripts/test/asm2wasm.py2
-rw-r--r--src/asm2wasm.h2
-rw-r--r--src/tools/asm2wasm.cpp8
-rw-r--r--src/tools/fuzzing.h2
-rw-r--r--src/wasm-js.cpp4
-rw-r--r--src/wasm.h10
-rw-r--r--src/wasm/wasm-binary.cpp16
-rw-r--r--src/wasm/wasm-s-parser.cpp7
-rw-r--r--src/wasm/wasm-validator.cpp2
-rw-r--r--test/empty_4GB.asm.js4
-rw-r--r--test/empty_4GB.fromasm4
-rw-r--r--test/empty_4GB.fromasm.clamp4
-rw-r--r--test/empty_4GB.fromasm.clamp.no-opts6
-rw-r--r--test/empty_4GB.fromasm.imprecise2
-rw-r--r--test/empty_4GB.fromasm.imprecise.no-opts6
-rw-r--r--test/empty_4GB.fromasm.no-opts6
-rw-r--r--test/wasm2js/grow-memory-tricky.2asm.js2
-rw-r--r--test/wasm2js/grow_memory.2asm.js2
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);