summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/passes/pass.cpp34
-rw-r--r--src/wasm-validator.h4
-rw-r--r--src/wasm/wasm-validator.cpp17
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