summaryrefslogtreecommitdiff
path: root/src/wasm
diff options
context:
space:
mode:
authorThomas Lively <7121787+tlively@users.noreply.github.com>2020-09-09 13:01:21 -0700
committerGitHub <noreply@github.com>2020-09-09 13:01:21 -0700
commit2aa0cf300998c62aea8cc6698f8325653a9f0895 (patch)
tree2116bc18fd58514589821858c528951e76c5178b /src/wasm
parent41ea543093ed734c73e5202c58c899be447b3223 (diff)
downloadbinaryen-2aa0cf300998c62aea8cc6698f8325653a9f0895.tar.gz
binaryen-2aa0cf300998c62aea8cc6698f8325653a9f0895.tar.bz2
binaryen-2aa0cf300998c62aea8cc6698f8325653a9f0895.zip
Poppy IR wast parsing and validation (#3105)
Adds an IR profile to each function so the validator can determine which validation rules to apply and adds a flag to have the wast parser set the profile to Poppy for testing purposes.
Diffstat (limited to 'src/wasm')
-rw-r--r--src/wasm/wasm-io.cpp8
-rw-r--r--src/wasm/wasm-s-parser.cpp4
-rw-r--r--src/wasm/wasm-validator.cpp102
3 files changed, 109 insertions, 5 deletions
diff --git a/src/wasm/wasm-io.cpp b/src/wasm/wasm-io.cpp
index 8fa714d9c..79687d469 100644
--- a/src/wasm/wasm-io.cpp
+++ b/src/wasm/wasm-io.cpp
@@ -33,16 +33,16 @@ namespace wasm {
#define DEBUG_TYPE "writer"
-static void readTextData(std::string& input, Module& wasm) {
+static void readTextData(std::string& input, Module& wasm, IRProfile profile) {
SExpressionParser parser(const_cast<char*>(input.c_str()));
Element& root = *parser.root;
- SExpressionWasmBuilder builder(wasm, *root[0]);
+ SExpressionWasmBuilder builder(wasm, *root[0], profile);
}
void ModuleReader::readText(std::string filename, Module& wasm) {
BYN_TRACE("reading text from " << filename << "\n");
auto input(read_file<std::string>(filename, Flags::Text));
- readTextData(input, wasm);
+ readTextData(input, wasm, profile);
}
void ModuleReader::readBinaryData(std::vector<char>& input,
@@ -113,7 +113,7 @@ void ModuleReader::readStdin(Module& wasm, std::string sourceMapFilename) {
s.write(input.data(), input.size());
s << '\0';
std::string input_str = s.str();
- readTextData(input_str, wasm);
+ readTextData(input_str, wasm, profile);
}
}
diff --git a/src/wasm/wasm-s-parser.cpp b/src/wasm/wasm-s-parser.cpp
index 0e4202a24..bac5f958c 100644
--- a/src/wasm/wasm-s-parser.cpp
+++ b/src/wasm/wasm-s-parser.cpp
@@ -314,8 +314,9 @@ Element* SExpressionParser::parseString() {
SExpressionWasmBuilder::SExpressionWasmBuilder(Module& wasm,
Element& module,
+ IRProfile profile,
Name* moduleName)
- : wasm(wasm), allocator(wasm.allocator) {
+ : wasm(wasm), allocator(wasm.allocator), profile(profile) {
if (module.size() == 0) {
throw ParseException("empty toplevel, expected module");
}
@@ -795,6 +796,7 @@ void SExpressionWasmBuilder::parseFunction(Element& s, bool preParseImport) {
// make a new function
currFunction = std::unique_ptr<Function>(Builder(wasm).makeFunction(
name, std::move(params), sig.results, std::move(vars)));
+ currFunction->profile = profile;
// parse body
Block* autoBlock = nullptr; // may need to add a block for the very top level
diff --git a/src/wasm/wasm-validator.cpp b/src/wasm/wasm-validator.cpp
index b236f9120..c6d679a24 100644
--- a/src/wasm/wasm-validator.cpp
+++ b/src/wasm/wasm-validator.cpp
@@ -22,6 +22,7 @@
#include "ir/features.h"
#include "ir/global-utils.h"
#include "ir/module-utils.h"
+#include "ir/stack-utils.h"
#include "ir/utils.h"
#include "support/colors.h"
#include "wasm-printing.h"
@@ -246,6 +247,13 @@ struct FunctionValidator : public WalkerPass<PostWalker<FunctionValidator>> {
public:
// visitors
+ void validatePoppyExpression(Expression* curr);
+
+ static void visitPoppyExpression(FunctionValidator* self,
+ Expression** currp) {
+ self->validatePoppyExpression(*currp);
+ }
+
static void visitPreBlock(FunctionValidator* self, Expression** currp) {
auto* curr = (*currp)->cast<Block>();
if (curr->name.is()) {
@@ -254,6 +262,8 @@ public:
}
void visitBlock(Block* curr);
+ void validateNormalBlockElements(Block* curr);
+ void validatePoppyBlockElements(Block* curr);
static void visitPreLoop(FunctionValidator* self, Expression** currp) {
auto* curr = (*currp)->cast<Loop>();
@@ -276,6 +286,11 @@ public:
if (curr->is<Loop>()) {
self->pushTask(visitPreLoop, currp);
}
+ if (auto* func = self->getFunction()) {
+ if (func->profile == IRProfile::Poppy) {
+ self->pushTask(visitPoppyExpression, currp);
+ }
+ }
}
void noteBreak(Name name, Expression* value, Expression* curr);
@@ -386,6 +401,39 @@ void FunctionValidator::noteLabelName(Name name) {
"names in Binaryen IR must be unique - IR generators must ensure that");
}
+void FunctionValidator::validatePoppyExpression(Expression* curr) {
+ if (curr->type == Type::unreachable) {
+ shouldBeTrue(StackUtils::mayBeUnreachable(curr),
+ curr,
+ "Only control flow structures and unreachable polymorphic"
+ " instructions may be unreachable in Poppy IR");
+ }
+ if (Properties::isControlFlowStructure(curr)) {
+ // Check that control flow children (except If conditions) are blocks
+ if (auto* if_ = curr->dynCast<If>()) {
+ shouldBeTrue(
+ if_->condition->is<Pop>(), curr, "Expected condition to be a Pop");
+ shouldBeTrue(if_->ifTrue->is<Block>(),
+ curr,
+ "Expected control flow child to be a block");
+ shouldBeTrue(!if_->ifFalse || if_->ifFalse->is<Block>(),
+ curr,
+ "Expected control flow child to be a block");
+ } else if (!curr->is<Block>()) {
+ for (auto* child : ChildIterator(curr)) {
+ shouldBeTrue(child->is<Block>(),
+ curr,
+ "Expected control flow child to be a block");
+ }
+ }
+ } else {
+ // Check that all children are Pops
+ for (auto* child : ChildIterator(curr)) {
+ shouldBeTrue(child->is<Pop>(), curr, "Unexpected non-Pop child");
+ }
+ }
+}
+
void FunctionValidator::visitBlock(Block* curr) {
if (!getModule()->features.hasMultivalue()) {
shouldBeTrue(!curr->type.isTuple(),
@@ -439,6 +487,17 @@ void FunctionValidator::visitBlock(Block* curr) {
}
breakInfos.erase(iter);
}
+ switch (getFunction()->profile) {
+ case IRProfile::Normal:
+ validateNormalBlockElements(curr);
+ break;
+ case IRProfile::Poppy:
+ validatePoppyBlockElements(curr);
+ break;
+ }
+}
+
+void FunctionValidator::validateNormalBlockElements(Block* curr) {
if (curr->list.size() > 1) {
for (Index i = 0; i < curr->list.size() - 1; i++) {
if (!shouldBeTrue(
@@ -482,6 +541,45 @@ void FunctionValidator::visitBlock(Block* curr) {
}
}
+void FunctionValidator::validatePoppyBlockElements(Block* curr) {
+ StackSignature blockSig;
+ for (size_t i = 0; i < curr->list.size(); ++i) {
+ Expression* expr = curr->list[i];
+ if (!shouldBeTrue(
+ !expr->is<Pop>(), expr, "Unexpected top-level pop in block")) {
+ return;
+ }
+ StackSignature sig(expr);
+ if (!shouldBeTrue(blockSig.composes(sig),
+ curr,
+ "block element has incompatible type") &&
+ !info.quiet) {
+ getStream() << "(on index " << i << ":\n"
+ << expr << "\n), required: " << sig.params << ", available: ";
+ if (blockSig.unreachable) {
+ getStream() << "unreachable, ";
+ }
+ getStream() << blockSig.results << "\n";
+ return;
+ }
+ blockSig += sig;
+ }
+ if (curr->type == Type::unreachable) {
+ shouldBeTrue(blockSig.unreachable,
+ curr,
+ "unreachable block should have unreachable element");
+ } else {
+ if (!shouldBeTrue(blockSig.satisfies(Signature(Type::none, curr->type)),
+ curr,
+ "block contents should satisfy block type") &&
+ !info.quiet) {
+ getStream() << "contents: " << blockSig.results
+ << (blockSig.unreachable ? " [unreachable]" : "") << "\n"
+ << "expected: " << curr->type << "\n";
+ }
+ }
+}
+
void FunctionValidator::visitLoop(Loop* curr) {
if (curr->name.is()) {
noteLabelName(curr->name);
@@ -1988,6 +2086,10 @@ void FunctionValidator::visitFunction(Function* curr) {
shouldBeTrue(features <= getModule()->features,
curr,
"all used types should be allowed");
+ if (curr->profile == IRProfile::Poppy) {
+ shouldBeTrue(
+ curr->body->is<Block>(), curr->body, "Function body must be a block");
+ }
// if function has no result, it is ignored
// if body is unreachable, it might be e.g. a return
shouldBeSubTypeOrFirstIsUnreachable(