diff options
author | Alon Zakai <alonzakai@gmail.com> | 2017-05-18 10:47:23 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-05-18 10:47:23 -0700 |
commit | 9c6b8e0f626ade30cee113294019edbdbf29dd36 (patch) | |
tree | da088ba8eef1d3d20f1f0e9fa3b5af2c8dbeba1d /src/wasm-validator.h | |
parent | bb1c44a3f975bf8fb72216b9c04bcd34e31bd815 (diff) | |
download | binaryen-9c6b8e0f626ade30cee113294019edbdbf29dd36.tar.gz binaryen-9c6b8e0f626ade30cee113294019edbdbf29dd36.tar.bz2 binaryen-9c6b8e0f626ade30cee113294019edbdbf29dd36.zip |
Validate finalization (#1014)
* validate that types are properly finalized, when in pass-debug mode (BINARYEN_PASS_DEBUG env var): check after each pass is run that the type of each node is equal to the proper type (when finalizing it, i.e., fully recomputing the type).
* fix many fuzz bugs found by that.
* in particular, fix dce bugs with type changes not being fully updated during code removal. add a new TypeUpdater helper class that lets a pass update types efficiently, by the helper tracking deps between blocks and branches etc., and updating/propagating type changes only as necessary.
Diffstat (limited to 'src/wasm-validator.h')
-rw-r--r-- | src/wasm-validator.h | 57 |
1 files changed, 50 insertions, 7 deletions
diff --git a/src/wasm-validator.h b/src/wasm-validator.h index b657b67a6..66375790c 100644 --- a/src/wasm-validator.h +++ b/src/wasm-validator.h @@ -42,6 +42,7 @@ #include "support/colors.h" #include "wasm.h" #include "wasm-printing.h" +#include "ast_utils.h" namespace wasm { @@ -68,7 +69,7 @@ struct WasmValidator : public PostWalker<WasmValidator> { void noteLabelName(Name name) { if (!name.is()) return; - shouldBeTrue(labelNames.find(name) == labelNames.end(), name, "names in Binaren IR must be unique - IR generators must ensure that"); + shouldBeTrue(labelNames.find(name) == labelNames.end(), name, "names in Binaryen IR must be unique - IR generators must ensure that"); labelNames.insert(name); } @@ -76,7 +77,13 @@ public: bool validate(Module& module, bool validateWeb_ = false, bool validateGlobally_ = true) { validateWeb = validateWeb_; validateGlobally = validateGlobally_; + // wasm logic validation walkModule(&module); + // validate additional internal IR details when in pass-debug mode + if (PassRunner::getPassDebug()) { + validateBinaryenIR(module); + } + // print if an error occurred if (!valid) { WasmPrinter::printModule(&module, std::cerr); } @@ -222,16 +229,23 @@ public: } } void visitBreak(Break *curr) { - noteBreak(curr->name, curr->value, curr); + // note breaks (that are actually taken) + if ((!curr->value || curr->value->type != unreachable) && + (!curr->condition || curr->condition->type != unreachable)) { + noteBreak(curr->name, curr->value, curr); + } if (curr->condition) { shouldBeTrue(curr->condition->type == unreachable || curr->condition->type == i32, curr, "break condition must be i32"); } } void visitSwitch(Switch *curr) { - for (auto& target : curr->targets) { - noteBreak(target, curr->value, curr); + // note breaks (that are actually taken) + if (curr->condition->type != unreachable && (!curr->value || curr->value->type != unreachable)) { + for (auto& target : curr->targets) { + noteBreak(target, curr->value, curr); + } + noteBreak(curr->default_, 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"); } void visitCall(Call *curr) { @@ -612,8 +626,6 @@ public: PostWalker<WasmValidator>::doWalkFunction(func); } -private: - // helpers std::ostream& fail() { @@ -718,6 +730,37 @@ private: default: {} } } + + void validateBinaryenIR(Module& wasm) { + struct BinaryenIRValidator : public PostWalker<BinaryenIRValidator, UnifiedExpressionVisitor<BinaryenIRValidator>> { + WasmValidator& parent; + + BinaryenIRValidator(WasmValidator& parent) : parent(parent) {} + + void visitExpression(Expression* curr) { + // 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; + if (newType != oldType) { + // We accept concrete => undefined, + // e.g. + // + // (drop (block 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. + if (!(isConcreteWasmType(oldType) && newType == unreachable)) { + parent.fail() << "stale type found in " << getFunction()->name << " on " << curr << "\n(marked as " << printWasmType(oldType) << ", should be " << printWasmType(newType) << ")\n"; + parent.valid = false; + } + curr->type = oldType; + } + } + }; + BinaryenIRValidator binaryenIRValidator(*this); + binaryenIRValidator.walkModule(&wasm); + } }; } // namespace wasm |