Chromium Code Reviews| 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 c0747b44c4f490bf234b3bb805b0dad615ecbe46..436dab88d079810e603b6694e6c15f81b80c4a74 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" |
| @@ -39,6 +41,9 @@ |
| #endif |
| static const uint32_t kMaxFunctions = 10; |
| +#define WASM_INTERPRETER 1 |
|
Clemens Hammacher
2016/05/24 12:47:03
This can be removed now.
titzer
2016/05/24 14:50:48
Done.
|
| + |
| +enum WasmExecutionMode { kExecuteInterpreted, kExecuteCompiled }; |
| // TODO(titzer): check traps more robustly in tests. |
| // Currently, in tests, we just return 0xdeadbeef from the function in which |
| @@ -72,10 +77,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 +101,7 @@ class TestingModule : public ModuleEnv { |
| if (instance->mem_start) { |
| free(instance->mem_start); |
| } |
| + if (interpreter_) delete interpreter_; |
| } |
| byte* AddMemory(size_t size) { |
| @@ -172,6 +182,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); |
| + WasmFunction* function = &module->functions.back(); |
|
Clemens Hammacher
2016/05/24 12:47:03
This can be moved inside the if.
titzer
2016/05/24 14:50:48
Done.
|
| + if (interpreter_) { |
| + int interpreter_index = interpreter_->AddFunctionForTesting(function); |
| + CHECK_EQ(index, static_cast<uint32_t>(interpreter_index)); |
| + } |
| DCHECK_LT(index, kMaxFunctions); // limited for testing. |
| return index; |
| } |
| @@ -224,12 +239,18 @@ class TestingModule : public ModuleEnv { |
| } |
| } |
| + 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_; |
| WasmGlobal* AddGlobal(MachineType mem_type) { |
| byte size = WasmOpcodes::MemSize(mem_type); |
| @@ -408,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), |
| @@ -423,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_->module->functions[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. |
| @@ -447,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_; } |
| @@ -462,13 +507,17 @@ class WasmFunctionCompiler : public HandleAndZoneScope, |
| } |
| } |
| CallDescriptor* descriptor() { return descriptor_; } |
| + int function_index() { return static_cast<int>(function_->func_index); } |
|
Clemens Hammacher
2016/05/24 12:47:03
You could use this function in all the code below.
titzer
2016/05/24 14:50:47
Done.
|
| 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) { |
| @@ -494,13 +543,14 @@ class WasmFunctionCompiler : public HandleAndZoneScope, |
| Handle<Code> code = info.code(); |
| - // Length is always 2, since usually <wasm_obj, func_index> is stored in the |
| + // Length is always 2, since usually <wasm_obj, func_index> is stored in |
| + // the |
|
Clemens Hammacher
2016/05/24 12:47:03
Huh, what happened here? The "the" should still fi
ahaas
2016/05/24 14:48:27
Fix the line break.
titzer
2016/05/24 14:50:48
Done.
|
| // 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(function_->func_index)); |
| deopt_data->set_length(2); |
| code->set_deoptimization_data(*deopt_data); |
| @@ -516,15 +566,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_->module->functions[function_index_]; |
| + testing_module_->SetFunctionCode(function_->func_index, code); |
| + return static_cast<uint32_t>(function_->func_index); |
| } |
| // Set the context, such that e.g. runtime functions can be called. |
| @@ -543,7 +588,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()) |
| @@ -551,7 +597,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); |
| } |
| @@ -594,51 +640,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(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(args, 1); |
| + } 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(args, 2); |
| + } 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(args, 3); |
| + } 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(args, 4); |
| + } 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(WasmVal* args, int count) { |
|
Clemens Hammacher
2016/05/24 12:47:03
Looks like a candidate for Vector<WasmVal*>.
titzer
2016/05/24 14:50:48
Done.
|
| + CHECK_EQ(count, |
| + static_cast<int>(compiler_.function_->sig->parameter_count())); |
| + WasmInterpreter::Thread& thread = interpreter()->GetThread(0); |
| + thread.Reset(); |
| + thread.PushFrame(compiler_.function_, args); |
| + 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; |
| @@ -648,6 +745,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; |
| @@ -662,10 +761,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) |
|
Clemens Hammacher
2016/05/24 12:47:03
Passing this execution_mode explicitely adds a ton
titzer
2016/05/24 14:50:48
Explicit is better than relying on global state, e
|
| } // namespace |