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) \ |