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.cpp1091
1 files changed, 792 insertions, 299 deletions
diff --git a/src/wasm/wasm-validator.cpp b/src/wasm/wasm-validator.cpp
index 1e94ead0a..ea8e92047 100644
--- a/src/wasm/wasm-validator.cpp
+++ b/src/wasm/wasm-validator.cpp
@@ -19,29 +19,30 @@
#include <sstream>
#include <unordered_set>
-#include "wasm.h"
-#include "wasm-printing.h"
-#include "wasm-validator.h"
-#include "ir/utils.h"
#include "ir/branch-utils.h"
#include "ir/features.h"
#include "ir/module-utils.h"
+#include "ir/utils.h"
#include "support/colors.h"
+#include "wasm-printing.h"
+#include "wasm-validator.h"
+#include "wasm.h"
namespace wasm {
// Print anything that can be streamed to an ostream
template<typename T,
- typename std::enable_if<
- !std::is_base_of<Expression, typename std::remove_pointer<T>::type>::value
- >::type* = nullptr>
+ typename std::enable_if<!std::is_base_of<
+ Expression,
+ typename std::remove_pointer<T>::type>::value>::type* = nullptr>
inline std::ostream& printModuleComponent(T curr, std::ostream& stream) {
stream << curr << std::endl;
return stream;
}
// Extra overload for Expressions, to print type info too
-inline std::ostream& printModuleComponent(Expression* curr, std::ostream& stream) {
+inline std::ostream& printModuleComponent(Expression* curr,
+ std::ostream& stream) {
WasmPrinter::printExpression(curr, stream, false, true) << std::endl;
return stream;
}
@@ -60,14 +61,13 @@ struct ValidationInfo {
std::mutex mutex;
std::unordered_map<Function*, std::unique_ptr<std::ostringstream>> outputs;
- ValidationInfo() {
- valid.store(true);
- }
+ ValidationInfo() { valid.store(true); }
std::ostringstream& getStream(Function* func) {
std::unique_lock<std::mutex> lock(mutex);
auto iter = outputs.find(func);
- if (iter != outputs.end()) return *(iter->second.get());
+ if (iter != outputs.end())
+ return *(iter->second.get());
auto& ret = outputs[func] = make_unique<std::ostringstream>();
return *ret.get();
}
@@ -78,7 +78,8 @@ struct ValidationInfo {
std::ostream& fail(S text, T curr, Function* func) {
valid.store(false);
auto& stream = getStream(func);
- if (quiet) return stream;
+ if (quiet)
+ return stream;
auto& ret = printFailureHeader(func);
ret << text << ", on \n";
return printModuleComponent(curr, ret);
@@ -86,7 +87,8 @@ struct ValidationInfo {
std::ostream& printFailureHeader(Function* func) {
auto& stream = getStream(func);
- if (quiet) return stream;
+ if (quiet)
+ return stream;
Colors::red(stream);
if (func) {
stream << "[wasm-validator error in function ";
@@ -104,7 +106,10 @@ struct ValidationInfo {
// checking utilities
template<typename T>
- bool shouldBeTrue(bool result, T curr, const char* text, Function* func = nullptr) {
+ bool shouldBeTrue(bool result,
+ T curr,
+ const char* text,
+ Function* func = nullptr) {
if (!result) {
fail("unexpected false: " + std::string(text), curr, func);
return false;
@@ -112,7 +117,10 @@ struct ValidationInfo {
return result;
}
template<typename T>
- bool shouldBeFalse(bool result, T curr, const char* text, Function* func = nullptr) {
+ bool shouldBeFalse(bool result,
+ T curr,
+ const char* text,
+ Function* func = nullptr) {
if (result) {
fail("unexpected true: " + std::string(text), curr, func);
return false;
@@ -121,7 +129,8 @@ struct ValidationInfo {
}
template<typename T, typename S>
- bool shouldBeEqual(S left, S right, T curr, const char* text, Function* func = nullptr) {
+ bool shouldBeEqual(
+ S left, S right, T curr, const char* text, Function* func = nullptr) {
if (left != right) {
std::ostringstream ss;
ss << left << " != " << right << ": " << text;
@@ -132,7 +141,8 @@ struct ValidationInfo {
}
template<typename T, typename S>
- bool shouldBeEqualOrFirstIsUnreachable(S left, S right, T curr, const char* text, Function* func = nullptr) {
+ bool shouldBeEqualOrFirstIsUnreachable(
+ S left, S right, T curr, const char* text, Function* func = nullptr) {
if (left != unreachable && left != right) {
std::ostringstream ss;
ss << left << " != " << right << ": " << text;
@@ -143,7 +153,8 @@ struct ValidationInfo {
}
template<typename T, typename S>
- bool shouldBeUnequal(S left, S right, T curr, const char* text, Function* func = nullptr) {
+ bool shouldBeUnequal(
+ S left, S right, T curr, const char* text, Function* func = nullptr) {
if (left == right) {
std::ostringstream ss;
ss << left << " == " << right << ": " << text;
@@ -153,17 +164,20 @@ struct ValidationInfo {
return true;
}
- void shouldBeIntOrUnreachable(Type ty, Expression* curr, const char* text, Function* func = nullptr) {
+ void shouldBeIntOrUnreachable(Type ty,
+ Expression* curr,
+ const char* text,
+ Function* func = nullptr) {
switch (ty) {
case i32:
case i64:
case unreachable: {
break;
}
- default: fail(text, curr, func);
+ default:
+ fail(text, curr, func);
}
}
-
};
struct FunctionValidator : public WalkerPass<PostWalker<FunctionValidator>> {
@@ -178,10 +192,7 @@ struct FunctionValidator : public WalkerPass<PostWalker<FunctionValidator>> {
FunctionValidator(ValidationInfo* info) : info(*info) {}
struct BreakInfo {
- enum {
- UnsetArity = Index(-1),
- PoisonArity = Index(-2)
- };
+ enum { UnsetArity = Index(-1), PoisonArity = Index(-2) };
Type type;
Index arity;
@@ -198,7 +209,9 @@ struct FunctionValidator : public WalkerPass<PostWalker<FunctionValidator>> {
Type returnType = unreachable; // type used in returns
- std::unordered_set<Name> labelNames; // Binaryen IR requires that label names must be unique - IR generators must ensure that
+ // Binaryen IR requires that label names must be unique - IR generators must
+ // ensure that
+ std::unordered_set<Name> labelNames;
void noteLabelName(Name name);
@@ -207,14 +220,16 @@ public:
static void visitPreBlock(FunctionValidator* self, Expression** currp) {
auto* curr = (*currp)->cast<Block>();
- if (curr->name.is()) self->breakInfos[curr->name];
+ if (curr->name.is())
+ self->breakInfos[curr->name];
}
void visitBlock(Block* curr);
static void visitPreLoop(FunctionValidator* self, Expression** currp) {
auto* curr = (*currp)->cast<Loop>();
- if (curr->name.is()) self->breakInfos[curr->name];
+ if (curr->name.is())
+ self->breakInfos[curr->name];
}
void visitLoop(Loop* curr);
@@ -225,8 +240,10 @@ public:
PostWalker<FunctionValidator>::scan(self, currp);
auto* curr = *currp;
- if (curr->is<Block>()) self->pushTask(visitPreBlock, currp);
- if (curr->is<Loop>()) self->pushTask(visitPreLoop, currp);
+ if (curr->is<Block>())
+ self->pushTask(visitPreBlock, currp);
+ if (curr->is<Loop>())
+ self->pushTask(visitPreLoop, currp);
}
void noteBreak(Name name, Expression* value, Expression* curr);
@@ -264,9 +281,7 @@ public:
// helpers
private:
- std::ostream& getStream() {
- return info.getStream(getFunction());
- }
+ std::ostream& getStream() { return info.getStream(getFunction()); }
template<typename T>
bool shouldBeTrue(bool result, T curr, const char* text) {
@@ -283,8 +298,10 @@ private:
}
template<typename T, typename S>
- bool shouldBeEqualOrFirstIsUnreachable(S left, S right, T curr, const char* text) {
- return info.shouldBeEqualOrFirstIsUnreachable(left, right, curr, text, getFunction());
+ bool
+ shouldBeEqualOrFirstIsUnreachable(S left, S right, T curr, const char* text) {
+ return info.shouldBeEqualOrFirstIsUnreachable(
+ left, right, curr, text, getFunction());
}
template<typename T, typename S>
@@ -296,16 +313,20 @@ private:
return info.shouldBeIntOrUnreachable(ty, curr, text, getFunction());
}
- void validateAlignment(size_t align, Type type, Index bytes, bool isAtomic,
- Expression* curr);
+ void validateAlignment(
+ size_t align, Type type, Index bytes, bool isAtomic, Expression* curr);
void validateMemBytes(uint8_t bytes, Type type, Expression* curr);
};
void FunctionValidator::noteLabelName(Name name) {
- if (!name.is()) return;
+ if (!name.is())
+ return;
bool inserted;
std::tie(std::ignore, inserted) = labelNames.insert(name);
- shouldBeTrue(inserted, name, "names in Binaryen IR must be unique - IR generators must ensure that");
+ shouldBeTrue(
+ inserted,
+ name,
+ "names in Binaryen IR must be unique - IR generators must ensure that");
}
void FunctionValidator::visitBlock(Block* curr) {
@@ -317,25 +338,46 @@ void FunctionValidator::visitBlock(Block* curr) {
auto& info = iter->second;
if (info.hasBeenSet()) {
if (isConcreteType(curr->type)) {
- shouldBeTrue(info.arity != 0, curr, "break arities must be > 0 if block has a value");
+ shouldBeTrue(info.arity != 0,
+ curr,
+ "break arities must be > 0 if block has a value");
} else {
- shouldBeTrue(info.arity == 0, curr, "break arities must be 0 if block has no value");
+ shouldBeTrue(info.arity == 0,
+ curr,
+ "break arities must be 0 if block has no value");
}
- // none or unreachable means a poison value that we should ignore - if consumed, it will error
+ // none or unreachable means a poison value that we should ignore - if
+ // consumed, it will error
if (isConcreteType(info.type) && isConcreteType(curr->type)) {
- shouldBeEqual(curr->type, info.type, curr, "block+breaks must have right type if breaks return a value");
+ shouldBeEqual(
+ curr->type,
+ info.type,
+ curr,
+ "block+breaks must have right type if breaks return a value");
}
- if (isConcreteType(curr->type) && info.arity && info.type != unreachable) {
- shouldBeEqual(curr->type, info.type, curr, "block+breaks must have right type if breaks have arity");
+ if (isConcreteType(curr->type) && info.arity &&
+ info.type != unreachable) {
+ shouldBeEqual(curr->type,
+ info.type,
+ curr,
+ "block+breaks must have right type if breaks have arity");
}
- shouldBeTrue(info.arity != BreakInfo::PoisonArity, curr, "break arities must match");
+ shouldBeTrue(
+ info.arity != BreakInfo::PoisonArity, curr, "break arities must match");
if (curr->list.size() > 0) {
auto last = curr->list.back()->type;
if (isConcreteType(last) && info.type != unreachable) {
- shouldBeEqual(last, info.type, curr, "block+breaks must have right type if block ends with a reachable value");
+ shouldBeEqual(last,
+ info.type,
+ curr,
+ "block+breaks must have right type if block ends with "
+ "a reachable value");
}
if (last == none) {
- shouldBeTrue(info.arity == Index(0), curr, "if block ends with a none, breaks cannot send a value of any type");
+ shouldBeTrue(info.arity == Index(0),
+ curr,
+ "if block ends with a none, breaks cannot send a value "
+ "of any type");
}
}
}
@@ -343,25 +385,44 @@ void FunctionValidator::visitBlock(Block* curr) {
}
if (curr->list.size() > 1) {
for (Index i = 0; i < curr->list.size() - 1; i++) {
- if (!shouldBeTrue(!isConcreteType(curr->list[i]->type), curr, "non-final block elements returning a value must be drop()ed (binaryen's autodrop option might help you)") && !info.quiet) {
- getStream() << "(on index " << i << ":\n" << curr->list[i] << "\n), type: " << curr->list[i]->type << "\n";
+ if (!shouldBeTrue(
+ !isConcreteType(curr->list[i]->type),
+ curr,
+ "non-final block elements returning a value must be drop()ed "
+ "(binaryen's autodrop option might help you)") &&
+ !info.quiet) {
+ getStream() << "(on index " << i << ":\n"
+ << curr->list[i] << "\n), type: " << curr->list[i]->type
+ << "\n";
}
}
}
if (curr->list.size() > 0) {
auto backType = curr->list.back()->type;
if (!isConcreteType(curr->type)) {
- shouldBeFalse(isConcreteType(backType), curr, "if block is not returning a value, final element should not flow out a value");
+ shouldBeFalse(isConcreteType(backType),
+ curr,
+ "if block is not returning a value, final element should "
+ "not flow out a value");
} else {
if (isConcreteType(backType)) {
- shouldBeEqual(curr->type, backType, curr, "block with value and last element with value must match types");
+ shouldBeEqual(
+ curr->type,
+ backType,
+ curr,
+ "block with value and last element with value must match types");
} else {
- shouldBeUnequal(backType, none, curr, "block with value must not have last element that is none");
+ shouldBeUnequal(
+ backType,
+ none,
+ curr,
+ "block with value must not have last element that is none");
}
}
}
if (isConcreteType(curr->type)) {
- shouldBeTrue(curr->list.size() > 0, curr, "block with a value must not be empty");
+ shouldBeTrue(
+ curr->list.size() > 0, curr, "block with a value must not be empty");
}
}
@@ -372,44 +433,84 @@ void FunctionValidator::visitLoop(Loop* curr) {
assert(iter != breakInfos.end()); // we set it ourselves
auto& info = iter->second;
if (info.hasBeenSet()) {
- shouldBeEqual(info.arity, Index(0), curr, "breaks to a loop cannot pass a value");
+ shouldBeEqual(
+ info.arity, Index(0), curr, "breaks to a loop cannot pass a value");
}
breakInfos.erase(iter);
}
if (curr->type == none) {
- shouldBeFalse(isConcreteType(curr->body->type), curr, "bad body for a loop that has no value");
+ shouldBeFalse(isConcreteType(curr->body->type),
+ curr,
+ "bad body for a loop that has no value");
}
}
void FunctionValidator::visitIf(If* curr) {
- shouldBeTrue(curr->condition->type == unreachable || curr->condition->type == i32, curr, "if condition must be valid");
+ shouldBeTrue(curr->condition->type == unreachable ||
+ curr->condition->type == i32,
+ curr,
+ "if condition must be valid");
if (!curr->ifFalse) {
- shouldBeFalse(isConcreteType(curr->ifTrue->type), curr, "if without else must not return a value in body");
+ shouldBeFalse(isConcreteType(curr->ifTrue->type),
+ curr,
+ "if without else must not return a value in body");
if (curr->condition->type != unreachable) {
- shouldBeEqual(curr->type, none, curr, "if without else and reachable condition must be none");
+ shouldBeEqual(curr->type,
+ none,
+ curr,
+ "if without else and reachable condition must be none");
}
} else {
if (curr->type != unreachable) {
- shouldBeEqualOrFirstIsUnreachable(curr->ifTrue->type, curr->type, curr, "returning if-else's true must have right type");
- shouldBeEqualOrFirstIsUnreachable(curr->ifFalse->type, curr->type, curr, "returning if-else's false must have right type");
+ shouldBeEqualOrFirstIsUnreachable(
+ curr->ifTrue->type,
+ curr->type,
+ curr,
+ "returning if-else's true must have right type");
+ shouldBeEqualOrFirstIsUnreachable(
+ curr->ifFalse->type,
+ curr->type,
+ curr,
+ "returning if-else's false must have right type");
} else {
if (curr->condition->type != unreachable) {
- shouldBeEqual(curr->ifTrue->type, unreachable, curr, "unreachable if-else must have unreachable true");
- shouldBeEqual(curr->ifFalse->type, unreachable, curr, "unreachable if-else must have unreachable false");
+ shouldBeEqual(curr->ifTrue->type,
+ unreachable,
+ curr,
+ "unreachable if-else must have unreachable true");
+ shouldBeEqual(curr->ifFalse->type,
+ unreachable,
+ curr,
+ "unreachable if-else must have unreachable false");
}
}
if (isConcreteType(curr->ifTrue->type)) {
- shouldBeEqual(curr->type, curr->ifTrue->type, curr, "if type must match concrete ifTrue");
- shouldBeEqualOrFirstIsUnreachable(curr->ifFalse->type, curr->ifTrue->type, curr, "other arm must match concrete ifTrue");
+ shouldBeEqual(curr->type,
+ curr->ifTrue->type,
+ curr,
+ "if type must match concrete ifTrue");
+ shouldBeEqualOrFirstIsUnreachable(curr->ifFalse->type,
+ curr->ifTrue->type,
+ curr,
+ "other arm must match concrete ifTrue");
}
if (isConcreteType(curr->ifFalse->type)) {
- shouldBeEqual(curr->type, curr->ifFalse->type, curr, "if type must match concrete ifFalse");
- shouldBeEqualOrFirstIsUnreachable(curr->ifTrue->type, curr->ifFalse->type, curr, "other arm must match concrete ifFalse");
+ shouldBeEqual(curr->type,
+ curr->ifFalse->type,
+ curr,
+ "if type must match concrete ifFalse");
+ shouldBeEqualOrFirstIsUnreachable(
+ curr->ifTrue->type,
+ curr->ifFalse->type,
+ curr,
+ "other arm must match concrete ifFalse");
}
}
}
-void FunctionValidator::noteBreak(Name name, Expression* value, Expression* curr) {
+void FunctionValidator::noteBreak(Name name,
+ Expression* value,
+ Expression* curr) {
Type valueType = none;
Index arity = 0;
if (value) {
@@ -418,7 +519,9 @@ void FunctionValidator::noteBreak(Name name, Expression* value, Expression* curr
arity = 1;
}
auto iter = breakInfos.find(name);
- if (!shouldBeTrue(iter != breakInfos.end(), curr, "all break targets must be valid")) return;
+ if (!shouldBeTrue(
+ iter != breakInfos.end(), curr, "all break targets must be valid"))
+ return;
auto& info = iter->second;
if (!info.hasBeenSet()) {
info = BreakInfo(valueType, arity);
@@ -438,7 +541,10 @@ void FunctionValidator::noteBreak(Name name, Expression* value, Expression* curr
void FunctionValidator::visitBreak(Break* curr) {
noteBreak(curr->name, curr->value, curr);
if (curr->condition) {
- shouldBeTrue(curr->condition->type == unreachable || curr->condition->type == i32, curr, "break condition must be i32");
+ shouldBeTrue(curr->condition->type == unreachable ||
+ curr->condition->type == i32,
+ curr,
+ "break condition must be i32");
}
}
@@ -447,260 +553,500 @@ void FunctionValidator::visitSwitch(Switch* curr) {
noteBreak(target, curr->value, curr);
}
noteBreak(curr->default_, curr->value, curr);
- shouldBeTrue(curr->condition->type == unreachable || curr->condition->type == i32, curr, "br_table condition must be i32");
+ shouldBeTrue(curr->condition->type == unreachable ||
+ curr->condition->type == i32,
+ curr,
+ "br_table condition must be i32");
}
void FunctionValidator::visitCall(Call* curr) {
- if (!info.validateGlobally) return;
+ if (!info.validateGlobally)
+ return;
auto* target = getModule()->getFunctionOrNull(curr->target);
- if (!shouldBeTrue(!!target, curr, "call target must exist")) return;
- if (!shouldBeTrue(curr->operands.size() == target->params.size(), curr, "call param number must match")) return;
+ if (!shouldBeTrue(!!target, curr, "call target must exist"))
+ return;
+ if (!shouldBeTrue(curr->operands.size() == target->params.size(),
+ curr,
+ "call param number must match"))
+ return;
for (size_t i = 0; i < curr->operands.size(); i++) {
- if (!shouldBeEqualOrFirstIsUnreachable(curr->operands[i]->type, target->params[i], curr, "call param types must match") && !info.quiet) {
+ if (!shouldBeEqualOrFirstIsUnreachable(curr->operands[i]->type,
+ target->params[i],
+ curr,
+ "call param types must match") &&
+ !info.quiet) {
getStream() << "(on argument " << i << ")\n";
}
}
}
void FunctionValidator::visitCallIndirect(CallIndirect* curr) {
- if (!info.validateGlobally) return;
+ if (!info.validateGlobally)
+ return;
auto* type = getModule()->getFunctionTypeOrNull(curr->fullType);
- if (!shouldBeTrue(!!type, curr, "call_indirect type must exist")) return;
- shouldBeEqualOrFirstIsUnreachable(curr->target->type, i32, curr, "indirect call target must be an i32");
- if (!shouldBeTrue(curr->operands.size() == type->params.size(), curr, "call param number must match")) return;
+ if (!shouldBeTrue(!!type, curr, "call_indirect type must exist"))
+ return;
+ shouldBeEqualOrFirstIsUnreachable(
+ curr->target->type, i32, curr, "indirect call target must be an i32");
+ if (!shouldBeTrue(curr->operands.size() == type->params.size(),
+ curr,
+ "call param number must match"))
+ return;
for (size_t i = 0; i < curr->operands.size(); i++) {
- if (!shouldBeEqualOrFirstIsUnreachable(curr->operands[i]->type, type->params[i], curr, "call param types must match") && !info.quiet) {
+ if (!shouldBeEqualOrFirstIsUnreachable(curr->operands[i]->type,
+ type->params[i],
+ curr,
+ "call param types must match") &&
+ !info.quiet) {
getStream() << "(on argument " << i << ")\n";
}
}
}
void FunctionValidator::visitConst(Const* curr) {
- shouldBeTrue(getFeatures(curr->type) <= getModule()->features, curr,
+ shouldBeTrue(getFeatures(curr->type) <= getModule()->features,
+ curr,
"all used features should be allowed");
}
void FunctionValidator::visitGetLocal(GetLocal* curr) {
- shouldBeTrue(curr->index < getFunction()->getNumLocals(), curr, "local.get index must be small enough");
- shouldBeTrue(isConcreteType(curr->type), curr, "local.get must have a valid type - check what you provided when you constructed the node");
- shouldBeTrue(curr->type == getFunction()->getLocalType(curr->index), curr, "local.get must have proper type");
+ shouldBeTrue(curr->index < getFunction()->getNumLocals(),
+ curr,
+ "local.get index must be small enough");
+ shouldBeTrue(isConcreteType(curr->type),
+ curr,
+ "local.get must have a valid type - check what you provided "
+ "when you constructed the node");
+ shouldBeTrue(curr->type == getFunction()->getLocalType(curr->index),
+ curr,
+ "local.get must have proper type");
}
void FunctionValidator::visitSetLocal(SetLocal* curr) {
- shouldBeTrue(curr->index < getFunction()->getNumLocals(), curr, "local.set index must be small enough");
+ shouldBeTrue(curr->index < getFunction()->getNumLocals(),
+ curr,
+ "local.set index must be small enough");
if (curr->value->type != unreachable) {
if (curr->type != none) { // tee is ok anyhow
- shouldBeEqualOrFirstIsUnreachable(curr->value->type, curr->type, curr, "local.set type must be correct");
+ shouldBeEqualOrFirstIsUnreachable(
+ curr->value->type, curr->type, curr, "local.set type must be correct");
}
- shouldBeEqual(getFunction()->getLocalType(curr->index), curr->value->type, curr, "local.set type must match function");
+ shouldBeEqual(getFunction()->getLocalType(curr->index),
+ curr->value->type,
+ curr,
+ "local.set type must match function");
}
}
void FunctionValidator::visitGetGlobal(GetGlobal* curr) {
- if (!info.validateGlobally) return;
- shouldBeTrue(getModule()->getGlobalOrNull(curr->name), curr, "global.get name must be valid");
+ if (!info.validateGlobally)
+ return;
+ shouldBeTrue(getModule()->getGlobalOrNull(curr->name),
+ curr,
+ "global.get name must be valid");
}
void FunctionValidator::visitSetGlobal(SetGlobal* curr) {
- if (!info.validateGlobally) return;
+ if (!info.validateGlobally)
+ return;
auto* global = getModule()->getGlobalOrNull(curr->name);
- if (shouldBeTrue(global, curr, "global.set name must be valid (and not an import; imports can't be modified)")) {
+ if (shouldBeTrue(global,
+ curr,
+ "global.set name must be valid (and not an import; imports "
+ "can't be modified)")) {
shouldBeTrue(global->mutable_, curr, "global.set global must be mutable");
- shouldBeEqualOrFirstIsUnreachable(curr->value->type, global->type, curr, "global.set value must have right type");
+ shouldBeEqualOrFirstIsUnreachable(curr->value->type,
+ global->type,
+ curr,
+ "global.set value must have right type");
}
}
void FunctionValidator::visitLoad(Load* curr) {
- shouldBeTrue(getModule()->memory.exists, curr, "Memory operations require a memory");
+ shouldBeTrue(
+ getModule()->memory.exists, curr, "Memory operations require a memory");
if (curr->isAtomic) {
- shouldBeTrue(getModule()->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");
- }
- if (curr->type == v128) shouldBeTrue(getModule()->features.hasSIMD(), curr, "SIMD operation (SIMD is disabled)");
- shouldBeFalse(curr->isAtomic && !getModule()->memory.shared, curr, "Atomic operation with non-shared memory");
+ shouldBeTrue(getModule()->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");
+ }
+ if (curr->type == v128)
+ shouldBeTrue(getModule()->features.hasSIMD(),
+ curr,
+ "SIMD operation (SIMD is disabled)");
+ shouldBeFalse(curr->isAtomic && !getModule()->memory.shared,
+ curr,
+ "Atomic operation with non-shared memory");
validateMemBytes(curr->bytes, curr->type, curr);
validateAlignment(curr->align, curr->type, curr->bytes, curr->isAtomic, curr);
- shouldBeEqualOrFirstIsUnreachable(curr->ptr->type, i32, curr, "load pointer type must be i32");
+ shouldBeEqualOrFirstIsUnreachable(
+ curr->ptr->type, i32, curr, "load pointer type must be i32");
if (curr->isAtomic) {
shouldBeFalse(curr->signed_, curr, "atomic loads must be unsigned");
- shouldBeIntOrUnreachable(curr->type, curr, "atomic loads must be of integers");
+ shouldBeIntOrUnreachable(
+ curr->type, curr, "atomic loads must be of integers");
}
}
void FunctionValidator::visitStore(Store* curr) {
- shouldBeTrue(getModule()->memory.exists, curr, "Memory operations require a memory");
+ shouldBeTrue(
+ getModule()->memory.exists, curr, "Memory operations require a memory");
if (curr->isAtomic) {
- shouldBeTrue(getModule()->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");
- }
- if (curr->valueType == v128) shouldBeTrue(getModule()->features.hasSIMD(), curr, "SIMD operation (SIMD is disabled)");
- shouldBeFalse(curr->isAtomic && !getModule()->memory.shared, curr, "Atomic operation with non-shared memory");
+ shouldBeTrue(getModule()->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");
+ }
+ if (curr->valueType == v128)
+ shouldBeTrue(getModule()->features.hasSIMD(),
+ curr,
+ "SIMD operation (SIMD is disabled)");
+ shouldBeFalse(curr->isAtomic && !getModule()->memory.shared,
+ curr,
+ "Atomic operation with non-shared memory");
validateMemBytes(curr->bytes, curr->valueType, curr);
- validateAlignment(curr->align, curr->valueType, curr->bytes, curr->isAtomic, curr);
- shouldBeEqualOrFirstIsUnreachable(curr->ptr->type, i32, curr, "store pointer type must be i32");
- shouldBeUnequal(curr->value->type, none, curr, "store value type must not be none");
- shouldBeEqualOrFirstIsUnreachable(curr->value->type, curr->valueType, curr, "store value type must match");
+ validateAlignment(
+ curr->align, curr->valueType, curr->bytes, curr->isAtomic, curr);
+ shouldBeEqualOrFirstIsUnreachable(
+ curr->ptr->type, i32, curr, "store pointer type must be i32");
+ shouldBeUnequal(
+ curr->value->type, none, curr, "store value type must not be none");
+ shouldBeEqualOrFirstIsUnreachable(
+ curr->value->type, curr->valueType, curr, "store value type must match");
if (curr->isAtomic) {
- shouldBeIntOrUnreachable(curr->valueType, curr, "atomic stores must be of integers");
+ shouldBeIntOrUnreachable(
+ curr->valueType, curr, "atomic stores must be of integers");
}
}
void FunctionValidator::visitAtomicRMW(AtomicRMW* curr) {
- shouldBeTrue(getModule()->memory.exists, curr, "Memory operations require a memory");
- shouldBeTrue(getModule()->features.hasAtomics(), curr, "Atomic operation (atomics are disabled)");
- shouldBeFalse(!getModule()->memory.shared, curr, "Atomic operation with non-shared memory");
+ shouldBeTrue(
+ getModule()->memory.exists, curr, "Memory operations require a memory");
+ shouldBeTrue(getModule()->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);
- shouldBeEqualOrFirstIsUnreachable(curr->ptr->type, i32, curr, "AtomicRMW pointer type must be i32");
- shouldBeEqualOrFirstIsUnreachable(curr->type, curr->value->type, curr, "AtomicRMW result type must match operand");
- shouldBeIntOrUnreachable(curr->type, curr, "Atomic operations are only valid on int types");
+ shouldBeEqualOrFirstIsUnreachable(
+ curr->ptr->type, i32, curr, "AtomicRMW pointer type must be i32");
+ shouldBeEqualOrFirstIsUnreachable(curr->type,
+ curr->value->type,
+ curr,
+ "AtomicRMW result type must match operand");
+ shouldBeIntOrUnreachable(
+ curr->type, curr, "Atomic operations are only valid on int types");
}
void FunctionValidator::visitAtomicCmpxchg(AtomicCmpxchg* curr) {
- shouldBeTrue(getModule()->memory.exists, curr, "Memory operations require a memory");
- shouldBeTrue(getModule()->features.hasAtomics(), curr, "Atomic operation (atomics are disabled)");
- shouldBeFalse(!getModule()->memory.shared, curr, "Atomic operation with non-shared memory");
+ shouldBeTrue(
+ getModule()->memory.exists, curr, "Memory operations require a memory");
+ shouldBeTrue(getModule()->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);
- shouldBeEqualOrFirstIsUnreachable(curr->ptr->type, i32, curr, "cmpxchg pointer type must be i32");
- if (curr->expected->type != unreachable && curr->replacement->type != unreachable) {
- shouldBeEqual(curr->expected->type, curr->replacement->type, curr, "cmpxchg operand types must match");
- }
- shouldBeEqualOrFirstIsUnreachable(curr->type, curr->expected->type, curr, "Cmpxchg result type must match expected");
- shouldBeEqualOrFirstIsUnreachable(curr->type, curr->replacement->type, curr, "Cmpxchg result type must match replacement");
- shouldBeIntOrUnreachable(curr->expected->type, curr, "Atomic operations are only valid on int types");
+ shouldBeEqualOrFirstIsUnreachable(
+ curr->ptr->type, i32, curr, "cmpxchg pointer type must be i32");
+ if (curr->expected->type != unreachable &&
+ curr->replacement->type != unreachable) {
+ shouldBeEqual(curr->expected->type,
+ curr->replacement->type,
+ curr,
+ "cmpxchg operand types must match");
+ }
+ shouldBeEqualOrFirstIsUnreachable(curr->type,
+ curr->expected->type,
+ curr,
+ "Cmpxchg result type must match expected");
+ shouldBeEqualOrFirstIsUnreachable(
+ curr->type,
+ curr->replacement->type,
+ curr,
+ "Cmpxchg result type must match replacement");
+ shouldBeIntOrUnreachable(curr->expected->type,
+ curr,
+ "Atomic operations are only valid on int types");
}
void FunctionValidator::visitAtomicWait(AtomicWait* curr) {
- shouldBeTrue(getModule()->memory.exists, curr, "Memory operations require a memory");
- shouldBeTrue(getModule()->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");
- shouldBeEqualOrFirstIsUnreachable(curr->ptr->type, i32, curr, "AtomicWait pointer type must be i32");
- shouldBeIntOrUnreachable(curr->expected->type, curr, "AtomicWait expected type must be int");
- shouldBeEqualOrFirstIsUnreachable(curr->expected->type, curr->expectedType, curr, "AtomicWait expected type must match operand");
- shouldBeEqualOrFirstIsUnreachable(curr->timeout->type, i64, curr, "AtomicWait timeout type must be i64");
+ shouldBeTrue(
+ getModule()->memory.exists, curr, "Memory operations require a memory");
+ shouldBeTrue(getModule()->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");
+ shouldBeEqualOrFirstIsUnreachable(
+ curr->ptr->type, i32, curr, "AtomicWait pointer type must be i32");
+ shouldBeIntOrUnreachable(
+ curr->expected->type, curr, "AtomicWait expected type must be int");
+ shouldBeEqualOrFirstIsUnreachable(
+ curr->expected->type,
+ curr->expectedType,
+ curr,
+ "AtomicWait expected type must match operand");
+ shouldBeEqualOrFirstIsUnreachable(
+ curr->timeout->type, i64, curr, "AtomicWait timeout type must be i64");
}
void FunctionValidator::visitAtomicNotify(AtomicNotify* curr) {
- shouldBeTrue(getModule()->memory.exists, curr, "Memory operations require a memory");
- shouldBeTrue(getModule()->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");
- shouldBeEqualOrFirstIsUnreachable(curr->ptr->type, i32, curr, "AtomicNotify pointer type must be i32");
- shouldBeEqualOrFirstIsUnreachable(curr->notifyCount->type, i32, curr, "AtomicNotify notifyCount type must be i32");
+ shouldBeTrue(
+ getModule()->memory.exists, curr, "Memory operations require a memory");
+ shouldBeTrue(getModule()->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");
+ shouldBeEqualOrFirstIsUnreachable(
+ curr->ptr->type, i32, curr, "AtomicNotify pointer type must be i32");
+ shouldBeEqualOrFirstIsUnreachable(
+ curr->notifyCount->type,
+ i32,
+ curr,
+ "AtomicNotify notifyCount type must be i32");
}
void FunctionValidator::visitSIMDExtract(SIMDExtract* curr) {
- shouldBeTrue(getModule()->features.hasSIMD(), curr, "SIMD operation (SIMD is disabled)");
- shouldBeEqualOrFirstIsUnreachable(curr->vec->type, v128, curr, "extract_lane must operate on a v128");
+ shouldBeTrue(
+ getModule()->features.hasSIMD(), curr, "SIMD operation (SIMD is disabled)");
+ shouldBeEqualOrFirstIsUnreachable(
+ curr->vec->type, v128, curr, "extract_lane must operate on a v128");
Type lane_t = none;
size_t lanes = 0;
switch (curr->op) {
case ExtractLaneSVecI8x16:
- case ExtractLaneUVecI8x16: lane_t = i32; lanes = 16; break;
+ case ExtractLaneUVecI8x16:
+ lane_t = i32;
+ lanes = 16;
+ break;
case ExtractLaneSVecI16x8:
- case ExtractLaneUVecI16x8: lane_t = i32; lanes = 8; break;
- case ExtractLaneVecI32x4: lane_t = i32; lanes = 4; break;
- case ExtractLaneVecI64x2: lane_t = i64; lanes = 2; break;
- case ExtractLaneVecF32x4: lane_t = f32; lanes = 4; break;
- case ExtractLaneVecF64x2: lane_t = f64; lanes = 2; break;
+ case ExtractLaneUVecI16x8:
+ lane_t = i32;
+ lanes = 8;
+ break;
+ case ExtractLaneVecI32x4:
+ lane_t = i32;
+ lanes = 4;
+ break;
+ case ExtractLaneVecI64x2:
+ lane_t = i64;
+ lanes = 2;
+ break;
+ case ExtractLaneVecF32x4:
+ lane_t = f32;
+ lanes = 4;
+ break;
+ case ExtractLaneVecF64x2:
+ lane_t = f64;
+ lanes = 2;
+ break;
}
- shouldBeEqualOrFirstIsUnreachable(curr->type, lane_t, curr, "extract_lane must have same type as vector lane");
+ shouldBeEqualOrFirstIsUnreachable(
+ curr->type,
+ lane_t,
+ curr,
+ "extract_lane must have same type as vector lane");
shouldBeTrue(curr->index < lanes, curr, "invalid lane index");
}
void FunctionValidator::visitSIMDReplace(SIMDReplace* curr) {
- shouldBeTrue(getModule()->features.hasSIMD(), curr, "SIMD operation (SIMD is disabled)");
- shouldBeEqualOrFirstIsUnreachable(curr->type, v128, curr, "replace_lane must have type v128");
- shouldBeEqualOrFirstIsUnreachable(curr->vec->type, v128, curr, "replace_lane must operate on a v128");
+ shouldBeTrue(
+ getModule()->features.hasSIMD(), curr, "SIMD operation (SIMD is disabled)");
+ shouldBeEqualOrFirstIsUnreachable(
+ curr->type, v128, curr, "replace_lane must have type v128");
+ shouldBeEqualOrFirstIsUnreachable(
+ curr->vec->type, v128, curr, "replace_lane must operate on a v128");
Type lane_t = none;
size_t lanes = 0;
switch (curr->op) {
- case ReplaceLaneVecI8x16: lane_t = i32; lanes = 16; break;
- case ReplaceLaneVecI16x8: lane_t = i32; lanes = 8; break;
- case ReplaceLaneVecI32x4: lane_t = i32; lanes = 4; break;
- case ReplaceLaneVecI64x2: lane_t = i64; lanes = 2; break;
- case ReplaceLaneVecF32x4: lane_t = f32; lanes = 4; break;
- case ReplaceLaneVecF64x2: lane_t = f64; lanes = 2; break;
- }
- shouldBeEqualOrFirstIsUnreachable(curr->value->type, lane_t, curr, "unexpected value type");
+ case ReplaceLaneVecI8x16:
+ lane_t = i32;
+ lanes = 16;
+ break;
+ case ReplaceLaneVecI16x8:
+ lane_t = i32;
+ lanes = 8;
+ break;
+ case ReplaceLaneVecI32x4:
+ lane_t = i32;
+ lanes = 4;
+ break;
+ case ReplaceLaneVecI64x2:
+ lane_t = i64;
+ lanes = 2;
+ break;
+ case ReplaceLaneVecF32x4:
+ lane_t = f32;
+ lanes = 4;
+ break;
+ case ReplaceLaneVecF64x2:
+ lane_t = f64;
+ lanes = 2;
+ break;
+ }
+ shouldBeEqualOrFirstIsUnreachable(
+ curr->value->type, lane_t, curr, "unexpected value type");
shouldBeTrue(curr->index < lanes, curr, "invalid lane index");
}
void FunctionValidator::visitSIMDShuffle(SIMDShuffle* curr) {
- shouldBeTrue(getModule()->features.hasSIMD(), curr, "SIMD operation (SIMD is disabled)");
- shouldBeEqualOrFirstIsUnreachable(curr->type, v128, curr, "v128.shuffle must have type v128");
- shouldBeEqualOrFirstIsUnreachable(curr->left->type, v128, curr, "expected operand of type v128");
- shouldBeEqualOrFirstIsUnreachable(curr->right->type, v128, curr, "expected operand of type v128");
+ shouldBeTrue(
+ getModule()->features.hasSIMD(), curr, "SIMD operation (SIMD is disabled)");
+ shouldBeEqualOrFirstIsUnreachable(
+ curr->type, v128, curr, "v128.shuffle must have type v128");
+ shouldBeEqualOrFirstIsUnreachable(
+ curr->left->type, v128, curr, "expected operand of type v128");
+ shouldBeEqualOrFirstIsUnreachable(
+ curr->right->type, v128, curr, "expected operand of type v128");
for (uint8_t index : curr->mask) {
shouldBeTrue(index < 32, curr, "Invalid lane index in mask");
}
}
void FunctionValidator::visitSIMDBitselect(SIMDBitselect* curr) {
- shouldBeTrue(getModule()->features.hasSIMD(), curr, "SIMD operation (SIMD is disabled)");
- shouldBeEqualOrFirstIsUnreachable(curr->type, v128, curr, "v128.bitselect must have type v128");
- shouldBeEqualOrFirstIsUnreachable(curr->left->type, v128, curr, "expected operand of type v128");
- shouldBeEqualOrFirstIsUnreachable(curr->right->type, v128, curr, "expected operand of type v128");
- shouldBeEqualOrFirstIsUnreachable(curr->cond->type, v128, curr, "expected operand of type v128");
+ shouldBeTrue(
+ getModule()->features.hasSIMD(), curr, "SIMD operation (SIMD is disabled)");
+ shouldBeEqualOrFirstIsUnreachable(
+ curr->type, v128, curr, "v128.bitselect must have type v128");
+ shouldBeEqualOrFirstIsUnreachable(
+ curr->left->type, v128, curr, "expected operand of type v128");
+ shouldBeEqualOrFirstIsUnreachable(
+ curr->right->type, v128, curr, "expected operand of type v128");
+ shouldBeEqualOrFirstIsUnreachable(
+ curr->cond->type, v128, curr, "expected operand of type v128");
}
void FunctionValidator::visitSIMDShift(SIMDShift* curr) {
- shouldBeTrue(getModule()->features.hasSIMD(), curr, "SIMD operation (SIMD is disabled)");
- shouldBeEqualOrFirstIsUnreachable(curr->type, v128, curr, "vector shift must have type v128");
- shouldBeEqualOrFirstIsUnreachable(curr->vec->type, v128, curr, "expected operand of type v128");
- shouldBeEqualOrFirstIsUnreachable(curr->shift->type, i32, curr, "expected shift amount to have type i32");
+ shouldBeTrue(
+ getModule()->features.hasSIMD(), curr, "SIMD operation (SIMD is disabled)");
+ shouldBeEqualOrFirstIsUnreachable(
+ curr->type, v128, curr, "vector shift must have type v128");
+ shouldBeEqualOrFirstIsUnreachable(
+ curr->vec->type, v128, curr, "expected operand of type v128");
+ shouldBeEqualOrFirstIsUnreachable(
+ curr->shift->type, i32, curr, "expected shift amount to have type i32");
}
void FunctionValidator::visitMemoryInit(MemoryInit* curr) {
- shouldBeTrue(getModule()->memory.exists, curr, "Memory operations require a memory");
- shouldBeTrue(getModule()->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");
- shouldBeEqualOrFirstIsUnreachable(curr->offset->type, i32, curr, "memory.init offset must be an i32");
- shouldBeEqualOrFirstIsUnreachable(curr->size->type, i32, curr, "memory.init size must be an i32");
- shouldBeTrue(curr->segment < getModule()->memory.segments.size(), curr, "memory.init segment index out of bounds");
+ shouldBeTrue(
+ getModule()->memory.exists, curr, "Memory operations require a memory");
+ shouldBeTrue(getModule()->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");
+ shouldBeEqualOrFirstIsUnreachable(
+ curr->offset->type, i32, curr, "memory.init offset must be an i32");
+ shouldBeEqualOrFirstIsUnreachable(
+ curr->size->type, i32, curr, "memory.init size must be an i32");
+ shouldBeTrue(curr->segment < getModule()->memory.segments.size(),
+ curr,
+ "memory.init segment index out of bounds");
}
void FunctionValidator::visitDataDrop(DataDrop* curr) {
- shouldBeTrue(getModule()->memory.exists, curr, "Memory operations require a memory");
- shouldBeTrue(getModule()->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");
+ shouldBeTrue(
+ getModule()->memory.exists, curr, "Memory operations require a memory");
+ shouldBeTrue(getModule()->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(getModule()->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");
- shouldBeEqualOrFirstIsUnreachable(curr->source->type, i32, curr, "memory.copy source must be an i32");
- shouldBeEqualOrFirstIsUnreachable(curr->size->type, i32, curr, "memory.copy size must be an i32");
+ shouldBeTrue(
+ getModule()->memory.exists, curr, "Memory operations require a memory");
+ shouldBeTrue(getModule()->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");
+ shouldBeEqualOrFirstIsUnreachable(
+ curr->source->type, i32, curr, "memory.copy source must be an i32");
+ shouldBeEqualOrFirstIsUnreachable(
+ curr->size->type, i32, curr, "memory.copy size must be an i32");
}
void FunctionValidator::visitMemoryFill(MemoryFill* curr) {
- shouldBeTrue(getModule()->memory.exists, curr, "Memory operations require a memory");
- shouldBeTrue(getModule()->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");
- shouldBeEqualOrFirstIsUnreachable(curr->value->type, i32, curr, "memory.fill value must be an i32");
- shouldBeEqualOrFirstIsUnreachable(curr->size->type, i32, curr, "memory.fill size must be an i32");
+ shouldBeTrue(
+ getModule()->memory.exists, curr, "Memory operations require a memory");
+ shouldBeTrue(getModule()->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");
+ shouldBeEqualOrFirstIsUnreachable(
+ curr->value->type, i32, curr, "memory.fill value must be an i32");
+ shouldBeEqualOrFirstIsUnreachable(
+ curr->size->type, i32, curr, "memory.fill size must be an i32");
}
-void FunctionValidator::validateMemBytes(uint8_t bytes, Type type, Expression* curr) {
+void FunctionValidator::validateMemBytes(uint8_t bytes,
+ Type type,
+ Expression* curr) {
switch (type) {
- case i32: shouldBeTrue(bytes == 1 || bytes == 2 || bytes == 4, curr, "expected i32 operation to touch 1, 2, or 4 bytes"); break;
- case i64: shouldBeTrue(bytes == 1 || bytes == 2 || bytes == 4 || bytes == 8, curr, "expected i64 operation to touch 1, 2, 4, or 8 bytes"); break;
- case f32: shouldBeEqual(bytes, uint8_t(4), curr, "expected f32 operation to touch 4 bytes"); break;
- case f64: shouldBeEqual(bytes, uint8_t(8), curr, "expected f64 operation to touch 8 bytes"); break;
- case v128: shouldBeEqual(bytes, uint8_t(16), curr, "expected v128 operation to touch 16 bytes"); break;
- case none: WASM_UNREACHABLE();
- case unreachable: break;
+ case i32:
+ shouldBeTrue(bytes == 1 || bytes == 2 || bytes == 4,
+ curr,
+ "expected i32 operation to touch 1, 2, or 4 bytes");
+ break;
+ case i64:
+ shouldBeTrue(bytes == 1 || bytes == 2 || bytes == 4 || bytes == 8,
+ curr,
+ "expected i64 operation to touch 1, 2, 4, or 8 bytes");
+ break;
+ case f32:
+ shouldBeEqual(
+ bytes, uint8_t(4), curr, "expected f32 operation to touch 4 bytes");
+ break;
+ case f64:
+ shouldBeEqual(
+ bytes, uint8_t(8), curr, "expected f64 operation to touch 8 bytes");
+ break;
+ case v128:
+ shouldBeEqual(
+ bytes, uint8_t(16), curr, "expected v128 operation to touch 16 bytes");
+ break;
+ case none:
+ WASM_UNREACHABLE();
+ case unreachable:
+ break;
}
}
void FunctionValidator::visitBinary(Binary* curr) {
if (curr->left->type != unreachable && curr->right->type != unreachable) {
- shouldBeEqual(curr->left->type, curr->right->type, curr, "binary child types must be equal");
+ shouldBeEqual(curr->left->type,
+ curr->right->type,
+ curr,
+ "binary child types must be equal");
}
switch (curr->op) {
case AddInt32:
@@ -866,30 +1212,41 @@ void FunctionValidator::visitBinary(Binary* curr) {
case MulVecF64x2:
case DivVecF64x2:
case MinVecF64x2:
- case MaxVecF64x2: {
- shouldBeEqualOrFirstIsUnreachable(curr->left->type, v128, curr, "v128 op");
- shouldBeEqualOrFirstIsUnreachable(curr->right->type, v128, curr, "v128 op");
+ case MaxVecF64x2: {
+ shouldBeEqualOrFirstIsUnreachable(
+ curr->left->type, v128, curr, "v128 op");
+ shouldBeEqualOrFirstIsUnreachable(
+ curr->right->type, v128, curr, "v128 op");
break;
}
- case InvalidBinary: WASM_UNREACHABLE();
+ case InvalidBinary:
+ WASM_UNREACHABLE();
}
- shouldBeTrue(Features::get(curr->op) <= getModule()->features, curr, "all used features should be allowed");
+ shouldBeTrue(Features::get(curr->op) <= getModule()->features,
+ curr,
+ "all used features should be allowed");
}
void FunctionValidator::visitUnary(Unary* curr) {
- shouldBeUnequal(curr->value->type, none, curr, "unaries must not receive a none as their input");
- if (curr->value->type == unreachable) return; // nothing to check
+ shouldBeUnequal(curr->value->type,
+ none,
+ curr,
+ "unaries must not receive a none as their input");
+ if (curr->value->type == unreachable)
+ return; // nothing to check
switch (curr->op) {
case ClzInt32:
case CtzInt32:
case PopcntInt32: {
- shouldBeEqual(curr->value->type, i32, curr, "i32 unary value type must be correct");
+ shouldBeEqual(
+ curr->value->type, i32, curr, "i32 unary value type must be correct");
break;
}
case ClzInt64:
case CtzInt64:
case PopcntInt64: {
- shouldBeEqual(curr->value->type, i64, curr, "i64 unary value type must be correct");
+ shouldBeEqual(
+ curr->value->type, i64, curr, "i64 unary value type must be correct");
break;
}
case NegFloat32:
@@ -899,7 +1256,8 @@ void FunctionValidator::visitUnary(Unary* curr) {
case TruncFloat32:
case NearestFloat32:
case SqrtFloat32: {
- shouldBeEqual(curr->value->type, f32, curr, "f32 unary value type must be correct");
+ shouldBeEqual(
+ curr->value->type, f32, curr, "f32 unary value type must be correct");
break;
}
case NegFloat64:
@@ -909,7 +1267,8 @@ void FunctionValidator::visitUnary(Unary* curr) {
case TruncFloat64:
case NearestFloat64:
case SqrtFloat64: {
- shouldBeEqual(curr->value->type, f64, curr, "f64 unary value type must be correct");
+ shouldBeEqual(
+ curr->value->type, f64, curr, "f64 unary value type must be correct");
break;
}
case EqZInt32: {
@@ -924,13 +1283,15 @@ void FunctionValidator::visitUnary(Unary* curr) {
case ExtendUInt32:
case ExtendS8Int32:
case ExtendS16Int32: {
- shouldBeEqual(curr->value->type, i32, curr, "extend type must be correct");
+ shouldBeEqual(
+ curr->value->type, i32, curr, "extend type must be correct");
break;
}
case ExtendS8Int64:
case ExtendS16Int64:
case ExtendS32Int64: {
- shouldBeEqual(curr->value->type, i64, curr, "extend type must be correct");
+ shouldBeEqual(
+ curr->value->type, i64, curr, "extend type must be correct");
break;
}
case WrapInt64: {
@@ -966,41 +1327,49 @@ void FunctionValidator::visitUnary(Unary* curr) {
break;
}
case ReinterpretFloat32: {
- shouldBeEqual(curr->value->type, f32, curr, "reinterpret/f32 type must be correct");
+ shouldBeEqual(
+ curr->value->type, f32, curr, "reinterpret/f32 type must be correct");
break;
}
case ReinterpretFloat64: {
- shouldBeEqual(curr->value->type, f64, curr, "reinterpret/f64 type must be correct");
+ shouldBeEqual(
+ curr->value->type, f64, curr, "reinterpret/f64 type must be correct");
break;
}
case ConvertUInt32ToFloat32:
case ConvertUInt32ToFloat64:
case ConvertSInt32ToFloat32:
case ConvertSInt32ToFloat64: {
- shouldBeEqual(curr->value->type, i32, curr, "convert type must be correct");
+ shouldBeEqual(
+ curr->value->type, i32, curr, "convert type must be correct");
break;
}
case ConvertUInt64ToFloat32:
case ConvertUInt64ToFloat64:
case ConvertSInt64ToFloat32:
case ConvertSInt64ToFloat64: {
- shouldBeEqual(curr->value->type, i64, curr, "convert type must be correct");
+ shouldBeEqual(
+ curr->value->type, i64, curr, "convert type must be correct");
break;
}
case PromoteFloat32: {
- shouldBeEqual(curr->value->type, f32, curr, "promote type must be correct");
+ shouldBeEqual(
+ curr->value->type, f32, curr, "promote type must be correct");
break;
}
case DemoteFloat64: {
- shouldBeEqual(curr->value->type, f64, curr, "demote type must be correct");
+ shouldBeEqual(
+ curr->value->type, f64, curr, "demote type must be correct");
break;
}
case ReinterpretInt32: {
- shouldBeEqual(curr->value->type, i32, curr, "reinterpret/i32 type must be correct");
+ shouldBeEqual(
+ curr->value->type, i32, curr, "reinterpret/i32 type must be correct");
break;
}
case ReinterpretInt64: {
- shouldBeEqual(curr->value->type, i64, curr, "reinterpret/i64 type must be correct");
+ shouldBeEqual(
+ curr->value->type, i64, curr, "reinterpret/i64 type must be correct");
break;
}
case SplatVecI8x16:
@@ -1051,25 +1420,39 @@ void FunctionValidator::visitUnary(Unary* curr) {
case AllTrueVecI32x4:
case AnyTrueVecI64x2:
case AllTrueVecI64x2:
- shouldBeEqual(curr->type, i32, curr, "expected boolean reduction to have i32 type");
+ shouldBeEqual(
+ curr->type, i32, curr, "expected boolean reduction to have i32 type");
shouldBeEqual(curr->value->type, v128, curr, "expected v128 operand");
break;
- case InvalidUnary: WASM_UNREACHABLE();
+ case InvalidUnary:
+ WASM_UNREACHABLE();
}
- shouldBeTrue(Features::get(curr->op) <= getModule()->features, curr, "all used features should be allowed");
+ shouldBeTrue(Features::get(curr->op) <= getModule()->features,
+ curr,
+ "all used features should be allowed");
}
void FunctionValidator::visitSelect(Select* curr) {
shouldBeUnequal(curr->ifTrue->type, none, curr, "select left must be valid");
- shouldBeUnequal(curr->ifFalse->type, none, curr, "select right must be valid");
- shouldBeTrue(curr->condition->type == unreachable || curr->condition->type == i32, curr, "select condition must be valid");
+ shouldBeUnequal(
+ curr->ifFalse->type, none, curr, "select right must be valid");
+ shouldBeTrue(curr->condition->type == unreachable ||
+ curr->condition->type == i32,
+ curr,
+ "select condition must be valid");
if (curr->ifTrue->type != unreachable && curr->ifFalse->type != unreachable) {
- shouldBeEqual(curr->ifTrue->type, curr->ifFalse->type, curr, "select sides must be equal");
+ shouldBeEqual(curr->ifTrue->type,
+ curr->ifFalse->type,
+ curr,
+ "select sides must be equal");
}
}
void FunctionValidator::visitDrop(Drop* curr) {
- shouldBeTrue(isConcreteType(curr->value->type) || curr->value->type == unreachable, curr, "can only drop a valid value");
+ shouldBeTrue(isConcreteType(curr->value->type) ||
+ curr->value->type == unreachable,
+ curr,
+ "can only drop a valid value");
}
void FunctionValidator::visitReturn(Return* curr) {
@@ -1077,7 +1460,8 @@ void FunctionValidator::visitReturn(Return* curr) {
if (returnType == unreachable) {
returnType = curr->value->type;
} else if (curr->value->type != unreachable) {
- shouldBeEqual(curr->value->type, returnType, curr, "function results must match");
+ shouldBeEqual(
+ curr->value->type, returnType, curr, "function results must match");
}
} else {
returnType = none;
@@ -1087,11 +1471,18 @@ void FunctionValidator::visitReturn(Return* curr) {
void FunctionValidator::visitHost(Host* curr) {
switch (curr->op) {
case GrowMemory: {
- shouldBeEqual(curr->operands.size(), size_t(1), curr, "grow_memory must have 1 operand");
- shouldBeEqualOrFirstIsUnreachable(curr->operands[0]->type, i32, curr, "grow_memory must have i32 operand");
+ shouldBeEqual(curr->operands.size(),
+ size_t(1),
+ curr,
+ "grow_memory must have 1 operand");
+ shouldBeEqualOrFirstIsUnreachable(curr->operands[0]->type,
+ i32,
+ curr,
+ "grow_memory must have i32 operand");
break;
}
- case CurrentMemory: break;
+ case CurrentMemory:
+ break;
}
}
@@ -1105,27 +1496,42 @@ void FunctionValidator::visitFunction(Function* curr) {
typeFeatures |= getFeatures(type);
shouldBeTrue(isConcreteType(type), curr, "vars must be concretely typed");
}
- shouldBeTrue(typeFeatures <= getModule()->features, curr,
+ shouldBeTrue(typeFeatures <= getModule()->features,
+ curr,
"all used types should be allowed");
// if function has no result, it is ignored
// if body is unreachable, it might be e.g. a return
if (curr->body->type != unreachable) {
- shouldBeEqual(curr->result, curr->body->type, curr->body, "function body type must match, if function returns");
+ shouldBeEqual(curr->result,
+ curr->body->type,
+ curr->body,
+ "function body type must match, if function returns");
}
if (returnType != unreachable) {
- shouldBeEqual(curr->result, returnType, curr->body, "function result must match, if function has returns");
+ shouldBeEqual(curr->result,
+ returnType,
+ curr->body,
+ "function result must match, if function has returns");
}
- shouldBeTrue(breakInfos.empty(), curr->body, "all named break targets must exist");
+ shouldBeTrue(
+ breakInfos.empty(), curr->body, "all named break targets must exist");
returnType = unreachable;
labelNames.clear();
- // if function has a named type, it must match up with the function's params and result
+ // if function has a named type, it must match up with the function's params
+ // and result
if (info.validateGlobally && curr->type.is()) {
auto* ft = getModule()->getFunctionType(curr->type);
- shouldBeTrue(ft->params == curr->params, curr->name, "function params must match its declared type");
- shouldBeTrue(ft->result == curr->result, curr->name, "function result must match its declared type");
+ shouldBeTrue(ft->params == curr->params,
+ curr->name,
+ "function params must match its declared type");
+ shouldBeTrue(ft->result == curr->result,
+ curr->name,
+ "function result must match its declared type");
}
if (curr->imported()) {
- shouldBeTrue(curr->type.is(), curr->name, "imported functions must have a function type");
+ shouldBeTrue(curr->type.is(),
+ curr->name,
+ "imported functions must have a function type");
}
// validate optional local names
std::set<Name> seen;
@@ -1136,9 +1542,11 @@ void FunctionValidator::visitFunction(Function* curr) {
}
static bool checkOffset(Expression* curr, Address add, Address max) {
- if (curr->is<GetGlobal>()) return true;
+ if (curr->is<GetGlobal>())
+ return true;
auto* c = curr->dynCast<Const>();
- if (!c) return false;
+ if (!c)
+ return false;
uint64_t raw = c->value.getInteger();
if (raw > std::numeric_limits<Address::address_t>::max()) {
return false;
@@ -1150,10 +1558,13 @@ static bool checkOffset(Expression* curr, Address add, Address max) {
return offset + add <= max;
}
-void FunctionValidator::validateAlignment(size_t align, Type type, Index bytes,
- bool isAtomic, Expression* curr) {
+void FunctionValidator::validateAlignment(
+ size_t align, Type type, Index bytes, bool isAtomic, Expression* curr) {
if (isAtomic) {
- shouldBeEqual(align, (size_t)bytes, curr, "atomic accesses must have natural alignment");
+ shouldBeEqual(align,
+ (size_t)bytes,
+ curr,
+ "atomic accesses must have natural alignment");
return;
}
switch (align) {
@@ -1161,8 +1572,9 @@ void FunctionValidator::validateAlignment(size_t align, Type type, Index bytes,
case 2:
case 4:
case 8:
- case 16: break;
- default:{
+ case 16:
+ break;
+ default: {
info.fail("bad alignment: " + std::to_string(align), curr, getFunction());
break;
}
@@ -1180,13 +1592,17 @@ void FunctionValidator::validateAlignment(size_t align, Type type, Index bytes,
break;
}
case v128:
- case unreachable: break;
- case none: WASM_UNREACHABLE();
+ case unreachable:
+ break;
+ case none:
+ WASM_UNREACHABLE();
}
}
static void validateBinaryenIR(Module& wasm, ValidationInfo& info) {
- struct BinaryenIRValidator : public PostWalker<BinaryenIRValidator, UnifiedExpressionVisitor<BinaryenIRValidator>> {
+ struct BinaryenIRValidator
+ : public PostWalker<BinaryenIRValidator,
+ UnifiedExpressionVisitor<BinaryenIRValidator>> {
ValidationInfo& info;
std::unordered_set<Expression*> seen;
@@ -1195,7 +1611,8 @@ static void validateBinaryenIR(Module& wasm, ValidationInfo& info) {
void visitExpression(Expression* curr) {
auto scope = getFunction() ? getFunction()->name : Name("(global scope)");
- // check if a node type is 'stale', i.e., we forgot to finalize() the node.
+ // check if a node type is 'stale', i.e., we forgot to finalize() the
+ // node.
auto oldType = curr->type;
ReFinalizeNode().visit(curr);
auto newType = curr->type;
@@ -1205,21 +1622,25 @@ static void validateBinaryenIR(Module& wasm, ValidationInfo& info) {
//
// (drop (block (result i32) (unreachable)))
//
- // The block has an added type, not derived from the ast itself, so it is
- // ok for it to be either i32 or unreachable.
+ // The block has an added type, not derived from the ast itself, so it
+ // is ok for it to be either i32 or unreachable.
if (!(isConcreteType(oldType) && newType == unreachable)) {
std::ostringstream ss;
- ss << "stale type found in " << scope << " on " << curr << "\n(marked as " << printType(oldType) << ", should be " << printType(newType) << ")\n";
+ ss << "stale type found in " << scope << " on " << curr
+ << "\n(marked as " << printType(oldType) << ", should be "
+ << printType(newType) << ")\n";
info.fail(ss.str(), curr, getFunction());
}
curr->type = oldType;
}
- // check if a node is a duplicate - expressions must not be seen more than once
+ // check if a node is a duplicate - expressions must not be seen more than
+ // once
bool inserted;
std::tie(std::ignore, inserted) = seen.insert(curr);
if (!inserted) {
std::ostringstream ss;
- ss << "expression seen more than once in the tree in " << scope << " on " << curr << '\n';
+ ss << "expression seen more than once in the tree in " << scope
+ << " on " << curr << '\n';
info.fail(ss.str(), curr, getFunction());
}
}
@@ -1234,15 +1655,22 @@ static void validateImports(Module& module, ValidationInfo& info) {
ModuleUtils::iterImportedFunctions(module, [&](Function* curr) {
if (info.validateWeb) {
auto* functionType = module.getFunctionType(curr->type);
- info.shouldBeUnequal(functionType->result, i64, curr->name, "Imported function must not have i64 return type");
+ info.shouldBeUnequal(functionType->result,
+ i64,
+ curr->name,
+ "Imported function must not have i64 return type");
for (Type param : functionType->params) {
- info.shouldBeUnequal(param, i64, curr->name, "Imported function must not have i64 parameters");
+ info.shouldBeUnequal(param,
+ i64,
+ curr->name,
+ "Imported function must not have i64 parameters");
}
}
});
if (!module.features.hasMutableGlobals()) {
ModuleUtils::iterImportedGlobals(module, [&](Global* curr) {
- info.shouldBeFalse(curr->mutable_, curr->name, "Imported global cannot be mutable");
+ info.shouldBeFalse(
+ curr->mutable_, curr->name, "Imported global cannot be mutable");
});
}
}
@@ -1252,14 +1680,23 @@ static void validateExports(Module& module, ValidationInfo& info) {
if (curr->kind == ExternalKind::Function) {
if (info.validateWeb) {
Function* f = module.getFunction(curr->value);
- info.shouldBeUnequal(f->result, i64, f->name, "Exported function must not have i64 return type");
+ info.shouldBeUnequal(f->result,
+ i64,
+ f->name,
+ "Exported function must not have i64 return type");
for (auto param : f->params) {
- info.shouldBeUnequal(param, i64, f->name, "Exported function must not have i64 parameters");
+ info.shouldBeUnequal(
+ param,
+ i64,
+ f->name,
+ "Exported function must not have i64 parameters");
}
}
- } else if (curr->kind == ExternalKind::Global && !module.features.hasMutableGlobals()) {
+ } else if (curr->kind == ExternalKind::Global &&
+ !module.features.hasMutableGlobals()) {
if (Global* g = module.getGlobalOrNull(curr->value)) {
- info.shouldBeFalse(g->mutable_, g->name, "Exported global cannot be mutable");
+ info.shouldBeFalse(
+ g->mutable_, g->name, "Exported global cannot be mutable");
}
}
}
@@ -1267,29 +1704,47 @@ static void validateExports(Module& module, ValidationInfo& info) {
for (auto& exp : module.exports) {
Name name = exp->value;
if (exp->kind == ExternalKind::Function) {
- info.shouldBeTrue(module.getFunctionOrNull(name), name, "module function exports must be found");
+ info.shouldBeTrue(module.getFunctionOrNull(name),
+ name,
+ "module function exports must be found");
} else if (exp->kind == ExternalKind::Global) {
- info.shouldBeTrue(module.getGlobalOrNull(name), name, "module global exports must be found");
+ info.shouldBeTrue(module.getGlobalOrNull(name),
+ name,
+ "module global exports must be found");
} else if (exp->kind == ExternalKind::Table) {
- info.shouldBeTrue(name == Name("0") || name == module.table.name, name, "module table exports must be found");
+ info.shouldBeTrue(name == Name("0") || name == module.table.name,
+ name,
+ "module table exports must be found");
} else if (exp->kind == ExternalKind::Memory) {
- info.shouldBeTrue(name == Name("0") || name == module.memory.name, name, "module memory exports must be found");
+ info.shouldBeTrue(name == Name("0") || name == module.memory.name,
+ name,
+ "module memory exports must be found");
} else {
WASM_UNREACHABLE();
}
Name exportName = exp->name;
- info.shouldBeFalse(exportNames.count(exportName) > 0, exportName, "module exports must be unique");
+ info.shouldBeFalse(exportNames.count(exportName) > 0,
+ exportName,
+ "module exports must be unique");
exportNames.insert(exportName);
}
}
static void validateGlobals(Module& module, ValidationInfo& info) {
ModuleUtils::iterDefinedGlobals(module, [&](Global* curr) {
- info.shouldBeTrue(getFeatures(curr->type) <= module.features, curr->name,
+ info.shouldBeTrue(getFeatures(curr->type) <= module.features,
+ curr->name,
"all used types should be allowed");
- info.shouldBeTrue(curr->init != nullptr, curr->name, "global init must be non-null");
- info.shouldBeTrue(curr->init->is<Const>() || curr->init->is<GetGlobal>(), curr->name, "global init must be valid");
- if (!info.shouldBeEqual(curr->type, curr->init->type, curr->init, "global init must have correct type") && !info.quiet) {
+ info.shouldBeTrue(
+ curr->init != nullptr, curr->name, "global init must be non-null");
+ info.shouldBeTrue(curr->init->is<Const>() || curr->init->is<GetGlobal>(),
+ curr->name,
+ "global init must be valid");
+ if (!info.shouldBeEqual(curr->type,
+ curr->init->type,
+ curr->init,
+ "global init must have correct type") &&
+ !info.quiet) {
info.getStream(nullptr) << "(on global " << curr->name << ")\n";
}
});
@@ -1297,30 +1752,57 @@ 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(module.features.hasAtomics(), "memory", "memory is shared, but atomics are disabled");
+ 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(module.features.hasAtomics(),
+ "memory",
+ "memory is shared, but atomics are disabled");
for (auto& segment : curr.segments) {
Index size = segment.data.size();
if (segment.isPassive) {
- info.shouldBeTrue(module.features.hasBulkMemory(), segment.offset, "nonzero segment flags (bulk memory is disabled)");
- info.shouldBeEqual(segment.offset, (Expression*)nullptr, segment.offset, "passive segment should not have an offset");
+ info.shouldBeTrue(module.features.hasBulkMemory(),
+ segment.offset,
+ "nonzero segment flags (bulk memory is disabled)");
+ info.shouldBeEqual(segment.offset,
+ (Expression*)nullptr,
+ segment.offset,
+ "passive segment should not have an offset");
} else {
- if (!info.shouldBeEqual(segment.offset->type, i32, segment.offset, "segment offset should be i32")) continue;
- info.shouldBeTrue(checkOffset(segment.offset, segment.data.size(), curr.initial * Memory::kPageSize), segment.offset, "segment offset should be reasonable");
+ if (!info.shouldBeEqual(segment.offset->type,
+ i32,
+ segment.offset,
+ "segment offset should be i32"))
+ continue;
+ info.shouldBeTrue(checkOffset(segment.offset,
+ segment.data.size(),
+ curr.initial * Memory::kPageSize),
+ segment.offset,
+ "segment offset should be reasonable");
if (segment.offset->is<Const>()) {
Index start = segment.offset->cast<Const>()->value.geti32();
Index end = start + size;
- info.shouldBeTrue(end <= curr.initial * Memory::kPageSize, segment.data.size(), "segment size should fit in memory (end)");
+ info.shouldBeTrue(end <= curr.initial * Memory::kPageSize,
+ segment.data.size(),
+ "segment size should fit in memory (end)");
}
}
// If the memory is imported we don't actually know its initial size.
// Specifically wasm dll's import a zero sized memory which is perfectly
// valid.
if (!curr.imported()) {
- info.shouldBeTrue(size <= curr.initial * Memory::kPageSize, segment.data.size(), "segment size should fit in memory (initial)");
+ info.shouldBeTrue(size <= curr.initial * Memory::kPageSize,
+ segment.data.size(),
+ "segment size should fit in memory (initial)");
}
}
}
@@ -1328,10 +1810,18 @@ static void validateMemory(Module& module, ValidationInfo& info) {
static void validateTable(Module& module, ValidationInfo& info) {
auto& curr = module.table;
for (auto& segment : curr.segments) {
- info.shouldBeEqual(segment.offset->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.shouldBeEqual(segment.offset->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");
for (auto name : segment.data) {
- info.shouldBeTrue(module.getFunctionOrNull(name), name, "segment name should be valid");
+ info.shouldBeTrue(
+ module.getFunctionOrNull(name), name, "segment name should be valid");
}
}
}
@@ -1340,16 +1830,19 @@ static void validateModule(Module& module, ValidationInfo& info) {
// start
if (module.start.is()) {
auto func = module.getFunctionOrNull(module.start);
- if (info.shouldBeTrue(func != nullptr, module.start, "start must be found")) {
- info.shouldBeTrue(func->params.size() == 0, module.start, "start must have 0 params");
- info.shouldBeTrue(func->result == none, module.start, "start must not return a value");
+ if (info.shouldBeTrue(
+ func != nullptr, module.start, "start must be found")) {
+ info.shouldBeTrue(
+ func->params.size() == 0, module.start, "start must have 0 params");
+ info.shouldBeTrue(
+ func->result == none, module.start, "start must not return a value");
}
}
}
-// TODO: If we want the validator to be part of libwasm rather than libpasses, then
-// Using PassRunner::getPassDebug causes a circular dependence. We should fix that,
-// perhaps by moving some of the pass infrastructure into libsupport.
+// TODO: If we want the validator to be part of libwasm rather than libpasses,
+// then Using PassRunner::getPassDebug causes a circular dependence. We should
+// fix that, perhaps by moving some of the pass infrastructure into libsupport.
bool WasmValidator::validate(Module& module, Flags flags) {
ValidationInfo info;
info.validateWeb = (flags & Web) != 0;