summaryrefslogtreecommitdiff
path: root/src/wasm/wasm-validator.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/wasm/wasm-validator.cpp')
-rw-r--r--src/wasm/wasm-validator.cpp128
1 files changed, 81 insertions, 47 deletions
diff --git a/src/wasm/wasm-validator.cpp b/src/wasm/wasm-validator.cpp
index c141fdd6f..505753c88 100644
--- a/src/wasm/wasm-validator.cpp
+++ b/src/wasm/wasm-validator.cpp
@@ -388,6 +388,8 @@ private:
void validateAlignment(
size_t align, Type type, Index bytes, bool isAtomic, Expression* curr);
void validateMemBytes(uint8_t bytes, Type type, Expression* curr);
+
+ Type indexType() { return getModule()->memory.indexType; }
};
void FunctionValidator::noteLabelName(Name name) {
@@ -931,7 +933,10 @@ void FunctionValidator::visitLoad(Load* curr) {
validateMemBytes(curr->bytes, curr->type, curr);
validateAlignment(curr->align, curr->type, curr->bytes, curr->isAtomic, curr);
shouldBeEqualOrFirstIsUnreachable(
- curr->ptr->type, Type(Type::i32), curr, "load pointer type must be i32");
+ curr->ptr->type,
+ indexType(),
+ curr,
+ "load pointer type must match memory index type");
if (curr->isAtomic) {
shouldBeFalse(curr->signed_, curr, "atomic loads must be unsigned");
shouldBeIntOrUnreachable(
@@ -963,7 +968,10 @@ void FunctionValidator::visitStore(Store* curr) {
validateAlignment(
curr->align, curr->valueType, curr->bytes, curr->isAtomic, curr);
shouldBeEqualOrFirstIsUnreachable(
- curr->ptr->type, Type(Type::i32), curr, "store pointer type must be i32");
+ curr->ptr->type,
+ indexType(),
+ curr,
+ "store pointer must match memory index type");
shouldBeUnequal(curr->value->type,
Type(Type::none),
curr,
@@ -986,10 +994,11 @@ void FunctionValidator::visitAtomicRMW(AtomicRMW* curr) {
curr,
"Atomic operation with non-shared memory");
validateMemBytes(curr->bytes, curr->type, curr);
- shouldBeEqualOrFirstIsUnreachable(curr->ptr->type,
- Type(Type::i32),
- curr,
- "AtomicRMW pointer type must be i32");
+ shouldBeEqualOrFirstIsUnreachable(
+ curr->ptr->type,
+ indexType(),
+ curr,
+ "AtomicRMW pointer type must match memory index type");
shouldBeEqualOrFirstIsUnreachable(curr->type,
curr->value->type,
curr,
@@ -1009,7 +1018,10 @@ void FunctionValidator::visitAtomicCmpxchg(AtomicCmpxchg* curr) {
"Atomic operation with non-shared memory");
validateMemBytes(curr->bytes, curr->type, curr);
shouldBeEqualOrFirstIsUnreachable(
- curr->ptr->type, Type(Type::i32), curr, "cmpxchg pointer type must be i32");
+ curr->ptr->type,
+ indexType(),
+ curr,
+ "cmpxchg pointer must match memory index type");
if (curr->expected->type != Type::unreachable &&
curr->replacement->type != Type::unreachable) {
shouldBeEqual(curr->expected->type,
@@ -1042,10 +1054,11 @@ void FunctionValidator::visitAtomicWait(AtomicWait* curr) {
"Atomic operation with non-shared memory");
shouldBeEqualOrFirstIsUnreachable(
curr->type, Type(Type::i32), curr, "AtomicWait must have type i32");
- shouldBeEqualOrFirstIsUnreachable(curr->ptr->type,
- Type(Type::i32),
- curr,
- "AtomicWait pointer type must be i32");
+ shouldBeEqualOrFirstIsUnreachable(
+ curr->ptr->type,
+ indexType(),
+ curr,
+ "AtomicWait pointer must match memory index type");
shouldBeIntOrUnreachable(
curr->expected->type, curr, "AtomicWait expected type must be int");
shouldBeEqualOrFirstIsUnreachable(
@@ -1070,10 +1083,11 @@ void FunctionValidator::visitAtomicNotify(AtomicNotify* curr) {
"Atomic operation with non-shared memory");
shouldBeEqualOrFirstIsUnreachable(
curr->type, Type(Type::i32), curr, "AtomicNotify must have type i32");
- shouldBeEqualOrFirstIsUnreachable(curr->ptr->type,
- Type(Type::i32),
- curr,
- "AtomicNotify pointer type must be i32");
+ shouldBeEqualOrFirstIsUnreachable(
+ curr->ptr->type,
+ indexType(),
+ curr,
+ "AtomicNotify pointer must match memory index type");
shouldBeEqualOrFirstIsUnreachable(
curr->notifyCount->type,
Type(Type::i32),
@@ -1230,10 +1244,11 @@ void FunctionValidator::visitSIMDLoad(SIMDLoad* curr) {
getModule()->features.hasSIMD(), curr, "SIMD operation (SIMD is disabled)");
shouldBeEqualOrFirstIsUnreachable(
curr->type, Type(Type::v128), curr, "load_splat must have type v128");
- shouldBeEqualOrFirstIsUnreachable(curr->ptr->type,
- Type(Type::i32),
- curr,
- "load_splat address must have type i32");
+ shouldBeEqualOrFirstIsUnreachable(
+ curr->ptr->type,
+ indexType(),
+ curr,
+ "load_splat address must match memory index type");
Type memAlignType = Type::none;
switch (curr->op) {
case LoadSplatVec8x16:
@@ -1264,7 +1279,10 @@ void FunctionValidator::visitMemoryInit(MemoryInit* curr) {
shouldBeEqualOrFirstIsUnreachable(
curr->type, Type(Type::none), curr, "memory.init must have type none");
shouldBeEqualOrFirstIsUnreachable(
- curr->dest->type, Type(Type::i32), curr, "memory.init dest must be an i32");
+ curr->dest->type,
+ indexType(),
+ curr,
+ "memory.init dest must match memory index type");
shouldBeEqualOrFirstIsUnreachable(curr->offset->type,
Type(Type::i32),
curr,
@@ -1304,13 +1322,20 @@ void FunctionValidator::visitMemoryCopy(MemoryCopy* curr) {
shouldBeEqualOrFirstIsUnreachable(
curr->type, Type(Type::none), curr, "memory.copy must have type none");
shouldBeEqualOrFirstIsUnreachable(
- curr->dest->type, Type(Type::i32), curr, "memory.copy dest must be an i32");
- shouldBeEqualOrFirstIsUnreachable(curr->source->type,
- Type(Type::i32),
- curr,
- "memory.copy source must be an i32");
+ curr->dest->type,
+ indexType(),
+ curr,
+ "memory.copy dest must match memory index type");
+ shouldBeEqualOrFirstIsUnreachable(
+ curr->source->type,
+ indexType(),
+ curr,
+ "memory.copy source must match memory index type");
shouldBeEqualOrFirstIsUnreachable(
- curr->size->type, Type(Type::i32), curr, "memory.copy size must be an i32");
+ curr->size->type,
+ indexType(),
+ curr,
+ "memory.copy size must match memory index type");
shouldBeTrue(
getModule()->memory.exists, curr, "Memory operations require a memory");
}
@@ -1322,13 +1347,19 @@ void FunctionValidator::visitMemoryFill(MemoryFill* curr) {
shouldBeEqualOrFirstIsUnreachable(
curr->type, Type(Type::none), curr, "memory.fill must have type none");
shouldBeEqualOrFirstIsUnreachable(
- curr->dest->type, Type(Type::i32), curr, "memory.fill dest must be an i32");
+ curr->dest->type,
+ indexType(),
+ curr,
+ "memory.fill dest must match memory index type");
shouldBeEqualOrFirstIsUnreachable(curr->value->type,
Type(Type::i32),
curr,
"memory.fill value must be an i32");
shouldBeEqualOrFirstIsUnreachable(
- curr->size->type, Type(Type::i32), curr, "memory.fill size must be an i32");
+ curr->size->type,
+ indexType(),
+ curr,
+ "memory.fill size must match memory index type");
shouldBeTrue(
getModule()->memory.exists, curr, "Memory operations require a memory");
}
@@ -1907,9 +1938,9 @@ void FunctionValidator::visitMemoryGrow(MemoryGrow* curr) {
shouldBeTrue(
getModule()->memory.exists, curr, "Memory operations require a memory");
shouldBeEqualOrFirstIsUnreachable(curr->delta->type,
- Type(Type::i32),
+ indexType(),
curr,
- "memory.grow must have i32 operand");
+ "memory.grow must match memory index type");
}
void FunctionValidator::visitRefIsNull(RefIsNull* curr) {
@@ -2113,7 +2144,7 @@ void FunctionValidator::visitFunction(Function* curr) {
}
}
-static bool checkOffset(Expression* curr, Address add, Address max) {
+static bool checkSegmentOffset(Expression* curr, Address add, Address max) {
if (curr->is<GlobalGet>()) {
return true;
}
@@ -2122,10 +2153,10 @@ static bool checkOffset(Expression* curr, Address add, Address max) {
return false;
}
uint64_t raw = c->value.getInteger();
- if (raw > std::numeric_limits<Address::address_t>::max()) {
+ if (raw > std::numeric_limits<Address::address32_t>::max()) {
return false;
}
- if (raw + uint64_t(add) > std::numeric_limits<Address::address_t>::max()) {
+ if (raw + uint64_t(add) > std::numeric_limits<Address::address32_t>::max()) {
return false;
}
Address offset = raw;
@@ -2354,12 +2385,14 @@ 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");
+ if (!curr.is64()) {
+ info.shouldBeTrue(curr.initial <= Memory::kMaxSize32,
+ "memory",
+ "initial memory must be <= 4GB");
+ info.shouldBeTrue(!curr.hasMax() || curr.max <= Memory::kMaxSize32,
+ "memory",
+ "max memory must be <= 4GB, or unlimited");
+ }
info.shouldBeTrue(!curr.shared || curr.hasMax(),
"memory",
"shared memory must have max size");
@@ -2385,9 +2418,9 @@ static void validateMemory(Module& module, ValidationInfo& info) {
"segment offset should be i32")) {
continue;
}
- info.shouldBeTrue(checkOffset(segment.offset,
- segment.data.size(),
- curr.initial * Memory::kPageSize),
+ info.shouldBeTrue(checkSegmentOffset(segment.offset,
+ segment.data.size(),
+ curr.initial * Memory::kPageSize),
segment.offset,
"segment offset should be reasonable");
if (segment.offset->is<Const>()) {
@@ -2416,11 +2449,12 @@ static void validateTable(Module& module, ValidationInfo& info) {
Type(Type::i32),
segment.offset,
"segment offset should be i32");
- info.shouldBeTrue(checkOffset(segment.offset,
- segment.data.size(),
- module.table.initial * Table::kPageSize),
- segment.offset,
- "segment offset should be reasonable");
+ info.shouldBeTrue(
+ checkSegmentOffset(segment.offset,
+ segment.data.size(),
+ module.table.initial * Table::kPageSize),
+ segment.offset,
+ "segment offset should be reasonable");
for (auto name : segment.data) {
info.shouldBeTrue(
module.getFunctionOrNull(name), name, "segment name should be valid");