summaryrefslogtreecommitdiff
path: root/src/tools/execution-results.h
blob: 5ed0ff01fe032c81d108da0e2c92b858f608c8df (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
/*
 * Copyright 2017 WebAssembly Community Group participants
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

//
// Shared execution result checking code
//

#include "wasm.h"
#include "shell-interface.h"

namespace wasm {

static bool areBitwiseEqual(Literal a, Literal b) {
  if (a == b) return true;
  // accept equal nans if equal in all bits
  if (a.type != b.type) return false;
  if (a.type == f32) {
    return a.reinterpreti32() == b.reinterpreti32();
  } else if (a.type == f64) {
    return a.reinterpreti64() == b.reinterpreti64();
  }
  return false;
}

// gets execution results from a wasm module. this is useful for fuzzing
//
// we can only get results when there are no imports. we then call each method
// that has a result, with some values
struct ExecutionResults {
  std::map<Name, Literal> results;

  // get results of execution
  void get(Module& wasm) {
    if (wasm.imports.size() > 0) {
      std::cout << "[fuzz-exec] imports, so quitting\n";
      return;
    }
    ShellExternalInterface interface;
    ModuleInstance instance(wasm, &interface);
    // execute all exported methods (that are therefore preserved through opts)
    for (auto& exp : wasm.exports) {
      if (exp->kind != ExternalKind::Function) continue;
      auto* func = wasm.getFunction(exp->value);
      if (func->result != none) {
        // this has a result
        results[exp->name] = run(func, wasm, instance);
        std::cout << "[fuzz-exec] note result: " << exp->name << " => " << results[exp->name] << '\n';
      } else {
        // no result, run it anyhow (it might modify memory etc.)
        run(func, wasm, instance);
        std::cout << "[fuzz-exec] no result for void func: " << exp->name << '\n';
      }
    }
    std::cout << "[fuzz-exec] " << results.size() << " results noted\n";
  }

  // get current results and check them against previous ones
  void check(Module& wasm) {
    ExecutionResults optimizedResults;
    optimizedResults.get(wasm);
    if (optimizedResults != *this) {
      std::cout << "[fuzz-exec] optimization passes changed execution results";
      abort();
    }
    std::cout << "[fuzz-exec] " << results.size() << " results match\n";
  }

  bool operator==(ExecutionResults& other) {
    for (auto& iter : results) {
      auto name = iter.first;
      if (other.results.find(name) == other.results.end()) {
        std::cout << "[fuzz-exec] missing " << name << '\n';
        abort();
      }
      std::cout << "[fuzz-exec] comparing " << name << '\n';
      if (!areBitwiseEqual(results[name], other.results[name])) {
        std::cout << "not identical!\n";
        abort();
      }
    }
    return true;
  }

  bool operator!=(ExecutionResults& other) {
    return !((*this) == other);
  }

  Literal run(Function* func, Module& wasm) {
    ShellExternalInterface interface;
    try {
      ModuleInstance instance(wasm, &interface);
      return run(func, wasm, instance);
    } catch (const TrapException&) {
      // may throw in instance creation (init of offsets)
      return Literal();
    }
  }

  Literal run(Function* func, Module& wasm, ModuleInstance& instance) {
    try {
      LiteralList arguments;
      // init hang support, if present
      if (wasm.getFunctionOrNull("hangLimitInitializer")) {
        instance.callFunction("hangLimitInitializer", arguments);
      }
      // call the method
      for (WasmType param : func->params) {
        // zeros in arguments TODO: more?
        arguments.push_back(Literal(param));
      }
      return instance.callFunction(func->name, arguments);
    } catch (const TrapException&) {
      return Literal();
    }
  }
};

} // namespace wasm