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 |