diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/passes/pass.cpp | 34 | ||||
-rw-r--r-- | src/wasm-validator.h | 4 | ||||
-rw-r--r-- | src/wasm/wasm-validator.cpp | 17 |
3 files changed, 53 insertions, 2 deletions
diff --git a/src/passes/pass.cpp b/src/passes/pass.cpp index 8dcc7e417..570cd8610 100644 --- a/src/passes/pass.cpp +++ b/src/passes/pass.cpp @@ -891,18 +891,48 @@ void PassRunner::runPass(Pass* pass) { void PassRunner::runPassOnFunction(Pass* pass, Function* func) { assert(pass->isFunctionParallel()); + + auto passDebug = getPassDebug(); + + // Add extra validation logic in pass-debug mode 2. The main logic in + // PassRunner::run will work at the module level, and here for a function- + // parallel pass we can do the same at the function level: we can print the + // function before the pass, run the pass on the function, and then if it + // fails to validate we can show an error and print the state right before the + // pass broke it. + // + // Skip nameless passes for this. Anything without a name is an internal + // component of some larger pass, and information about it won't be very + // useful - leave it to the entire module to fail validation in that case. + bool extraFunctionValidation = + passDebug == 2 && options.validate && !pass->name.empty(); + std::stringstream bodyBefore; + if (extraFunctionValidation) { + bodyBefore << *func->body << '\n'; + } + // function-parallel passes get a new instance per function auto instance = std::unique_ptr<Pass>(pass->create()); std::unique_ptr<AfterEffectFunctionChecker> checker; - if (getPassDebug()) { + if (passDebug) { checker = std::unique_ptr<AfterEffectFunctionChecker>( new AfterEffectFunctionChecker(func)); } instance->runOnFunction(this, wasm, func); handleAfterEffects(pass, func); - if (getPassDebug()) { + if (passDebug) { checker->check(); } + + if (extraFunctionValidation) { + if (!WasmValidator().validate(func, *wasm, WasmValidator::Minimal)) { + Fatal() << "Last nested function-parallel pass (" << pass->name + << ") broke validation of function " << func->name + << ". Here is the function body before:\n" + << bodyBefore.str() << "\n\nAnd here it is now:\n" + << *func->body << '\n'; + } + } } void PassRunner::handleAfterEffects(Pass* pass, Function* func) { diff --git a/src/wasm-validator.h b/src/wasm-validator.h index b7f17c19c..ffd70ba89 100644 --- a/src/wasm-validator.h +++ b/src/wasm-validator.h @@ -56,7 +56,11 @@ struct WasmValidator { }; typedef uint32_t Flags; + // Validate an entire module. bool validate(Module& module, Flags flags = Globally); + + // Validate a specific function. + bool validate(Function* func, Module& module, Flags flags = Globally); }; } // namespace wasm diff --git a/src/wasm/wasm-validator.cpp b/src/wasm/wasm-validator.cpp index 10310f8c0..572a3b4ba 100644 --- a/src/wasm/wasm-validator.cpp +++ b/src/wasm/wasm-validator.cpp @@ -223,6 +223,9 @@ struct FunctionValidator : public WalkerPass<PostWalker<FunctionValidator>> { // Validate a specific expression. void validate(Expression* curr) { walk(curr); } + // Validate a function. + void validate(Function* func) { walkFunction(func); } + std::unordered_map<Name, std::unordered_set<Type>> breakTypes; std::unordered_set<Name> delegateTargetNames; std::unordered_set<Name> rethrowTargetNames; @@ -3215,4 +3218,18 @@ bool WasmValidator::validate(Module& module, Flags flags) { return info.valid.load(); } +bool WasmValidator::validate(Function* func, Module& module, Flags flags) { + ValidationInfo info(module); + info.validateWeb = (flags & Web) != 0; + info.validateGlobally = (flags & Globally) != 0; + info.quiet = (flags & Quiet) != 0; + FunctionValidator(module, &info).validate(func); + // print all the data + if (!info.valid.load() && !info.quiet) { + std::cerr << info.getStream(func).str(); + std::cerr << info.getStream(nullptr).str(); + } + return info.valid.load(); +} + } // namespace wasm |