| 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 e2b8d057a9625d56afee3b029bd16343a03e72bf..ed8e893a7fcf7921340766d8ac0352e35aa704a3 100644
|
| --- a/test/cctest/wasm/wasm-run-utils.h
|
| +++ b/test/cctest/wasm/wasm-run-utils.h
|
| @@ -9,7 +9,6 @@
|
| #include <stdint.h>
|
| #include <stdlib.h>
|
| #include <string.h>
|
| -#include <array>
|
| #include <memory>
|
|
|
| #include "src/base/utils/random-number-generator.h"
|
| @@ -52,6 +51,7 @@
|
| 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 @@
|
| // {WasmInstance}.
|
| class TestingModule : public ModuleEnv {
|
| public:
|
| - explicit TestingModule(Zone* zone, WasmExecutionMode mode = kExecuteCompiled)
|
| + explicit TestingModule(WasmExecutionMode mode = kExecuteCompiled)
|
| : ModuleEnv(&module_, &instance_),
|
| execution_mode_(mode),
|
| instance_(&module_),
|
| @@ -83,7 +83,7 @@
|
| ? new WasmInterpreter(
|
| ModuleBytesEnv(&module_, &instance_,
|
| Vector<const byte>::empty()),
|
| - zone->allocator())
|
| + &allocator_)
|
| : nullptr) {
|
| instance->module = &module_;
|
| instance->globals_start = global_data;
|
| @@ -104,7 +104,7 @@
|
|
|
| byte* AddMemory(uint32_t size) {
|
| CHECK_NULL(instance->mem_start);
|
| - CHECK_EQ(0, instance->mem_size);
|
| + CHECK_EQ(0u, instance->mem_size);
|
| instance->mem_start = reinterpret_cast<byte*>(malloc(size));
|
| CHECK(instance->mem_start);
|
| memset(instance->mem_start, 0, size);
|
| @@ -119,8 +119,7 @@
|
| }
|
|
|
| template <typename T>
|
| - T* AddGlobal(
|
| - LocalType type = WasmOpcodes::LocalTypeFor(MachineTypeForC<T>())) {
|
| + T* AddGlobal(LocalType type) {
|
| const WasmGlobal* global = AddGlobal(type);
|
| return reinterpret_cast<T*>(instance->globals_start + global->offset);
|
| }
|
| @@ -245,7 +244,6 @@
|
| }
|
|
|
| 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];
|
| @@ -264,13 +262,13 @@
|
|
|
| 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_;
|
| @@ -316,29 +314,36 @@
|
| }
|
| }
|
|
|
| -class WasmFunctionWrapper : private GraphAndBuilders {
|
| +template <typename ReturnType>
|
| +class WasmFunctionWrapper : public HandleAndZoneScope,
|
| + private GraphAndBuilders {
|
| public:
|
| - explicit WasmFunctionWrapper(Zone* zone, int num_params)
|
| - : GraphAndBuilders(zone), inner_code_node_(nullptr), signature_(nullptr) {
|
| + WasmFunctionWrapper()
|
| + : GraphAndBuilders(main_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, num_params + 1);
|
| + Signature<MachineType>::Builder sig_builder(
|
| + zone(), 1, WASM_RUNNER_MAX_NUM_PARAMETERS + 1);
|
|
|
| sig_builder.AddReturn(MachineType::Int32());
|
| - for (int i = 0; i < num_params + 1; i++) {
|
| + for (int i = 0; i < WASM_RUNNER_MAX_NUM_PARAMETERS + 1; i++) {
|
| sig_builder.AddParam(MachineType::Pointer());
|
| }
|
| signature_ = sig_builder.Build();
|
| }
|
|
|
| - 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.
|
| + 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.
|
|
|
| // Function, effect, and control.
|
| - Node** parameters = zone()->NewArray<Node*>(param_types.length() + 3);
|
| + Node** parameters =
|
| + zone()->template NewArray<Node*>(WASM_RUNNER_MAX_NUM_PARAMETERS + 3);
|
| graph()->SetStart(graph()->NewNode(common()->Start(6)));
|
| Node* effect = graph()->start();
|
| int parameter_count = 0;
|
| @@ -347,12 +352,34 @@
|
| inner_code_node_ = graph()->NewNode(common()->Int32Constant(0));
|
| parameters[parameter_count++] = inner_code_node_;
|
|
|
| - int param_idx = 0;
|
| - for (MachineType t : param_types) {
|
| - DCHECK_NE(MachineType::None(), t);
|
| + if (p0 != MachineType::None()) {
|
| parameters[parameter_count] = graph()->NewNode(
|
| - machine()->Load(t),
|
| - graph()->NewNode(common()->Parameter(param_idx++), graph()->start()),
|
| + 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()) {
|
| + 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()),
|
| graph()->NewNode(common()->Int32Constant(0)), effect,
|
| graph()->start());
|
| effect = parameters[parameter_count++];
|
| @@ -363,15 +390,14 @@
|
| Node* call = graph()->NewNode(common()->Call(descriptor), parameter_count,
|
| parameters);
|
|
|
| - 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());
|
| - }
|
| + 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());
|
| Node* zero = graph()->NewNode(common()->Int32Constant(0));
|
| Node* r = graph()->NewNode(
|
| common()->Return(), zero,
|
| @@ -380,15 +406,6 @@
|
| 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));
|
| @@ -402,13 +419,12 @@
|
| 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,
|
| - num_params + 1);
|
| + Signature<MachineRepresentation>::Builder rep_builder(
|
| + zone(), 1, WASM_RUNNER_MAX_NUM_PARAMETERS + 1);
|
|
|
| rep_builder.AddReturn(MachineRepresentation::kWord32);
|
| - for (size_t i = 0; i < num_params + 1; i++) {
|
| + for (int i = 0; i < WASM_RUNNER_MAX_NUM_PARAMETERS + 1; i++) {
|
| rep_builder.AddParam(MachineRepresentation::kWord32);
|
| }
|
| Int64Lowering r(graph(), machine(), common(), zone(),
|
| @@ -440,60 +456,52 @@
|
| Signature<MachineType>* signature_;
|
| };
|
|
|
| -// 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 {
|
| +// 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 {
|
| public:
|
| - 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_; }
|
| - CallDescriptor* descriptor() {
|
| - if (descriptor_ == nullptr) {
|
| - descriptor_ = testing_module_->GetWasmCallDescriptor(zone(), sig);
|
| - }
|
| - return descriptor_;
|
| - }
|
| - uint32_t function_index() { return function_->func_index; }
|
| -
|
| - void Build(const byte* start, const byte* 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) {
|
| - uint32_t index = local_decls.AddLocals(1, type);
|
| - byte result = static_cast<byte>(index);
|
| - DCHECK_EQ(index, result);
|
| - 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,
|
| + 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),
|
| - local_decls(zone, sig),
|
| + 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.
|
| @@ -501,41 +509,13 @@
|
| function_ = testing_module_->GetFunctionAt(index);
|
| }
|
|
|
| - Handle<Code> Compile() {
|
| - CallDescriptor* desc = descriptor();
|
| - if (kPointerSize == 4) {
|
| - desc = testing_module_->GetI32WasmCallDescriptor(this->zone(), desc);
|
| - }
|
| - 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));
|
| - if (job->ExecuteJob() != CompilationJob::SUCCEEDED ||
|
| - job->FinalizeJob() != CompilationJob::SUCCEEDED)
|
| - return Handle<Code>::null();
|
| -
|
| - 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.
|
| - 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(static_cast<int>(function_index())));
|
| - deopt_data->set_length(2);
|
| - code->set_deoptimization_data(*deopt_data);
|
| -
|
| -#ifdef ENABLE_DISASSEMBLER
|
| - if (FLAG_print_opt_code) {
|
| - OFStream os(stdout);
|
| - code->Disassemble("wasm code", os);
|
| - }
|
| -#endif
|
| -
|
| - return code;
|
| - }
|
| -
|
| + ~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.
|
| @@ -546,16 +526,150 @@
|
| LocalDeclEncoder local_decls;
|
| SourcePositionTable source_position_table_;
|
| WasmInterpreter* interpreter_;
|
| +
|
| + Isolate* isolate() { return main_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() {
|
| + if (descriptor_ == nullptr) {
|
| + descriptor_ = testing_module_->GetWasmCallDescriptor(main_zone(), sig);
|
| + }
|
| + }
|
| + 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);
|
| + if (interpreter_) {
|
| + // Add the code to the interpreter.
|
| + CHECK(interpreter_->SetFunctionCodeForTesting(function_, start, end));
|
| + }
|
| + }
|
| +
|
| + byte AllocateLocal(LocalType type) {
|
| + uint32_t index = local_decls.AddLocals(1, type);
|
| + byte result = static_cast<byte>(index);
|
| + DCHECK_EQ(index, result);
|
| + return result;
|
| + }
|
| +
|
| + Handle<Code> Compile() {
|
| + InitializeDescriptor();
|
| + CallDescriptor* desc = descriptor_;
|
| + if (kPointerSize == 4) {
|
| + desc = testing_module_->GetI32WasmCallDescriptor(this->zone(), desc);
|
| + }
|
| + CompilationInfo info(debug_name_, this->isolate(), this->zone(),
|
| + Code::ComputeFlags(Code::WASM_FUNCTION));
|
| + std::unique_ptr<CompilationJob> job(Pipeline::NewWasmCompilationJob(
|
| + &info, &jsgraph, desc, &source_position_table_, nullptr));
|
| + if (job->ExecuteJob() != CompilationJob::SUCCEEDED ||
|
| + job->FinalizeJob() != CompilationJob::SUCCEEDED)
|
| + return Handle<Code>::null();
|
| +
|
| + 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.
|
| + 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(static_cast<int>(function_index())));
|
| + deopt_data->set_length(2);
|
| + code->set_deoptimization_data(*deopt_data);
|
| +
|
| +#ifdef ENABLE_DISASSEMBLER
|
| + if (FLAG_print_opt_code) {
|
| + OFStream os(stdout);
|
| + code->Disassemble("wasm code", os);
|
| + }
|
| +#endif
|
| +
|
| + 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();
|
| + }
|
| };
|
|
|
| -// A helper class to build a module around Wasm bytecode, generate machine
|
| +template <typename ReturnType>
|
| +union ReturnTypeUnion {
|
| + ReturnType value;
|
| + uint64_t trap;
|
| +};
|
| +
|
| +// A helper class to build graphs from Wasm bytecode, generate machine
|
| // code, and run that code.
|
| -class WasmRunnerBase : public HandleAndZoneScope {
|
| +template <typename ReturnType>
|
| +class WasmRunner {
|
| public:
|
| - explicit WasmRunnerBase(WasmExecutionMode execution_mode, int num_params)
|
| - : zone_(&allocator_, ZONE_NAME),
|
| - module_(&zone_, execution_mode),
|
| - wrapper_(&zone_, num_params) {}
|
| + 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);
|
| + }
|
|
|
| // 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
|
| @@ -563,118 +677,102 @@
|
| void Build(const byte* start, const byte* end) {
|
| CHECK(!compiled_);
|
| compiled_ = true;
|
| - functions_[0]->Build(start, end);
|
| - }
|
| -
|
| - // 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...>());
|
| - }
|
| -
|
| - // 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();
|
| - }
|
| -
|
| - byte AllocateLocal(LocalType type) {
|
| - return functions_[0]->AllocateLocal(type);
|
| - }
|
| -
|
| - 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();
|
| - }
|
| -
|
| - 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 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.
|
| + compiler_.Build(start, end);
|
| +
|
| + 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);
|
| + }
|
| + }
|
| +
|
| + ReturnType Call() {
|
| + if (interpret()) {
|
| + return CallInterpreter(Vector<WasmVal>(nullptr, 0));
|
| + } else {
|
| + return Call(0, 0, 0, 0);
|
| + }
|
| + }
|
| +
|
| + template <typename P0>
|
| + ReturnType Call(P0 p0) {
|
| + 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) {
|
| + 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) {
|
| + if (interpret()) {
|
| + WasmVal args[] = {WasmVal(p0), WasmVal(p1), WasmVal(p2)};
|
| + return CallInterpreter(ArrayVector(args));
|
| + } else {
|
| + return Call(p0, p1, p2, 0);
|
| + }
|
| + }
|
| +
|
| static jmp_buf jump_buffer;
|
| -};
|
| -
|
| -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());
|
| - }
|
| - }
|
| -
|
| - ReturnType Call(ParamTypes... p) {
|
| - DCHECK(compiled_);
|
| - if (interpret()) return CallInterpreter(p...);
|
| -
|
| - // Use setjmp/longjmp to deal with traps in WebAssembly code.
|
| - int jump_value = setjmp(WasmRunnerBase::jump_buffer);
|
| - // jump_value == 0 --> first return; jump_value == 1 --> longjmp happened.
|
| - return jump_value ? static_cast<ReturnType>(0xdeadbeefdeadbeef)
|
| - : DoCall(p...);
|
| - }
|
| -
|
| - ReturnType CallInterpreter(ParamTypes... p) {
|
| + 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 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 CallInterpreter(Vector<WasmVal> args) {
|
| + CHECK_EQ(args.length(),
|
| + static_cast<int>(compiler_.function_->sig->parameter_count()));
|
| WasmInterpreter::Thread* thread = interpreter()->GetThread(0);
|
| thread->Reset();
|
| - std::array<WasmVal, sizeof...(p)> args{{WasmVal(p)...}};
|
| - thread->PushFrame(function(), args.data());
|
| + thread->PushFrame(compiler_.function_, args.start());
|
| if (thread->Run() == WasmInterpreter::FINISHED) {
|
| WasmVal val = thread->GetReturnValue();
|
| possible_nondeterminism_ |= thread->PossibleNondeterminism();
|
| @@ -685,33 +783,45 @@
|
| return static_cast<ReturnType>(result);
|
| } else {
|
| // TODO(titzer): falling off end
|
| - return ReturnType{0};
|
| - }
|
| - }
|
| -
|
| - private:
|
| - ReturnType DoCall(ParamTypes... p) {
|
| - auto trap_callback = []() -> void {
|
| - set_trap_callback_for_testing(nullptr);
|
| - longjmp(WasmRunnerBase::jump_buffer, 1);
|
| - };
|
| - set_trap_callback_for_testing(trap_callback);
|
| -
|
| - wrapper_.SetInnerCode(
|
| - module_.GetFunctionCode(functions_[0]->function_index()));
|
| - CodeRunner<int32_t> runner(CcTest::InitIsolateOnce(),
|
| - wrapper_.GetWrapperCode(), wrapper_.signature());
|
| - ReturnType return_value;
|
| - int32_t result = runner.Call(static_cast<void*>(&p)...,
|
| - static_cast<void*>(&return_value));
|
| - // If we arrive here, no trap happened.
|
| - CHECK_EQ(WASM_WRAPPER_RETURN_VALUE, result);
|
| - return return_value;
|
| + ReturnType val = 0;
|
| + return val;
|
| + }
|
| + }
|
| +
|
| + byte AllocateLocal(LocalType type) { return compiler_.AllocateLocal(type); }
|
| +
|
| + WasmFunction* function() { return compiler_.function_; }
|
| + WasmInterpreter* interpreter() { return compiler_.interpreter_; }
|
| + bool possible_nondeterminism() { return possible_nondeterminism_; }
|
| +
|
| + 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;
|
| }
|
| };
|
|
|
| -// Declare static variable.
|
| -jmp_buf WasmRunnerBase::jump_buffer;
|
| +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;
|
|
|
| // A macro to define tests that run in different engine configurations.
|
| #define WASM_EXEC_TEST(name) \
|
|
|