diff options
author | Thomas Lively <7121787+tlively@users.noreply.github.com> | 2020-09-09 13:01:21 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-09-09 13:01:21 -0700 |
commit | 2aa0cf300998c62aea8cc6698f8325653a9f0895 (patch) | |
tree | 2116bc18fd58514589821858c528951e76c5178b /src/wasm | |
parent | 41ea543093ed734c73e5202c58c899be447b3223 (diff) | |
download | binaryen-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.cpp | 8 | ||||
-rw-r--r-- | src/wasm/wasm-s-parser.cpp | 4 | ||||
-rw-r--r-- | src/wasm/wasm-validator.cpp | 102 |
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( |