summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/tools/execution-results.h60
-rw-r--r--src/tools/wasm-opt.cpp2
-rw-r--r--test/lit/exec/trap.wast55
3 files changed, 94 insertions, 23 deletions
diff --git a/src/tools/execution-results.h b/src/tools/execution-results.h
index 18f4a399c..afd7a78c1 100644
--- a/src/tools/execution-results.h
+++ b/src/tools/execution-results.h
@@ -87,15 +87,20 @@ struct LoggingExternalInterface : public ShellExternalInterface {
// we can only get results when there are no imports. we then call each method
// that has a result, with some values
struct ExecutionResults {
- struct FunctionResult {
- Literals values;
- bool exception; // Whether an exception is uncaught and the function crashes
- };
+ struct Trap {};
+ struct Exception {};
+ using FunctionResult = std::variant<Literals, Trap, Exception>;
std::map<Name, FunctionResult> results;
Loggings loggings;
// If set, we should ignore this and not compare it to anything.
bool ignore = false;
+ // If set, we don't compare whether a trap has occurred or not.
+ bool ignoreTrap = false;
+
+ ExecutionResults(const PassOptions& options)
+ : ignoreTrap(options.ignoreImplicitTraps || options.trapsNeverHappen) {}
+ ExecutionResults(bool ignoreTrap) : ignoreTrap(ignoreTrap) {}
// get results of execution
void get(Module& wasm) {
@@ -112,17 +117,19 @@ struct ExecutionResults {
auto* func = wasm.getFunction(exp->value);
FunctionResult ret = run(func, wasm, instance);
results[exp->name] = ret;
- // ignore the result if we hit an unreachable and returned no value
- if (ret.values.size() > 0) {
- std::cout << "[fuzz-exec] note result: " << exp->name << " => ";
- auto resultType = func->getResults();
- if (resultType.isRef()) {
- // Don't print reference values, as funcref(N) contains an index
- // for example, which is not guaranteed to remain identical after
- // optimizations.
- std::cout << resultType << '\n';
- } else {
- std::cout << ret.values << '\n';
+ if (auto* values = std::get_if<Literals>(&ret)) {
+ // ignore the result if we hit an unreachable and returned no value
+ if (values->size() > 0) {
+ std::cout << "[fuzz-exec] note result: " << exp->name << " => ";
+ auto resultType = func->getResults();
+ if (resultType.isRef()) {
+ // Don't print reference values, as funcref(N) contains an index
+ // for example, which is not guaranteed to remain identical after
+ // optimizations.
+ std::cout << resultType << '\n';
+ } else {
+ std::cout << *values << '\n';
+ }
}
}
}
@@ -133,7 +140,7 @@ struct ExecutionResults {
// get current results and check them against previous ones
void check(Module& wasm) {
- ExecutionResults optimizedResults;
+ ExecutionResults optimizedResults(ignoreTrap);
optimizedResults.get(wasm);
if (optimizedResults != *this) {
std::cout << "[fuzz-exec] optimization passes changed results\n";
@@ -189,10 +196,19 @@ struct ExecutionResults {
return false;
}
std::cout << "[fuzz-exec] comparing " << name << '\n';
- if (!areEqual(results[name].values, other.results[name].values)) {
- return false;
+ if (results[name].index() != other.results[name].index()) {
+ if (ignoreTrap) {
+ if (!std::get_if<Trap>(&results[name]) &&
+ !std::get_if<Trap>(&other.results[name])) {
+ return false;
+ }
+ } else {
+ return false;
+ }
}
- if (results[name].exception != other.results[name].exception) {
+ auto* values = std::get_if<Literals>(&results[name]);
+ auto* otherValues = std::get_if<Literals>(&other.results[name]);
+ if (values && otherValues && !areEqual(*values, *otherValues)) {
return false;
}
}
@@ -237,12 +253,12 @@ struct ExecutionResults {
}
arguments.push_back(Literal::makeZero(param));
}
- return {instance.callFunction(func->name, arguments), false};
+ return instance.callFunction(func->name, arguments);
} catch (const TrapException&) {
- return {};
+ return Trap{};
} catch (const WasmException& e) {
std::cout << "[exception thrown: " << e << "]" << std::endl;
- return {{}, true};
+ return Exception{};
} catch (const HostLimitException&) {
// This should be ignored and not compared with, as optimizations can
// change whether a host limit is reached.
diff --git a/src/tools/wasm-opt.cpp b/src/tools/wasm-opt.cpp
index 5aa0c374a..2ccf11fa0 100644
--- a/src/tools/wasm-opt.cpp
+++ b/src/tools/wasm-opt.cpp
@@ -289,7 +289,7 @@ int main(int argc, const char* argv[]) {
runner.run();
}
- ExecutionResults results;
+ ExecutionResults results(options.passOptions);
if (fuzzExecBefore) {
results.get(wasm);
}
diff --git a/test/lit/exec/trap.wast b/test/lit/exec/trap.wast
new file mode 100644
index 000000000..a0ed6f0f2
--- /dev/null
+++ b/test/lit/exec/trap.wast
@@ -0,0 +1,55 @@
+;; NOTE: Assertions have been generated by update_lit_checks.py --output=fuzz-exec and should not be edited.
+
+;; RUN: wasm-opt %s --vacuum --fuzz-exec -q -o /dev/null 2>&1 | filecheck %s
+;; RUN: wasm-opt %s --ignore-implicit-traps --vacuum --fuzz-exec -q -o /dev/null 2>&1 | filecheck %s --check-prefix=IIT
+;; RUN: wasm-opt %s --traps-never-happen --vacuum --fuzz-exec -q -o /dev/null 2>&1 | filecheck %s --check-prefix=TNH
+
+(module
+ ;; CHECK: [fuzz-exec] calling trap
+ ;; CHECK-NEXT: [trap unreachable]
+ ;; IIT: [fuzz-exec] calling trap
+ ;; IIT-NEXT: [trap unreachable]
+ ;; TNH: [fuzz-exec] calling trap
+ ;; TNH-NEXT: [trap unreachable]
+ (func "trap"
+ (unreachable)
+ )
+
+ (memory 1 1)
+ ;; CHECK: [fuzz-exec] calling load-trap
+ ;; CHECK-NEXT: [trap highest > memory: 65536 > 65532]
+ ;; IIT: [fuzz-exec] calling load-trap
+ ;; IIT-NEXT: [trap highest > memory: 65536 > 65532]
+ ;; TNH: [fuzz-exec] calling load-trap
+ ;; TNH-NEXT: [trap highest > memory: 65536 > 65532]
+ (func "load-trap"
+ ;; This load traps, so this cannot be removed. But if either of
+ ;; --ignore-implicit-traps or --traps-never-happen is set, this load is
+ ;; assumed not to trap and we end up optimizing this out with --vacuum,
+ ;; causes the trap behavior to change. This should not result in [fuzz-exec]
+ ;; comparison failure.
+ (drop
+ (i32.load (i32.const 65536))
+ )
+ )
+)
+;; CHECK: [fuzz-exec] calling trap
+;; CHECK-NEXT: [trap unreachable]
+
+;; CHECK: [fuzz-exec] calling load-trap
+;; CHECK-NEXT: [trap highest > memory: 65536 > 65532]
+;; CHECK-NEXT: [fuzz-exec] comparing load-trap
+;; CHECK-NEXT: [fuzz-exec] comparing trap
+
+;; IIT: [fuzz-exec] calling trap
+;; IIT-NEXT: [trap unreachable]
+
+;; IIT: [fuzz-exec] calling load-trap
+;; IIT-NEXT: [fuzz-exec] comparing load-trap
+;; IIT-NEXT: [fuzz-exec] comparing trap
+
+;; TNH: [fuzz-exec] calling trap
+
+;; TNH: [fuzz-exec] calling load-trap
+;; TNH-NEXT: [fuzz-exec] comparing load-trap
+;; TNH-NEXT: [fuzz-exec] comparing trap