summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/ir/memory-utils.h8
-rw-r--r--src/passes/I64ToI32Lowering.cpp9
-rw-r--r--src/passes/RemoveNonJSOps.cpp4
-rw-r--r--src/tools/fuzzing.h5
-rw-r--r--src/wasm/wasm-validator.cpp11
5 files changed, 34 insertions, 3 deletions
diff --git a/src/ir/memory-utils.h b/src/ir/memory-utils.h
index 5980a3218..c8f39a2ec 100644
--- a/src/ir/memory-utils.h
+++ b/src/ir/memory-utils.h
@@ -48,6 +48,14 @@ namespace MemoryUtils {
memory.segments[0].data.swap(data);
return true;
}
+
+ // Ensures that the memory exists (of minimal size).
+ inline void ensureExists(Memory& memory) {
+ if (!memory.exists) {
+ memory.exists = true;
+ memory.initial = memory.max = 1;
+ }
+ }
};
} // namespace wasm
diff --git a/src/passes/I64ToI32Lowering.cpp b/src/passes/I64ToI32Lowering.cpp
index 4575dd2f8..ae69ec19d 100644
--- a/src/passes/I64ToI32Lowering.cpp
+++ b/src/passes/I64ToI32Lowering.cpp
@@ -27,6 +27,7 @@
#include "emscripten-optimizer/istring.h"
#include "support/name.h"
#include "wasm-builder.h"
+#include "ir/memory-utils.h"
#include "ir/module-utils.h"
#include "ir/names.h"
#include "asmjs/shared-constants.h"
@@ -608,6 +609,7 @@ struct I64ToI32Lowering : public WalkerPass<PostWalker<I64ToI32Lowering>> {
);
setOutParam(result, std::move(highBits));
replaceCurrent(result);
+ ensureMinimalMemory();
}
void lowerReinterpretInt64(Unary* curr) {
@@ -620,6 +622,13 @@ struct I64ToI32Lowering : public WalkerPass<PostWalker<I64ToI32Lowering>> {
builder->makeLoad(8, true, 0, 8, builder->makeConst(Literal(int32_t(0))), f64)
);
replaceCurrent(result);
+ ensureMinimalMemory();
+ }
+
+ // Ensure memory exists with a minimal size, enough for round-tripping operations on
+ // address 0, which we need for reinterpret operations.
+ void ensureMinimalMemory() {
+ MemoryUtils::ensureExists(getModule()->memory);
}
void lowerTruncFloatToInt(Unary *curr) {
diff --git a/src/passes/RemoveNonJSOps.cpp b/src/passes/RemoveNonJSOps.cpp
index cc9a40a1f..342f59eba 100644
--- a/src/passes/RemoveNonJSOps.cpp
+++ b/src/passes/RemoveNonJSOps.cpp
@@ -33,6 +33,7 @@
#include "asmjs/shared-constants.h"
#include "wasm-builder.h"
#include "wasm-s-parser.h"
+#include "ir/memory-utils.h"
#include "ir/module-utils.h"
#include "ir/find_all.h"
#include "passes/intrinsics-module.h"
@@ -97,6 +98,9 @@ struct RemoveNonJSOpsPass : public WalkerPass<PostWalker<RemoveNonJSOpsPass>> {
}
neededFunctions.clear();
}
+
+ // Intrinsics may use memory, so ensure the module has one.
+ MemoryUtils::ensureExists(module->memory);
}
void addNeededFunctions(Module &m, Name name, std::set<Name> &needed) {
diff --git a/src/tools/fuzzing.h b/src/tools/fuzzing.h
index 6aa0764f1..b17be8afe 100644
--- a/src/tools/fuzzing.h
+++ b/src/tools/fuzzing.h
@@ -29,6 +29,7 @@ high chance for set at start of loop
#include <ir/find_all.h>
#include <ir/literal-utils.h>
#include <ir/manipulation.h>
+#include "ir/memory-utils.h"
#include <ir/utils.h>
namespace wasm {
@@ -254,9 +255,7 @@ private:
}
void setupMemory() {
- wasm.memory.exists = true;
- // use one page
- wasm.memory.initial = wasm.memory.max = 1;
+ MemoryUtils::ensureExists(wasm.memory);
// init some data
wasm.memory.segments.emplace_back(builder.makeConst(Literal(int32_t(0))));
auto num = upTo(USABLE_MEMORY * 2);
diff --git a/src/wasm/wasm-validator.cpp b/src/wasm/wasm-validator.cpp
index 6d4490982..79c1d4f9b 100644
--- a/src/wasm/wasm-validator.cpp
+++ b/src/wasm/wasm-validator.cpp
@@ -512,6 +512,7 @@ void FunctionValidator::visitSetGlobal(SetGlobal* curr) {
}
void FunctionValidator::visitLoad(Load* curr) {
+ shouldBeTrue(getModule()->memory.exists, curr, "Memory operations require a memory");
if (curr->isAtomic) {
shouldBeTrue(info.features.hasAtomics(), curr, "Atomic operation (atomics are disabled)");
shouldBeTrue(curr->type == i32 || curr->type == i64 || curr->type == unreachable, curr, "Atomic load should be i32 or i64");
@@ -528,6 +529,7 @@ void FunctionValidator::visitLoad(Load* curr) {
}
void FunctionValidator::visitStore(Store* curr) {
+ shouldBeTrue(getModule()->memory.exists, curr, "Memory operations require a memory");
if (curr->isAtomic) {
shouldBeTrue(info.features.hasAtomics(), curr, "Atomic operation (atomics are disabled)");
shouldBeTrue(curr->valueType == i32 || curr->valueType == i64 || curr->valueType == unreachable, curr, "Atomic store should be i32 or i64");
@@ -545,6 +547,7 @@ void FunctionValidator::visitStore(Store* curr) {
}
void FunctionValidator::visitAtomicRMW(AtomicRMW* curr) {
+ shouldBeTrue(getModule()->memory.exists, curr, "Memory operations require a memory");
shouldBeTrue(info.features.hasAtomics(), curr, "Atomic operation (atomics are disabled)");
shouldBeFalse(!getModule()->memory.shared, curr, "Atomic operation with non-shared memory");
validateMemBytes(curr->bytes, curr->type, curr);
@@ -554,6 +557,7 @@ void FunctionValidator::visitAtomicRMW(AtomicRMW* curr) {
}
void FunctionValidator::visitAtomicCmpxchg(AtomicCmpxchg* curr) {
+ shouldBeTrue(getModule()->memory.exists, curr, "Memory operations require a memory");
shouldBeTrue(info.features.hasAtomics(), curr, "Atomic operation (atomics are disabled)");
shouldBeFalse(!getModule()->memory.shared, curr, "Atomic operation with non-shared memory");
validateMemBytes(curr->bytes, curr->type, curr);
@@ -567,6 +571,7 @@ void FunctionValidator::visitAtomicCmpxchg(AtomicCmpxchg* curr) {
}
void FunctionValidator::visitAtomicWait(AtomicWait* curr) {
+ shouldBeTrue(getModule()->memory.exists, curr, "Memory operations require a memory");
shouldBeTrue(info.features.hasAtomics(), curr, "Atomic operation (atomics are disabled)");
shouldBeFalse(!getModule()->memory.shared, curr, "Atomic operation with non-shared memory");
shouldBeEqualOrFirstIsUnreachable(curr->type, i32, curr, "AtomicWait must have type i32");
@@ -577,6 +582,7 @@ void FunctionValidator::visitAtomicWait(AtomicWait* curr) {
}
void FunctionValidator::visitAtomicNotify(AtomicNotify* curr) {
+ shouldBeTrue(getModule()->memory.exists, curr, "Memory operations require a memory");
shouldBeTrue(info.features.hasAtomics(), curr, "Atomic operation (atomics are disabled)");
shouldBeFalse(!getModule()->memory.shared, curr, "Atomic operation with non-shared memory");
shouldBeEqualOrFirstIsUnreachable(curr->type, i32, curr, "AtomicNotify must have type i32");
@@ -647,6 +653,7 @@ void FunctionValidator::visitSIMDShift(SIMDShift* curr) {
}
void FunctionValidator::visitMemoryInit(MemoryInit* curr) {
+ shouldBeTrue(getModule()->memory.exists, curr, "Memory operations require a memory");
shouldBeTrue(info.features.hasBulkMemory(), curr, "Bulk memory operation (bulk memory is disabled)");
shouldBeEqualOrFirstIsUnreachable(curr->type, none, curr, "memory.init must have type none");
shouldBeEqualOrFirstIsUnreachable(curr->dest->type, i32, curr, "memory.init dest must be an i32");
@@ -656,12 +663,14 @@ void FunctionValidator::visitMemoryInit(MemoryInit* curr) {
}
void FunctionValidator::visitDataDrop(DataDrop* curr) {
+ shouldBeTrue(getModule()->memory.exists, curr, "Memory operations require a memory");
shouldBeTrue(info.features.hasBulkMemory(), curr, "Bulk memory operation (bulk memory is disabled)");
shouldBeEqualOrFirstIsUnreachable(curr->type, none, curr, "data.drop must have type none");
shouldBeTrue(curr->segment < getModule()->memory.segments.size(), curr, "data.drop segment index out of bounds");
}
void FunctionValidator::visitMemoryCopy(MemoryCopy* curr) {
+ shouldBeTrue(getModule()->memory.exists, curr, "Memory operations require a memory");
shouldBeTrue(info.features.hasBulkMemory(), curr, "Bulk memory operation (bulk memory is disabled)");
shouldBeEqualOrFirstIsUnreachable(curr->type, none, curr, "memory.copy must have type none");
shouldBeEqualOrFirstIsUnreachable(curr->dest->type, i32, curr, "memory.copy dest must be an i32");
@@ -670,6 +679,7 @@ void FunctionValidator::visitMemoryCopy(MemoryCopy* curr) {
}
void FunctionValidator::visitMemoryFill(MemoryFill* curr) {
+ shouldBeTrue(getModule()->memory.exists, curr, "Memory operations require a memory");
shouldBeTrue(info.features.hasBulkMemory(), curr, "Bulk memory operation (bulk memory is disabled)");
shouldBeEqualOrFirstIsUnreachable(curr->type, none, curr, "memory.fill must have type none");
shouldBeEqualOrFirstIsUnreachable(curr->dest->type, i32, curr, "memory.fill dest must be an i32");
@@ -1289,6 +1299,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.initial <= Memory::kMaxSize, "memory", "initial 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.hasAtomics(), "memory", "memory is shared, but atomics are disabled");