| Index: test/cctest/wasm/wasm-run-utils.h
|
| diff --git a/test/cctest/wasm/wasm-run-utils.h b/test/cctest/wasm/wasm-run-utils.h
|
| index 23c92f3c61cfc5bdbc217d9123fe99847935110b..4bbbac326d406d27214e4636a6a304f1a8a6a353 100644
|
| --- a/test/cctest/wasm/wasm-run-utils.h
|
| +++ b/test/cctest/wasm/wasm-run-utils.h
|
| @@ -9,6 +9,7 @@
|
| #include <stdlib.h>
|
| #include <string.h>
|
|
|
| +#include "src/base/accounting-allocator.h"
|
| #include "src/base/utils/random-number-generator.h"
|
|
|
| #include "src/compiler/graph-visualizer.h"
|
| @@ -20,6 +21,7 @@
|
| #include "src/compiler/zone-pool.h"
|
|
|
| #include "src/wasm/ast-decoder.h"
|
| +#include "src/wasm/wasm-interpreter.h"
|
| #include "src/wasm/wasm-js.h"
|
| #include "src/wasm/wasm-macro-gen.h"
|
| #include "src/wasm/wasm-module.h"
|
| @@ -40,6 +42,8 @@
|
|
|
| static const uint32_t kMaxFunctions = 10;
|
|
|
| +enum WasmExecutionMode { kExecuteInterpreted, kExecuteCompiled };
|
| +
|
| // TODO(titzer): check traps more robustly in tests.
|
| // Currently, in tests, we just return 0xdeadbeef from the function in which
|
| // the trap occurs if the runtime context is not available to throw a JavaScript
|
| @@ -72,10 +76,14 @@ const uint32_t kMaxGlobalsSize = 128;
|
| // {WasmModuleInstance}.
|
| class TestingModule : public ModuleEnv {
|
| public:
|
| - TestingModule()
|
| - : instance_(&module_),
|
| + explicit TestingModule(WasmExecutionMode mode = kExecuteCompiled)
|
| + : execution_mode_(mode),
|
| + instance_(&module_),
|
| isolate_(CcTest::InitIsolateOnce()),
|
| - global_offset(0) {
|
| + global_offset(0),
|
| + interpreter_(mode == kExecuteInterpreted
|
| + ? new WasmInterpreter(&instance_, &allocator_)
|
| + : nullptr) {
|
| module = &module_;
|
| instance = &instance_;
|
| instance->module = &module_;
|
| @@ -92,6 +100,7 @@ class TestingModule : public ModuleEnv {
|
| if (instance->mem_start) {
|
| free(instance->mem_start);
|
| }
|
| + if (interpreter_) delete interpreter_;
|
| }
|
|
|
| byte* AddMemory(size_t size) {
|
| @@ -172,6 +181,11 @@ class TestingModule : public ModuleEnv {
|
| uint32_t index = static_cast<uint32_t>(module->functions.size());
|
| module_.functions.push_back({sig, index, 0, 0, 0, 0, 0, false});
|
| instance->function_code.push_back(code);
|
| + if (interpreter_) {
|
| + const WasmFunction* function = &module->functions.back();
|
| + int interpreter_index = interpreter_->AddFunctionForTesting(function);
|
| + CHECK_EQ(index, static_cast<uint32_t>(interpreter_index));
|
| + }
|
| DCHECK_LT(index, kMaxFunctions); // limited for testing.
|
| return index;
|
| }
|
| @@ -225,12 +239,18 @@ class TestingModule : public ModuleEnv {
|
| }
|
| WasmFunction* GetFunctionAt(int index) { return &module_.functions[index]; }
|
|
|
| + WasmInterpreter* interpreter() { return interpreter_; }
|
| + WasmExecutionMode execution_mode() { return execution_mode_; }
|
| +
|
| private:
|
| + WasmExecutionMode execution_mode_;
|
| WasmModule module_;
|
| WasmModuleInstance instance_;
|
| Isolate* isolate_;
|
| + v8::base::AccountingAllocator allocator_;
|
| uint32_t global_offset;
|
| V8_ALIGNED(8) byte global_data[kMaxGlobalsSize]; // preallocated global data.
|
| + WasmInterpreter* interpreter_;
|
|
|
| const WasmGlobal* AddGlobal(MachineType mem_type) {
|
| byte size = WasmOpcodes::MemSize(mem_type);
|
| @@ -409,14 +429,41 @@ class WasmFunctionWrapper : public HandleAndZoneScope,
|
| // A helper for compiling WASM functions for testing. This class can create a
|
| // standalone function if {module} is NULL or a function within a
|
| // {TestingModule}. It contains the internal state for compilation (i.e.
|
| -// TurboFan graph) and, later, interpretation.
|
| +// TurboFan graph) and interpretation (by adding to the interpreter manually).
|
| class WasmFunctionCompiler : public HandleAndZoneScope,
|
| private GraphAndBuilders {
|
| public:
|
| explicit WasmFunctionCompiler(
|
| + FunctionSig* sig, WasmExecutionMode mode,
|
| + Vector<const char> debug_name = ArrayVector("<WASM UNNAMED>"))
|
| + : GraphAndBuilders(main_zone()),
|
| + execution_mode_(mode),
|
| + jsgraph(this->isolate(), this->graph(), this->common(), nullptr,
|
| + nullptr, this->machine()),
|
| + sig(sig),
|
| + descriptor_(nullptr),
|
| + testing_module_(nullptr),
|
| + debug_name_(debug_name),
|
| + local_decls(main_zone(), sig),
|
| + source_position_table_(this->graph()),
|
| + interpreter_(nullptr) {
|
| + // Create our own function.
|
| + function_ = new WasmFunction();
|
| + function_->sig = sig;
|
| + function_->func_index = 0;
|
| + function_->sig_index = 0;
|
| + if (mode == kExecuteInterpreted) {
|
| + interpreter_ = new WasmInterpreter(nullptr, zone()->allocator());
|
| + int index = interpreter_->AddFunctionForTesting(function_);
|
| + CHECK_EQ(0, index);
|
| + }
|
| + }
|
| +
|
| + explicit WasmFunctionCompiler(
|
| FunctionSig* sig, TestingModule* module,
|
| Vector<const char> debug_name = ArrayVector("<WASM UNNAMED>"))
|
| : GraphAndBuilders(main_zone()),
|
| + execution_mode_(module->execution_mode()),
|
| jsgraph(this->isolate(), this->graph(), this->common(), nullptr,
|
| nullptr, this->machine()),
|
| sig(sig),
|
| @@ -424,23 +471,20 @@ class WasmFunctionCompiler : public HandleAndZoneScope,
|
| testing_module_(module),
|
| debug_name_(debug_name),
|
| local_decls(main_zone(), sig),
|
| - source_position_table_(this->graph()) {
|
| - if (module) {
|
| - // Get a new function from the testing module.
|
| - function_ = nullptr;
|
| - function_index_ = module->AddFunction(sig, Handle<Code>::null());
|
| - } else {
|
| - // Create our own function.
|
| - function_ = new WasmFunction();
|
| - function_->sig = sig;
|
| - function_index_ = 0;
|
| - }
|
| + source_position_table_(this->graph()),
|
| + interpreter_(module->interpreter()) {
|
| + // Get a new function from the testing module.
|
| + int index = module->AddFunction(sig, Handle<Code>::null());
|
| + function_ = testing_module_->GetFunctionAt(index);
|
| }
|
|
|
| ~WasmFunctionCompiler() {
|
| - if (function_) delete function_;
|
| + if (testing_module_) return; // testing module owns the below things.
|
| + delete function_;
|
| + if (interpreter_) delete interpreter_;
|
| }
|
|
|
| + WasmExecutionMode execution_mode_;
|
| JSGraph jsgraph;
|
| FunctionSig* sig;
|
| // The call descriptor is initialized when the function is compiled.
|
| @@ -448,9 +492,9 @@ class WasmFunctionCompiler : public HandleAndZoneScope,
|
| TestingModule* testing_module_;
|
| Vector<const char> debug_name_;
|
| WasmFunction* function_;
|
| - int function_index_;
|
| LocalDeclEncoder local_decls;
|
| SourcePositionTable source_position_table_;
|
| + WasmInterpreter* interpreter_;
|
|
|
| Isolate* isolate() { return main_isolate(); }
|
| Graph* graph() const { return main_graph_; }
|
| @@ -463,13 +507,17 @@ class WasmFunctionCompiler : public HandleAndZoneScope,
|
| }
|
| }
|
| CallDescriptor* descriptor() { return descriptor_; }
|
| + uint32_t function_index() { return function_->func_index; }
|
|
|
| void Build(const byte* start, const byte* end) {
|
| // Build the TurboFan graph.
|
| - local_decls.Prepend(&start, &end);
|
| + local_decls.Prepend(main_zone(), &start, &end);
|
| TestBuildingGraph(main_zone(), &jsgraph, testing_module_, sig,
|
| &source_position_table_, start, end);
|
| - delete[] start;
|
| + if (interpreter_) {
|
| + // Add the code to the interpreter.
|
| + CHECK(interpreter_->SetFunctionCodeForTesting(function_, start, end));
|
| + }
|
| }
|
|
|
| byte AllocateLocal(LocalType type) {
|
| @@ -495,13 +543,13 @@ class WasmFunctionCompiler : public HandleAndZoneScope,
|
|
|
| Handle<Code> code = info.code();
|
|
|
| - // Length is always 2, since usually <wasm_obj, func_index> is stored in the
|
| - // deopt data. Here, we only store the function index.
|
| + // Length is always 2, since usually <wasm_obj, func_index> is stored in
|
| + // the deopt data. Here, we only store the function index.
|
| DCHECK(code->deoptimization_data() == nullptr ||
|
| code->deoptimization_data()->length() == 0);
|
| Handle<FixedArray> deopt_data =
|
| isolate()->factory()->NewFixedArray(2, TENURED);
|
| - deopt_data->set(1, Smi::FromInt(function_index_));
|
| + deopt_data->set(1, Smi::FromInt(static_cast<int>(function_index())));
|
| deopt_data->set_length(2);
|
| code->set_deoptimization_data(*deopt_data);
|
|
|
| @@ -517,15 +565,10 @@ class WasmFunctionCompiler : public HandleAndZoneScope,
|
|
|
| uint32_t CompileAndAdd(uint16_t sig_index = 0) {
|
| CHECK(testing_module_);
|
| - function()->sig_index = sig_index;
|
| + function_->sig_index = sig_index;
|
| Handle<Code> code = Compile();
|
| - testing_module_->SetFunctionCode(function_index_, code);
|
| - return static_cast<uint32_t>(function_index_);
|
| - }
|
| -
|
| - WasmFunction* function() {
|
| - if (function_) return function_;
|
| - return testing_module_->GetFunctionAt(function_index_);
|
| + testing_module_->SetFunctionCode(function_index(), code);
|
| + return function_index();
|
| }
|
|
|
| // Set the context, such that e.g. runtime functions can be called.
|
| @@ -544,7 +587,8 @@ class WasmFunctionCompiler : public HandleAndZoneScope,
|
| template <typename ReturnType>
|
| class WasmRunner {
|
| public:
|
| - WasmRunner(MachineType p0 = MachineType::None(),
|
| + WasmRunner(WasmExecutionMode execution_mode,
|
| + MachineType p0 = MachineType::None(),
|
| MachineType p1 = MachineType::None(),
|
| MachineType p2 = MachineType::None(),
|
| MachineType p3 = MachineType::None())
|
| @@ -552,7 +596,7 @@ class WasmRunner {
|
| compiled_(false),
|
| signature_(MachineTypeForC<ReturnType>() == MachineType::None() ? 0 : 1,
|
| GetParameterCount(p0, p1, p2, p3), storage_),
|
| - compiler_(&signature_, nullptr) {
|
| + compiler_(&signature_, execution_mode) {
|
| InitSigStorage(p0, p1, p2, p3);
|
| }
|
|
|
| @@ -595,51 +639,102 @@ class WasmRunner {
|
| void Build(const byte* start, const byte* end) {
|
| CHECK(!compiled_);
|
| compiled_ = true;
|
| -
|
| - // Build the TF graph within the compiler.
|
| compiler_.Build(start, end);
|
| - // Generate code.
|
| - Handle<Code> code = compiler_.Compile();
|
|
|
| - if (compiler_.testing_module_) {
|
| - // Update the table of function code in the module.
|
| - compiler_.testing_module_->SetFunctionCode(compiler_.function_index_,
|
| - code);
|
| - }
|
| + if (!interpret()) {
|
| + // Compile machine code and install it into the module.
|
| + Handle<Code> code = compiler_.Compile();
|
| +
|
| + if (compiler_.testing_module_) {
|
| + // Update the table of function code in the module.
|
| + compiler_.testing_module_->SetFunctionCode(
|
| + compiler_.function_->func_index, code);
|
| + }
|
|
|
| - wrapper_.SetInnerCode(code);
|
| + wrapper_.SetInnerCode(code);
|
| + }
|
| }
|
|
|
| - ReturnType Call() { return Call(0, 0, 0, 0); }
|
| + ReturnType Call() {
|
| + if (interpret()) {
|
| + return CallInterpreter(Vector<WasmVal>(nullptr, 0));
|
| + } else {
|
| + return Call(0, 0, 0, 0);
|
| + }
|
| + }
|
|
|
| template <typename P0>
|
| ReturnType Call(P0 p0) {
|
| - return Call(p0, 0, 0, 0);
|
| + if (interpret()) {
|
| + WasmVal args[] = {WasmVal(p0)};
|
| + return CallInterpreter(ArrayVector(args));
|
| + } else {
|
| + return Call(p0, 0, 0, 0);
|
| + }
|
| }
|
|
|
| template <typename P0, typename P1>
|
| ReturnType Call(P0 p0, P1 p1) {
|
| - return Call(p0, p1, 0, 0);
|
| + if (interpret()) {
|
| + WasmVal args[] = {WasmVal(p0), WasmVal(p1)};
|
| + return CallInterpreter(ArrayVector(args));
|
| + } else {
|
| + return Call(p0, p1, 0, 0);
|
| + }
|
| }
|
|
|
| template <typename P0, typename P1, typename P2>
|
| ReturnType Call(P0 p0, P1 p1, P2 p2) {
|
| - return Call(p0, p1, p2, 0);
|
| + if (interpret()) {
|
| + WasmVal args[] = {WasmVal(p0), WasmVal(p1), WasmVal(p2)};
|
| + return CallInterpreter(ArrayVector(args));
|
| + } else {
|
| + return Call(p0, p1, p2, 0);
|
| + }
|
| }
|
|
|
| template <typename P0, typename P1, typename P2, typename P3>
|
| ReturnType Call(P0 p0, P1 p1, P2 p2, P3 p3) {
|
| - CodeRunner<int32_t> runner(CcTest::InitIsolateOnce(),
|
| - wrapper_.GetWrapperCode(), wrapper_.signature());
|
| - ReturnType return_value;
|
| - int32_t result = runner.Call<void*, void*, void*, void*, void*>(
|
| - &p0, &p1, &p2, &p3, &return_value);
|
| - CHECK_EQ(WASM_WRAPPER_RETURN_VALUE, result);
|
| - return return_value;
|
| + if (interpret()) {
|
| + WasmVal args[] = {WasmVal(p0), WasmVal(p1), WasmVal(p2), WasmVal(p3)};
|
| + return CallInterpreter(ArrayVector(args));
|
| + } else {
|
| + CodeRunner<int32_t> runner(CcTest::InitIsolateOnce(),
|
| + wrapper_.GetWrapperCode(),
|
| + wrapper_.signature());
|
| + ReturnType return_value;
|
| + int32_t result = runner.Call<void*, void*, void*, void*, void*>(
|
| + &p0, &p1, &p2, &p3, &return_value);
|
| + CHECK_EQ(WASM_WRAPPER_RETURN_VALUE, result);
|
| + return return_value;
|
| + }
|
| + }
|
| +
|
| + ReturnType CallInterpreter(Vector<WasmVal> args) {
|
| + CHECK_EQ(args.length(),
|
| + static_cast<int>(compiler_.function_->sig->parameter_count()));
|
| + WasmInterpreter::Thread& thread = interpreter()->GetThread(0);
|
| + thread.Reset();
|
| + thread.PushFrame(compiler_.function_, args.start());
|
| + if (thread.Run() == WasmInterpreter::FINISHED) {
|
| + WasmVal val = thread.GetReturnValue();
|
| + return val.to<ReturnType>();
|
| + } else if (thread.state() == WasmInterpreter::TRAPPED) {
|
| + // TODO(titzer): return the correct trap code
|
| + int64_t result = 0xdeadbeefdeadbeef;
|
| + return static_cast<ReturnType>(result);
|
| + } else {
|
| + // TODO(titzer): falling off end
|
| + ReturnType val = 0;
|
| + return val;
|
| + }
|
| }
|
|
|
| byte AllocateLocal(LocalType type) { return compiler_.AllocateLocal(type); }
|
|
|
| + WasmFunction* function() { return compiler_.function_; }
|
| + WasmInterpreter* interpreter() { return compiler_.interpreter_; }
|
| +
|
| protected:
|
| v8::base::AccountingAllocator allocator_;
|
| Zone zone;
|
| @@ -649,6 +744,8 @@ class WasmRunner {
|
| WasmFunctionCompiler compiler_;
|
| WasmFunctionWrapper<ReturnType> wrapper_;
|
|
|
| + bool interpret() { return compiler_.execution_mode_ == kExecuteInterpreted; }
|
| +
|
| static size_t GetParameterCount(MachineType p0, MachineType p1,
|
| MachineType p2, MachineType p3) {
|
| if (p0 == MachineType::None()) return 0;
|
| @@ -663,10 +760,11 @@ class WasmRunner {
|
| // Currently only supports compiled tests, but a future
|
| // RunWasmInterpreted_##name version will allow each test to also run in the
|
| // interpreter.
|
| -#define WASM_EXEC_TEST(name) \
|
| - void RunWasm_##name(); \
|
| - TEST(RunWasmCompiled_##name) { RunWasm_##name(); } \
|
| - void RunWasm_##name()
|
| +#define WASM_EXEC_TEST(name) \
|
| + void RunWasm_##name(WasmExecutionMode execution_mode); \
|
| + TEST(RunWasmCompiled_##name) { RunWasm_##name(kExecuteCompiled); } \
|
| + TEST(RunWasmInterpreted_##name) { RunWasm_##name(kExecuteInterpreted); } \
|
| + void RunWasm_##name(WasmExecutionMode execution_mode)
|
|
|
| } // namespace
|
|
|
|
|