| 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 ed8e893a7fcf7921340766d8ac0352e35aa704a3..13f37738e649ba9d4197eb79e12ea48c9ff3b959 100644
|
| --- a/test/cctest/wasm/wasm-run-utils.h
|
| +++ b/test/cctest/wasm/wasm-run-utils.h
|
| @@ -9,6 +9,7 @@
|
| #include <stdint.h>
|
| #include <stdlib.h>
|
| #include <string.h>
|
| +#include <array>
|
| #include <memory>
|
|
|
| #include "src/base/utils/random-number-generator.h"
|
| @@ -51,7 +52,6 @@ enum WasmExecutionMode { kExecuteInterpreted, kExecuteCompiled };
|
| CHECK_EQ(0xdeadbeefdeadbeef, (bit_cast<uint64_t>(x)) & 0xFFFFFFFFFFFFFFFF)
|
| #define CHECK_TRAP(x) CHECK_TRAP32(x)
|
|
|
| -#define WASM_RUNNER_MAX_NUM_PARAMETERS 4
|
| #define WASM_WRAPPER_RETURN_VALUE 8754
|
|
|
| #define BUILD(r, ...) \
|
| @@ -73,7 +73,7 @@ const uint32_t kMaxGlobalsSize = 128;
|
| // {WasmInstance}.
|
| class TestingModule : public ModuleEnv {
|
| public:
|
| - explicit TestingModule(WasmExecutionMode mode = kExecuteCompiled)
|
| + explicit TestingModule(Zone* zone, WasmExecutionMode mode = kExecuteCompiled)
|
| : ModuleEnv(&module_, &instance_),
|
| execution_mode_(mode),
|
| instance_(&module_),
|
| @@ -83,7 +83,7 @@ class TestingModule : public ModuleEnv {
|
| ? new WasmInterpreter(
|
| ModuleBytesEnv(&module_, &instance_,
|
| Vector<const byte>::empty()),
|
| - &allocator_)
|
| + zone->allocator())
|
| : nullptr) {
|
| instance->module = &module_;
|
| instance->globals_start = global_data;
|
| @@ -104,7 +104,7 @@ class TestingModule : public ModuleEnv {
|
|
|
| byte* AddMemory(uint32_t size) {
|
| CHECK_NULL(instance->mem_start);
|
| - CHECK_EQ(0u, instance->mem_size);
|
| + CHECK_EQ(0, instance->mem_size);
|
| instance->mem_start = reinterpret_cast<byte*>(malloc(size));
|
| CHECK(instance->mem_start);
|
| memset(instance->mem_start, 0, size);
|
| @@ -119,7 +119,8 @@ class TestingModule : public ModuleEnv {
|
| }
|
|
|
| template <typename T>
|
| - T* AddGlobal(LocalType type) {
|
| + T* AddGlobal(
|
| + LocalType type = WasmOpcodes::LocalTypeFor(MachineTypeForC<T>())) {
|
| const WasmGlobal* global = AddGlobal(type);
|
| return reinterpret_cast<T*>(instance->globals_start + global->offset);
|
| }
|
| @@ -244,6 +245,7 @@ class TestingModule : public ModuleEnv {
|
| }
|
|
|
| void PopulateIndirectFunctionTable() {
|
| + if (execution_mode_ == kExecuteInterpreted) return;
|
| // Initialize the fixed arrays in instance->function_tables.
|
| for (uint32_t i = 0; i < instance->function_tables.size(); i++) {
|
| WasmIndirectFunctionTable& table = module_.function_tables[i];
|
| @@ -262,13 +264,13 @@ class TestingModule : public ModuleEnv {
|
|
|
| WasmInterpreter* interpreter() { return interpreter_; }
|
| WasmExecutionMode execution_mode() { return execution_mode_; }
|
| + Isolate* isolate() { return isolate_; }
|
|
|
| private:
|
| WasmExecutionMode execution_mode_;
|
| WasmModule module_;
|
| WasmInstance instance_;
|
| Isolate* isolate_;
|
| - v8::internal::AccountingAllocator allocator_;
|
| uint32_t global_offset;
|
| V8_ALIGNED(8) byte global_data[kMaxGlobalsSize]; // preallocated global data.
|
| WasmInterpreter* interpreter_;
|
| @@ -314,36 +316,29 @@ inline void TestBuildingGraph(Zone* zone, JSGraph* jsgraph, ModuleEnv* module,
|
| }
|
| }
|
|
|
| -template <typename ReturnType>
|
| -class WasmFunctionWrapper : public HandleAndZoneScope,
|
| - private GraphAndBuilders {
|
| +class WasmFunctionWrapper : private GraphAndBuilders {
|
| public:
|
| - WasmFunctionWrapper()
|
| - : GraphAndBuilders(main_zone()),
|
| - inner_code_node_(nullptr),
|
| - signature_(nullptr) {
|
| + explicit WasmFunctionWrapper(Zone* zone, int num_params)
|
| + : GraphAndBuilders(zone), inner_code_node_(nullptr), signature_(nullptr) {
|
| // One additional parameter for the pointer to the return value memory.
|
| - Signature<MachineType>::Builder sig_builder(
|
| - zone(), 1, WASM_RUNNER_MAX_NUM_PARAMETERS + 1);
|
| + Signature<MachineType>::Builder sig_builder(zone, 1, num_params + 1);
|
|
|
| sig_builder.AddReturn(MachineType::Int32());
|
| - for (int i = 0; i < WASM_RUNNER_MAX_NUM_PARAMETERS + 1; i++) {
|
| + for (int i = 0; i < num_params + 1; i++) {
|
| sig_builder.AddParam(MachineType::Pointer());
|
| }
|
| signature_ = sig_builder.Build();
|
| }
|
|
|
| - void Init(CallDescriptor* descriptor, MachineType p0 = MachineType::None(),
|
| - MachineType p1 = MachineType::None(),
|
| - MachineType p2 = MachineType::None(),
|
| - MachineType p3 = MachineType::None()) {
|
| - // Create the TF graph for the wrapper. The wrapper always takes four
|
| - // pointers as parameters, but may not pass the values of all pointers to
|
| - // the actual test function.
|
| + void Init(CallDescriptor* descriptor, MachineType return_type,
|
| + Vector<MachineType> param_types) {
|
| + DCHECK_NOT_NULL(descriptor);
|
| + DCHECK_EQ(signature_->parameter_count(), param_types.length() + 1);
|
| +
|
| + // Create the TF graph for the wrapper.
|
|
|
| // Function, effect, and control.
|
| - Node** parameters =
|
| - zone()->template NewArray<Node*>(WASM_RUNNER_MAX_NUM_PARAMETERS + 3);
|
| + Node** parameters = zone()->NewArray<Node*>(param_types.length() + 3);
|
| graph()->SetStart(graph()->NewNode(common()->Start(6)));
|
| Node* effect = graph()->start();
|
| int parameter_count = 0;
|
| @@ -352,34 +347,12 @@ class WasmFunctionWrapper : public HandleAndZoneScope,
|
| inner_code_node_ = graph()->NewNode(common()->Int32Constant(0));
|
| parameters[parameter_count++] = inner_code_node_;
|
|
|
| - if (p0 != MachineType::None()) {
|
| - parameters[parameter_count] = graph()->NewNode(
|
| - machine()->Load(p0),
|
| - graph()->NewNode(common()->Parameter(0), graph()->start()),
|
| - graph()->NewNode(common()->Int32Constant(0)), effect,
|
| - graph()->start());
|
| - effect = parameters[parameter_count++];
|
| - }
|
| - if (p1 != MachineType::None()) {
|
| + int param_idx = 0;
|
| + for (MachineType t : param_types) {
|
| + DCHECK_NE(MachineType::None(), t);
|
| parameters[parameter_count] = graph()->NewNode(
|
| - machine()->Load(p1),
|
| - graph()->NewNode(common()->Parameter(1), graph()->start()),
|
| - graph()->NewNode(common()->Int32Constant(0)), effect,
|
| - graph()->start());
|
| - effect = parameters[parameter_count++];
|
| - }
|
| - if (p2 != MachineType::None()) {
|
| - parameters[parameter_count] = graph()->NewNode(
|
| - machine()->Load(p2),
|
| - graph()->NewNode(common()->Parameter(2), graph()->start()),
|
| - graph()->NewNode(common()->Int32Constant(0)), effect,
|
| - graph()->start());
|
| - effect = parameters[parameter_count++];
|
| - }
|
| - if (p3 != MachineType::None()) {
|
| - parameters[parameter_count] = graph()->NewNode(
|
| - machine()->Load(p3),
|
| - graph()->NewNode(common()->Parameter(3), graph()->start()),
|
| + machine()->Load(t),
|
| + graph()->NewNode(common()->Parameter(param_idx++), graph()->start()),
|
| graph()->NewNode(common()->Int32Constant(0)), effect,
|
| graph()->start());
|
| effect = parameters[parameter_count++];
|
| @@ -390,14 +363,15 @@ class WasmFunctionWrapper : public HandleAndZoneScope,
|
| Node* call = graph()->NewNode(common()->Call(descriptor), parameter_count,
|
| parameters);
|
|
|
| - effect = graph()->NewNode(
|
| - machine()->Store(
|
| - StoreRepresentation(MachineTypeForC<ReturnType>().representation(),
|
| - WriteBarrierKind::kNoWriteBarrier)),
|
| - graph()->NewNode(common()->Parameter(WASM_RUNNER_MAX_NUM_PARAMETERS),
|
| - graph()->start()),
|
| - graph()->NewNode(common()->Int32Constant(0)), call, effect,
|
| - graph()->start());
|
| + if (!return_type.IsNone()) {
|
| + effect = graph()->NewNode(
|
| + machine()->Store(StoreRepresentation(
|
| + return_type.representation(), WriteBarrierKind::kNoWriteBarrier)),
|
| + graph()->NewNode(common()->Parameter(param_types.length()),
|
| + graph()->start()),
|
| + graph()->NewNode(common()->Int32Constant(0)), call, effect,
|
| + graph()->start());
|
| + }
|
| Node* zero = graph()->NewNode(common()->Int32Constant(0));
|
| Node* r = graph()->NewNode(
|
| common()->Return(), zero,
|
| @@ -406,6 +380,15 @@ class WasmFunctionWrapper : public HandleAndZoneScope,
|
| graph()->SetEnd(graph()->NewNode(common()->End(2), r, graph()->start()));
|
| }
|
|
|
| + template <typename ReturnType, typename... ParamTypes>
|
| + void Init(CallDescriptor* descriptor) {
|
| + std::array<MachineType, sizeof...(ParamTypes)> param_machine_types{
|
| + {MachineTypeForC<ParamTypes>()...}};
|
| + Vector<MachineType> param_vec(param_machine_types.data(),
|
| + param_machine_types.size());
|
| + Init(descriptor, MachineTypeForC<ReturnType>(), param_vec);
|
| + }
|
| +
|
| void SetInnerCode(Handle<Code> code_handle) {
|
| NodeProperties::ChangeOp(inner_code_node_,
|
| common()->HeapConstant(code_handle));
|
| @@ -419,12 +402,13 @@ class WasmFunctionWrapper : public HandleAndZoneScope,
|
| Linkage::GetSimplifiedCDescriptor(zone(), signature_, true);
|
|
|
| if (kPointerSize == 4) {
|
| + size_t num_params = signature_->parameter_count();
|
| // One additional parameter for the pointer of the return value.
|
| - Signature<MachineRepresentation>::Builder rep_builder(
|
| - zone(), 1, WASM_RUNNER_MAX_NUM_PARAMETERS + 1);
|
| + Signature<MachineRepresentation>::Builder rep_builder(zone(), 1,
|
| + num_params + 1);
|
|
|
| rep_builder.AddReturn(MachineRepresentation::kWord32);
|
| - for (int i = 0; i < WASM_RUNNER_MAX_NUM_PARAMETERS + 1; i++) {
|
| + for (size_t i = 0; i < num_params + 1; i++) {
|
| rep_builder.AddParam(MachineRepresentation::kWord32);
|
| }
|
| Int64Lowering r(graph(), machine(), common(), zone(),
|
| @@ -456,99 +440,37 @@ class WasmFunctionWrapper : public HandleAndZoneScope,
|
| Signature<MachineType>* signature_;
|
| };
|
|
|
| -// 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 interpretation (by adding to the interpreter manually).
|
| -class WasmFunctionCompiler : public HandleAndZoneScope,
|
| - private GraphAndBuilders {
|
| +// A helper for compiling WASM functions for testing.
|
| +// It contains the internal state for compilation (i.e. TurboFan graph) and
|
| +// interpretation (by adding to the interpreter manually).
|
| +class WasmFunctionCompiler : 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) {
|
| - ModuleBytesEnv empty_env(nullptr, nullptr, Vector<const byte>::empty());
|
| - interpreter_ = new WasmInterpreter(empty_env, 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),
|
| - descriptor_(nullptr),
|
| - testing_module_(module),
|
| - debug_name_(debug_name),
|
| - local_decls(main_zone(), sig),
|
| - 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 (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.
|
| - CallDescriptor* descriptor_;
|
| - TestingModule* testing_module_;
|
| - Vector<const char> debug_name_;
|
| - WasmFunction* function_;
|
| - LocalDeclEncoder local_decls;
|
| - SourcePositionTable source_position_table_;
|
| - WasmInterpreter* interpreter_;
|
| -
|
| - Isolate* isolate() { return main_isolate(); }
|
| + Isolate* isolate() { return testing_module_->isolate(); }
|
| Graph* graph() const { return main_graph_; }
|
| Zone* zone() const { return graph()->zone(); }
|
| CommonOperatorBuilder* common() { return &main_common_; }
|
| MachineOperatorBuilder* machine() { return &main_machine_; }
|
| - void InitializeDescriptor() {
|
| + CallDescriptor* descriptor() {
|
| if (descriptor_ == nullptr) {
|
| - descriptor_ = testing_module_->GetWasmCallDescriptor(main_zone(), sig);
|
| + descriptor_ = testing_module_->GetWasmCallDescriptor(zone(), sig);
|
| }
|
| + return descriptor_;
|
| }
|
| - 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(main_zone(), &start, &end);
|
| - TestBuildingGraph(main_zone(), &jsgraph, testing_module_, sig,
|
| - &source_position_table_, start, end);
|
| + local_decls.Prepend(zone(), &start, &end);
|
| if (interpreter_) {
|
| // Add the code to the interpreter.
|
| CHECK(interpreter_->SetFunctionCodeForTesting(function_, start, end));
|
| + return;
|
| }
|
| +
|
| + // Build the TurboFan graph.
|
| + TestBuildingGraph(zone(), &jsgraph, testing_module_, sig,
|
| + &source_position_table_, start, end);
|
| + Handle<Code> code = Compile();
|
| + testing_module_->SetFunctionCode(function_index(), code);
|
| }
|
|
|
| byte AllocateLocal(LocalType type) {
|
| @@ -558,13 +480,33 @@ class WasmFunctionCompiler : public HandleAndZoneScope,
|
| return result;
|
| }
|
|
|
| + void SetSigIndex(int sig_index) { function_->sig_index = sig_index; }
|
| +
|
| + private:
|
| + friend class WasmRunnerBase;
|
| +
|
| + explicit WasmFunctionCompiler(Zone* zone, FunctionSig* sig,
|
| + TestingModule* module)
|
| + : GraphAndBuilders(zone),
|
| + jsgraph(module->isolate(), this->graph(), this->common(), nullptr,
|
| + nullptr, this->machine()),
|
| + sig(sig),
|
| + descriptor_(nullptr),
|
| + testing_module_(module),
|
| + local_decls(zone, sig),
|
| + 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);
|
| + }
|
| +
|
| Handle<Code> Compile() {
|
| - InitializeDescriptor();
|
| - CallDescriptor* desc = descriptor_;
|
| + CallDescriptor* desc = descriptor();
|
| if (kPointerSize == 4) {
|
| desc = testing_module_->GetI32WasmCallDescriptor(this->zone(), desc);
|
| }
|
| - CompilationInfo info(debug_name_, this->isolate(), this->zone(),
|
| + CompilationInfo info(CStrVector("wasm"), this->isolate(), this->zone(),
|
| Code::ComputeFlags(Code::WASM_FUNCTION));
|
| std::unique_ptr<CompilationJob> job(Pipeline::NewWasmCompilationJob(
|
| &info, &jsgraph, desc, &source_position_table_, nullptr));
|
| @@ -594,82 +536,26 @@ class WasmFunctionCompiler : public HandleAndZoneScope,
|
| return code;
|
| }
|
|
|
| - uint32_t CompileAndAdd(uint16_t sig_index = 0) {
|
| - CHECK(testing_module_);
|
| - function_->sig_index = sig_index;
|
| - Handle<Code> code = Compile();
|
| - testing_module_->SetFunctionCode(function_index(), code);
|
| - return function_index();
|
| - }
|
| -
|
| - // Set the context, such that e.g. runtime functions can be called.
|
| - void SetModuleContext() {
|
| - if (!testing_module_->instance->context.is_null()) {
|
| - CHECK(testing_module_->instance->context.is_identical_to(
|
| - main_isolate()->native_context()));
|
| - return;
|
| - }
|
| - testing_module_->instance->context = main_isolate()->native_context();
|
| - }
|
| -};
|
| -
|
| -template <typename ReturnType>
|
| -union ReturnTypeUnion {
|
| - ReturnType value;
|
| - uint64_t trap;
|
| + JSGraph jsgraph;
|
| + FunctionSig* sig;
|
| + // The call descriptor is initialized when the function is compiled.
|
| + CallDescriptor* descriptor_;
|
| + TestingModule* testing_module_;
|
| + Vector<const char> debug_name_;
|
| + WasmFunction* function_;
|
| + LocalDeclEncoder local_decls;
|
| + SourcePositionTable source_position_table_;
|
| + WasmInterpreter* interpreter_;
|
| };
|
|
|
| -// A helper class to build graphs from Wasm bytecode, generate machine
|
| +// A helper class to build a module around Wasm bytecode, generate machine
|
| // code, and run that code.
|
| -template <typename ReturnType>
|
| -class WasmRunner {
|
| +class WasmRunnerBase : public HandleAndZoneScope {
|
| public:
|
| - WasmRunner(WasmExecutionMode execution_mode,
|
| - MachineType p0 = MachineType::None(),
|
| - MachineType p1 = MachineType::None(),
|
| - MachineType p2 = MachineType::None(),
|
| - MachineType p3 = MachineType::None())
|
| - : zone(&allocator_, ZONE_NAME),
|
| - compiled_(false),
|
| - signature_(MachineTypeForC<ReturnType>() == MachineType::None() ? 0 : 1,
|
| - GetParameterCount(p0, p1, p2, p3), storage_),
|
| - compiler_(&signature_, execution_mode) {
|
| - InitSigStorage(p0, p1, p2, p3);
|
| - }
|
| -
|
| - WasmRunner(TestingModule* module, MachineType p0 = MachineType::None(),
|
| - MachineType p1 = MachineType::None(),
|
| - MachineType p2 = MachineType::None(),
|
| - MachineType p3 = MachineType::None())
|
| - : zone(&allocator_, ZONE_NAME),
|
| - compiled_(false),
|
| - signature_(MachineTypeForC<ReturnType>() == MachineType::None() ? 0 : 1,
|
| - GetParameterCount(p0, p1, p2, p3), storage_),
|
| - compiler_(&signature_, module),
|
| - possible_nondeterminism_(false) {
|
| - DCHECK(module);
|
| - InitSigStorage(p0, p1, p2, p3);
|
| - }
|
| -
|
| - void InitSigStorage(MachineType p0, MachineType p1, MachineType p2,
|
| - MachineType p3) {
|
| - int index = 0;
|
| - MachineType ret = MachineTypeForC<ReturnType>();
|
| - if (ret != MachineType::None()) {
|
| - storage_[index++] = WasmOpcodes::LocalTypeFor(ret);
|
| - }
|
| - if (p0 != MachineType::None())
|
| - storage_[index++] = WasmOpcodes::LocalTypeFor(p0);
|
| - if (p1 != MachineType::None())
|
| - storage_[index++] = WasmOpcodes::LocalTypeFor(p1);
|
| - if (p2 != MachineType::None())
|
| - storage_[index++] = WasmOpcodes::LocalTypeFor(p2);
|
| - if (p3 != MachineType::None())
|
| - storage_[index++] = WasmOpcodes::LocalTypeFor(p3);
|
| -
|
| - compiler_.InitializeDescriptor();
|
| - wrapper_.Init(compiler_.descriptor(), p0, p1, p2, p3);
|
| - }
|
| + explicit WasmRunnerBase(WasmExecutionMode execution_mode, int num_params)
|
| + : zone_(&allocator_, ZONE_NAME),
|
| + module_(&zone_, execution_mode),
|
| + wrapper_(&zone_, num_params) {}
|
|
|
| // Builds a graph from the given Wasm code and generates the machine
|
| // code and call wrapper for that graph. This method must not be called
|
| @@ -677,102 +563,122 @@ class WasmRunner {
|
| void Build(const byte* start, const byte* end) {
|
| CHECK(!compiled_);
|
| compiled_ = true;
|
| - compiler_.Build(start, end);
|
| -
|
| - if (!interpret()) {
|
| - // Compile machine code and install it into the module.
|
| - Handle<Code> code = compiler_.Compile();
|
| + functions_[0]->Build(start, end);
|
| + }
|
|
|
| - if (compiler_.testing_module_) {
|
| - // Update the table of function code in the module.
|
| - compiler_.testing_module_->SetFunctionCode(
|
| - compiler_.function_->func_index, code);
|
| - }
|
| + // Resets the state for building the next function.
|
| + // The main function called will always be the first function.
|
| + template <typename ReturnType, typename... ParamTypes>
|
| + WasmFunctionCompiler& NewFunction() {
|
| + return NewFunction(CreateSig<ReturnType, ParamTypes...>());
|
| + }
|
|
|
| - wrapper_.SetInnerCode(code);
|
| - }
|
| + // Resets the state for building the next function.
|
| + // The main function called will be the last generated function.
|
| + // Returns the index of the previously built function.
|
| + WasmFunctionCompiler& NewFunction(FunctionSig* sig) {
|
| + functions_.emplace_back(new WasmFunctionCompiler(&zone_, sig, &module_));
|
| + return *functions_.back();
|
| }
|
|
|
| - ReturnType Call() {
|
| - if (interpret()) {
|
| - return CallInterpreter(Vector<WasmVal>(nullptr, 0));
|
| - } else {
|
| - return Call(0, 0, 0, 0);
|
| - }
|
| + byte AllocateLocal(LocalType type) {
|
| + return functions_[0]->AllocateLocal(type);
|
| }
|
|
|
| - template <typename P0>
|
| - ReturnType Call(P0 p0) {
|
| - if (interpret()) {
|
| - WasmVal args[] = {WasmVal(p0)};
|
| - return CallInterpreter(ArrayVector(args));
|
| - } else {
|
| - return Call(p0, 0, 0, 0);
|
| + WasmFunction* function() { return functions_[0]->function_; }
|
| + WasmInterpreter* interpreter() { return functions_[0]->interpreter_; }
|
| + bool possible_nondeterminism() { return possible_nondeterminism_; }
|
| + TestingModule& module() { return module_; }
|
| + Zone* zone() { return &zone_; }
|
| +
|
| + // Set the context, such that e.g. runtime functions can be called.
|
| + void SetModuleContext() {
|
| + if (!module_.instance->context.is_null()) {
|
| + CHECK(module_.instance->context.is_identical_to(
|
| + main_isolate()->native_context()));
|
| + return;
|
| }
|
| + module_.instance->context = main_isolate()->native_context();
|
| }
|
|
|
| - template <typename P0, typename P1>
|
| - ReturnType Call(P0 p0, P1 p1) {
|
| - if (interpret()) {
|
| - WasmVal args[] = {WasmVal(p0), WasmVal(p1)};
|
| - return CallInterpreter(ArrayVector(args));
|
| - } else {
|
| - return Call(p0, p1, 0, 0);
|
| + private:
|
| + FunctionSig* CreateSig(MachineType return_type,
|
| + Vector<MachineType> param_types) {
|
| + int return_count = return_type.IsNone() ? 0 : 1;
|
| + int param_count = param_types.length();
|
| +
|
| + // Allocate storage array in zone.
|
| + LocalType* sig_types =
|
| + zone_.NewArray<LocalType>(return_count + param_count);
|
| +
|
| + // Convert machine types to local types, and check that there are no
|
| + // MachineType::None()'s in the parameters.
|
| + int idx = 0;
|
| + if (return_count) sig_types[idx++] = WasmOpcodes::LocalTypeFor(return_type);
|
| + for (MachineType param : param_types) {
|
| + CHECK_NE(MachineType::None(), param);
|
| + sig_types[idx++] = WasmOpcodes::LocalTypeFor(param);
|
| }
|
| + return new (&zone_) FunctionSig(return_count, param_count, sig_types);
|
| }
|
|
|
| - template <typename P0, typename P1, typename P2>
|
| - ReturnType Call(P0 p0, P1 p1, P2 p2) {
|
| - if (interpret()) {
|
| - WasmVal args[] = {WasmVal(p0), WasmVal(p1), WasmVal(p2)};
|
| - return CallInterpreter(ArrayVector(args));
|
| - } else {
|
| - return Call(p0, p1, p2, 0);
|
| - }
|
| + template <typename ReturnType, typename... ParamTypes>
|
| + FunctionSig* CreateSig() {
|
| + std::array<MachineType, sizeof...(ParamTypes)> param_machine_types{
|
| + {MachineTypeForC<ParamTypes>()...}};
|
| + Vector<MachineType> param_vec(param_machine_types.data(),
|
| + param_machine_types.size());
|
| + return CreateSig(MachineTypeForC<ReturnType>(), param_vec);
|
| }
|
|
|
| + protected:
|
| + v8::internal::AccountingAllocator allocator_;
|
| + Zone zone_;
|
| + TestingModule module_;
|
| + std::vector<std::unique_ptr<WasmFunctionCompiler>> functions_;
|
| + WasmFunctionWrapper wrapper_;
|
| + bool compiled_ = false;
|
| + bool possible_nondeterminism_ = false;
|
| +
|
| + bool interpret() { return module_.execution_mode() == kExecuteInterpreted; }
|
| +
|
| + public:
|
| + // This field has to be static. Otherwise, gcc complains about the using in
|
| + // the lambda context below.
|
| static jmp_buf jump_buffer;
|
| - static int jump_value;
|
| - static ReturnTypeUnion<ReturnType> return_value;
|
| -
|
| - template <typename P0, typename P1, typename P2, typename P3>
|
| - void DoCall(P0 p0, P1 p1, P2 p2, P3 p3) {
|
| - auto trap_callback = []() {
|
| - WasmRunner<ReturnType>::return_value.trap = 0xdeadbeefdeadbeef;
|
| - longjmp(WasmRunner<ReturnType>::jump_buffer, 1);
|
| - };
|
| - set_trap_callback_for_testing(trap_callback);
|
| - CodeRunner<int32_t> runner(CcTest::InitIsolateOnce(),
|
| - wrapper_.GetWrapperCode(), wrapper_.signature());
|
| +};
|
|
|
| - int32_t result = runner.Call<void*, void*, void*, void*, void*>(
|
| - &p0, &p1, &p2, &p3, &WasmRunner<ReturnType>::return_value.value);
|
| - CHECK_EQ(WASM_WRAPPER_RETURN_VALUE, result);
|
| +template <typename ReturnType, typename... ParamTypes>
|
| +class WasmRunner : public WasmRunnerBase {
|
| + public:
|
| + explicit WasmRunner(WasmExecutionMode execution_mode)
|
| + : WasmRunnerBase(execution_mode, sizeof...(ParamTypes)) {
|
| + NewFunction<ReturnType, ParamTypes...>();
|
| + if (!interpret()) {
|
| + wrapper_.Init<ReturnType, ParamTypes...>(functions_[0]->descriptor());
|
| + }
|
| }
|
|
|
| - template <typename P0, typename P1, typename P2, typename P3>
|
| - ReturnType Call(P0 p0, P1 p1, P2 p2, P3 p3) {
|
| - if (interpret()) {
|
| - WasmVal args[] = {WasmVal(p0), WasmVal(p1), WasmVal(p2), WasmVal(p3)};
|
| - return CallInterpreter(ArrayVector(args));
|
| - } else {
|
| - // Use setjmp/longjmp to deal with traps in WebAssembly code.
|
| - WasmRunner<ReturnType>::jump_value =
|
| - setjmp(WasmRunner<ReturnType>::jump_buffer);
|
| - if (!WasmRunner<ReturnType>::jump_value) {
|
| - DoCall(p0, p1, p2, p3);
|
| - }
|
| - set_trap_callback_for_testing(nullptr);
|
| - return WasmRunner<ReturnType>::return_value.value;
|
| - }
|
| + ReturnType Call(ParamTypes... p) {
|
| + DCHECK(compiled_);
|
| + if (interpret()) return CallInterpreter(p...);
|
| +
|
| + // Use setjmp/longjmp to deal with traps in WebAssembly code.
|
| + // Make the return value volatile, to give defined semantics if accessed
|
| + // after setjmp.
|
| + volatile ReturnType return_value =
|
| + static_cast<ReturnType>(0xdeadbeefdeadbeef);
|
| + int jump_value = setjmp(WasmRunnerBase::jump_buffer);
|
| + // jump_value == 0 --> first return; jump_value == 1 --> longjmp happened.
|
| + if (!jump_value) DoCall(&return_value, p...);
|
| + return return_value;
|
| }
|
|
|
| - ReturnType CallInterpreter(Vector<WasmVal> args) {
|
| - CHECK_EQ(args.length(),
|
| - static_cast<int>(compiler_.function_->sig->parameter_count()));
|
| + ReturnType CallInterpreter(ParamTypes... p) {
|
| WasmInterpreter::Thread* thread = interpreter()->GetThread(0);
|
| thread->Reset();
|
| - thread->PushFrame(compiler_.function_, args.start());
|
| + std::array<WasmVal, sizeof...(p)> args{{WasmVal(p)...}};
|
| + thread->PushFrame(function(), args.data());
|
| if (thread->Run() == WasmInterpreter::FINISHED) {
|
| WasmVal val = thread->GetReturnValue();
|
| possible_nondeterminism_ |= thread->PossibleNondeterminism();
|
| @@ -783,45 +689,35 @@ class WasmRunner {
|
| return static_cast<ReturnType>(result);
|
| } else {
|
| // TODO(titzer): falling off end
|
| - ReturnType val = 0;
|
| - return val;
|
| + return ReturnType{0};
|
| }
|
| }
|
|
|
| - byte AllocateLocal(LocalType type) { return compiler_.AllocateLocal(type); }
|
| -
|
| - WasmFunction* function() { return compiler_.function_; }
|
| - WasmInterpreter* interpreter() { return compiler_.interpreter_; }
|
| - bool possible_nondeterminism() { return possible_nondeterminism_; }
|
| + private:
|
| + // Don't inline this function. The setjmp above should be followed immediately
|
| + // by a call.
|
| + V8_NOINLINE void DoCall(volatile ReturnType* return_value, ParamTypes... p) {
|
| + auto trap_callback = []() -> void {
|
| + set_trap_callback_for_testing(nullptr);
|
| + longjmp(WasmRunnerBase::jump_buffer, 1);
|
| + };
|
| + set_trap_callback_for_testing(trap_callback);
|
|
|
| - protected:
|
| - v8::internal::AccountingAllocator allocator_;
|
| - Zone zone;
|
| - bool compiled_;
|
| - LocalType storage_[WASM_RUNNER_MAX_NUM_PARAMETERS];
|
| - FunctionSig signature_;
|
| - WasmFunctionCompiler compiler_;
|
| - WasmFunctionWrapper<ReturnType> wrapper_;
|
| - bool possible_nondeterminism_;
|
| -
|
| - 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;
|
| - if (p1 == MachineType::None()) return 1;
|
| - if (p2 == MachineType::None()) return 2;
|
| - if (p3 == MachineType::None()) return 3;
|
| - return 4;
|
| + wrapper_.SetInnerCode(
|
| + module_.GetFunctionCode(functions_[0]->function_index()));
|
| + CodeRunner<int32_t> runner(CcTest::InitIsolateOnce(),
|
| + wrapper_.GetWrapperCode(), wrapper_.signature());
|
| + ReturnType return_value_local;
|
| + int32_t result = runner.Call(static_cast<void*>(&p)...,
|
| + static_cast<void*>(&return_value_local));
|
| + *return_value = return_value_local;
|
| + // If we arrive here, no trap happened.
|
| + CHECK_EQ(WASM_WRAPPER_RETURN_VALUE, result);
|
| }
|
| };
|
|
|
| -template <typename ReturnType>
|
| -jmp_buf WasmRunner<ReturnType>::jump_buffer;
|
| -template <typename ReturnType>
|
| -int WasmRunner<ReturnType>::jump_value;
|
| -template <typename ReturnType>
|
| -ReturnTypeUnion<ReturnType> WasmRunner<ReturnType>::return_value;
|
| +// Declare static variable.
|
| +jmp_buf WasmRunnerBase::jump_buffer;
|
|
|
| // A macro to define tests that run in different engine configurations.
|
| #define WASM_EXEC_TEST(name) \
|
|
|