diff options
author | Michaël Zasso <targos@protonmail.com> | 2017-03-21 10:16:54 +0100 |
---|---|---|
committer | Michaël Zasso <targos@protonmail.com> | 2017-03-25 09:44:10 +0100 |
commit | c459d8ea5d402c702948c860d9497b2230ff7e8a (patch) | |
tree | 56c282fc4d40e5cb613b47cf7be3ea0526ed5b6f /deps/v8/test/cctest/wasm/wasm-run-utils.h | |
parent | e0bc5a7361b1d29c3ed034155fd779ce6f44fb13 (diff) | |
download | node-new-c459d8ea5d402c702948c860d9497b2230ff7e8a.tar.gz |
deps: update V8 to 5.7.492.69
PR-URL: https://github.com/nodejs/node/pull/11752
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: Franziska Hinkelmann <franziska.hinkelmann@gmail.com>
Diffstat (limited to 'deps/v8/test/cctest/wasm/wasm-run-utils.h')
-rw-r--r-- | deps/v8/test/cctest/wasm/wasm-run-utils.h | 675 |
1 files changed, 348 insertions, 327 deletions
diff --git a/deps/v8/test/cctest/wasm/wasm-run-utils.h b/deps/v8/test/cctest/wasm/wasm-run-utils.h index 284b21c7c4..782e7d9635 100644 --- a/deps/v8/test/cctest/wasm/wasm-run-utils.h +++ b/deps/v8/test/cctest/wasm/wasm-run-utils.h @@ -5,9 +5,11 @@ #ifndef WASM_RUN_UTILS_H #define WASM_RUN_UTILS_H +#include <setjmp.h> #include <stdint.h> #include <stdlib.h> #include <string.h> +#include <array> #include <memory> #include "src/base/utils/random-number-generator.h" @@ -21,7 +23,8 @@ #include "src/compiler/pipeline.h" #include "src/compiler/wasm-compiler.h" #include "src/compiler/zone-stats.h" -#include "src/wasm/ast-decoder.h" +#include "src/wasm/function-body-decoder.h" +#include "src/wasm/wasm-external-refs.h" #include "src/wasm/wasm-interpreter.h" #include "src/wasm/wasm-js.h" #include "src/wasm/wasm-macro-gen.h" @@ -49,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, ...) \ @@ -71,23 +73,26 @@ const uint32_t kMaxGlobalsSize = 128; // {WasmInstance}. class TestingModule : public ModuleEnv { public: - explicit TestingModule(WasmExecutionMode mode = kExecuteCompiled) - : execution_mode_(mode), + explicit TestingModule(Zone* zone, WasmExecutionMode mode = kExecuteCompiled) + : ModuleEnv(&module_, &instance_), + execution_mode_(mode), instance_(&module_), isolate_(CcTest::InitIsolateOnce()), global_offset(0), interpreter_(mode == kExecuteInterpreted - ? new WasmInterpreter(&instance_, &allocator_) + ? new WasmInterpreter( + ModuleBytesEnv(&module_, &instance_, + Vector<const byte>::empty()), + zone->allocator()) : nullptr) { - module = &module_; - instance = &instance_; + WasmJs::Install(isolate_); instance->module = &module_; instance->globals_start = global_data; module_.globals_size = kMaxGlobalsSize; instance->mem_start = nullptr; instance->mem_size = 0; - origin = kWasmOrigin; memset(global_data, 0, sizeof(global_data)); + instance_object_ = InitInstanceObject(); } ~TestingModule() { @@ -97,13 +102,15 @@ class TestingModule : public ModuleEnv { if (interpreter_) delete interpreter_; } - void ChangeOriginToAsmjs() { origin = kAsmJsOrigin; } + void ChangeOriginToAsmjs() { module_.origin = kAsmJsOrigin; } byte* AddMemory(uint32_t size) { + CHECK(!module_.has_memory); CHECK_NULL(instance->mem_start); - CHECK_EQ(0u, instance->mem_size); + CHECK_EQ(0, instance->mem_size); + module_.has_memory = true; instance->mem_start = reinterpret_cast<byte*>(malloc(size)); - CHECK(instance->mem_start); + CHECK(size == 0 || instance->mem_start); memset(instance->mem_start, 0, size); instance->mem_size = size; return raw_mem_start<byte>(); @@ -116,7 +123,8 @@ class TestingModule : public ModuleEnv { } template <typename T> - T* AddGlobal(LocalType type) { + T* AddGlobal( + ValueType type = WasmOpcodes::ValueTypeFor(MachineTypeForC<T>())) { const WasmGlobal* global = AddGlobal(type); return reinterpret_cast<T*>(instance->globals_start + global->offset); } @@ -176,7 +184,11 @@ class TestingModule : public ModuleEnv { rng.NextBytes(raw, end - raw); } - uint32_t AddFunction(FunctionSig* sig, Handle<Code> code) { + void SetMaxMemPages(uint32_t max_mem_pages) { + module_.max_mem_pages = max_mem_pages; + } + + uint32_t AddFunction(FunctionSig* sig, Handle<Code> code, const char* name) { if (module->functions.size() == 0) { // TODO(titzer): Reserving space here to avoid the underlying WasmFunction // structs from moving. @@ -184,6 +196,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, false}); + if (name) { + Vector<const byte> name_vec = Vector<const byte>::cast(CStrVector(name)); + module_.functions.back().name_offset = AddBytes(name_vec); + module_.functions.back().name_length = name_vec.length(); + } instance->function_code.push_back(code); if (interpreter_) { const WasmFunction* function = &module->functions.back(); @@ -197,26 +214,24 @@ class TestingModule : public ModuleEnv { uint32_t AddJsFunction(FunctionSig* sig, const char* source) { Handle<JSFunction> jsfunc = Handle<JSFunction>::cast(v8::Utils::OpenHandle( *v8::Local<v8::Function>::Cast(CompileRun(source)))); - uint32_t index = AddFunction(sig, Handle<Code>::null()); - Handle<Code> code = - CompileWasmToJSWrapper(isolate_, jsfunc, sig, index, - Handle<String>::null(), Handle<String>::null()); + uint32_t index = AddFunction(sig, Handle<Code>::null(), nullptr); + Handle<Code> code = CompileWasmToJSWrapper( + isolate_, jsfunc, sig, index, Handle<String>::null(), + Handle<String>::null(), module->origin); instance->function_code[index] = code; return index; } Handle<JSFunction> WrapCode(uint32_t index) { // Wrap the code so it can be called as a JS function. - Handle<String> name = isolate_->factory()->NewStringFromStaticChars("main"); Handle<WasmInstanceObject> instance_obj(0, isolate_); Handle<Code> code = instance->function_code[index]; - WasmJs::InstallWasmMapsIfNeeded(isolate_, isolate_->native_context()); Handle<Code> ret_code = - compiler::CompileJSToWasmWrapper(isolate_, this, code, index); + compiler::CompileJSToWasmWrapper(isolate_, &module_, code, index); Handle<JSFunction> ret = WasmExportedFunction::New( - isolate_, instance_obj, name, ret_code, + isolate_, instance_obj, MaybeHandle<String>(), static_cast<int>(index), static_cast<int>(this->module->functions[index].sig->parameter_count()), - static_cast<int>(index)); + ret_code); return ret; } @@ -238,40 +253,59 @@ class TestingModule : public ModuleEnv { } instance->function_tables.push_back( - isolate_->factory()->NewFixedArray(table_size * 2)); + isolate_->factory()->NewFixedArray(table_size)); + instance->signature_tables.push_back( + isolate_->factory()->NewFixedArray(table_size)); } 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]; - Handle<FixedArray> array = instance->function_tables[i]; + Handle<FixedArray> function_table = instance->function_tables[i]; + Handle<FixedArray> signature_table = instance->signature_tables[i]; int table_size = static_cast<int>(table.values.size()); for (int j = 0; j < table_size; j++) { WasmFunction& function = module_.functions[table.values[j]]; - array->set(j, Smi::FromInt(table.map.Find(function.sig))); - array->set(j + table_size, - *instance->function_code[function.func_index]); + signature_table->set(j, Smi::FromInt(table.map.Find(function.sig))); + function_table->set(j, *instance->function_code[function.func_index]); } } } + uint32_t AddBytes(Vector<const byte> bytes) { + Handle<SeqOneByteString> old_bytes( + instance_object_->compiled_module()->module_bytes(), isolate_); + uint32_t old_size = static_cast<uint32_t>(old_bytes->length()); + ScopedVector<byte> new_bytes(old_size + bytes.length()); + memcpy(new_bytes.start(), old_bytes->GetChars(), old_size); + memcpy(new_bytes.start() + old_size, bytes.start(), bytes.length()); + Handle<SeqOneByteString> new_bytes_str = Handle<SeqOneByteString>::cast( + isolate_->factory()->NewStringFromOneByte(new_bytes).ToHandleChecked()); + instance_object_->compiled_module()->shared()->set_module_bytes( + *new_bytes_str); + return old_size; + } + WasmFunction* GetFunctionAt(int index) { return &module_.functions[index]; } WasmInterpreter* interpreter() { return interpreter_; } WasmExecutionMode execution_mode() { return execution_mode_; } + Isolate* isolate() { return isolate_; } + Handle<WasmInstanceObject> instance_object() { return instance_object_; } 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_; + Handle<WasmInstanceObject> instance_object_; - const WasmGlobal* AddGlobal(LocalType type) { + const WasmGlobal* AddGlobal(ValueType type) { byte size = WasmOpcodes::MemSize(WasmOpcodes::MachineTypeFor(type)); global_offset = (global_offset + size - 1) & ~(size - 1); // align module_.globals.push_back( @@ -281,21 +315,43 @@ class TestingModule : public ModuleEnv { CHECK_LT(global_offset, kMaxGlobalsSize); return &module->globals.back(); } + + Handle<WasmInstanceObject> InitInstanceObject() { + Handle<SeqOneByteString> empty_string = Handle<SeqOneByteString>::cast( + isolate_->factory()->NewStringFromOneByte({}).ToHandleChecked()); + Handle<Managed<wasm::WasmModule>> module_wrapper = + Managed<wasm::WasmModule>::New(isolate_, &module_, false); + Handle<Script> script = + isolate_->factory()->NewScript(isolate_->factory()->empty_string()); + script->set_type(Script::TYPE_WASM); + Handle<WasmSharedModuleData> shared_module_data = + WasmSharedModuleData::New(isolate_, module_wrapper, empty_string, + script, Handle<ByteArray>::null()); + Handle<WasmCompiledModule> compiled_module = + WasmCompiledModule::New(isolate_, shared_module_data); + // Minimally initialize the compiled module such that IsWasmCompiledModule + // passes. + // If tests need more (correct) information, add it later. + compiled_module->set_min_mem_pages(0); + compiled_module->set_max_mem_pages(Smi::kMaxValue); + DCHECK(WasmCompiledModule::IsWasmCompiledModule(*compiled_module)); + return WasmInstanceObject::New(isolate_, compiled_module); + } }; inline void TestBuildingGraph(Zone* zone, JSGraph* jsgraph, ModuleEnv* module, FunctionSig* sig, SourcePositionTable* source_position_table, const byte* start, const byte* end) { - compiler::WasmGraphBuilder builder(zone, jsgraph, sig, source_position_table); + compiler::WasmGraphBuilder builder(module, zone, jsgraph, sig, + source_position_table); DecodeResult result = - BuildTFGraph(zone->allocator(), &builder, module, sig, start, end); + BuildTFGraph(zone->allocator(), &builder, sig, start, end); if (result.failed()) { if (!FLAG_trace_wasm_decoder) { // Retry the compilation with the tracing flag on, to help in debugging. FLAG_trace_wasm_decoder = true; - result = - BuildTFGraph(zone->allocator(), &builder, module, sig, start, end); + result = BuildTFGraph(zone->allocator(), &builder, sig, start, end); } ptrdiff_t pc = result.error_pc - result.start; @@ -307,39 +363,34 @@ inline void TestBuildingGraph(Zone* zone, JSGraph* jsgraph, ModuleEnv* module, FATAL(str.str().c_str()); } builder.Int64LoweringForTesting(); - builder.SimdScalarLoweringForTesting(); + if (!CpuFeatures::SupportsSimd128()) { + builder.SimdScalarLoweringForTesting(); + } } -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; @@ -348,34 +399,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()) { - 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()) { + int param_idx = 0; + for (MachineType t : param_types) { + DCHECK_NE(MachineType::None(), t); 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++]; @@ -386,14 +415,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, @@ -402,6 +432,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)); @@ -415,12 +454,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(), @@ -452,129 +492,108 @@ 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) { - 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), - 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); + size_t locals_size = local_decls.Size(); + size_t total_size = end - start + locals_size + 1; + byte* buffer = static_cast<byte*>(zone()->New(total_size)); + // Prepend the local decls to the code. + local_decls.Emit(buffer); + // Emit the code. + memcpy(buffer + locals_size, start, end - start); + // Append an extra end opcode. + buffer[total_size - 1] = kExprEnd; + + start = buffer; + end = buffer + total_size; + + CHECK_GE(kMaxInt, end - start); + int len = static_cast<int>(end - start); + function_->code_start_offset = + testing_module_->AddBytes(Vector<const byte>(start, len)); + function_->code_end_offset = function_->code_start_offset + len; + 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) { + byte AllocateLocal(ValueType 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, const char* name) + : 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(), name); + 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, graph(), desc, &source_position_table_)); + &info, &jsgraph, desc, &source_position_table_, nullptr, false)); 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. + // Deopt data holds <WeakCell<wasm_instance>, func_index>. DCHECK(code->deoptimization_data() == nullptr || code->deoptimization_data()->length() == 0); Handle<FixedArray> deopt_data = isolate()->factory()->NewFixedArray(2, TENURED); + Handle<Object> weak_instance = + isolate()->factory()->NewWeakCell(testing_module_->instance_object()); + deopt_data->set(0, *weak_instance); deopt_data->set(1, Smi::FromInt(static_cast<int>(function_index()))); deopt_data->set_length(2); code->set_deoptimization_data(*deopt_data); @@ -589,76 +608,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(); - } + 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 @@ -666,83 +635,125 @@ class WasmRunner { void Build(const byte* start, const byte* end) { CHECK(!compiled_); compiled_ = true; - compiler_.Build(start, end); + functions_[0]->Build(start, end); + } - if (!interpret()) { - // Compile machine code and install it into the module. - Handle<Code> code = compiler_.Compile(); + // 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(const char* name = nullptr) { + return NewFunction(CreateSig<ReturnType, ParamTypes...>(), name); + } - 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 be the last generated function. + // Returns the index of the previously built function. + WasmFunctionCompiler& NewFunction(FunctionSig* sig, + const char* name = nullptr) { + functions_.emplace_back( + new WasmFunctionCompiler(&zone_, sig, &module_, name)); + return *functions_.back(); + } - wrapper_.SetInnerCode(code); - } + byte AllocateLocal(ValueType type) { + return functions_[0]->AllocateLocal(type); } - ReturnType Call() { - if (interpret()) { - return CallInterpreter(Vector<WasmVal>(nullptr, 0)); - } else { - return Call(0, 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> - ReturnType Call(P0 p0) { - if (interpret()) { - WasmVal args[] = {WasmVal(p0)}; - return CallInterpreter(ArrayVector(args)); - } else { - return Call(p0, 0, 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. + ValueType* sig_types = + zone_.NewArray<ValueType>(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::ValueTypeFor(return_type); + for (MachineType param : param_types) { + CHECK_NE(MachineType::None(), param); + sig_types[idx++] = WasmOpcodes::ValueTypeFor(param); } + return new (&zone_) FunctionSig(return_count, param_count, sig_types); } - 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 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); } - 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); + 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; +}; + +template <typename ReturnType, typename... ParamTypes> +class WasmRunner : public WasmRunnerBase { + public: + explicit WasmRunner(WasmExecutionMode execution_mode, + const char* main_fn_name = "main") + : WasmRunnerBase(execution_mode, sizeof...(ParamTypes)) { + NewFunction<ReturnType, ParamTypes...>(main_fn_name); + 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 { - 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 Call(ParamTypes... p) { + DCHECK(compiled_); + if (interpret()) return CallInterpreter(p...); + + // Use setjmp/longjmp to deal with traps in WebAssembly code. + ReturnType return_value = static_cast<ReturnType>(0xdeadbeefdeadbeef); + static int setjmp_ret; + setjmp_ret = setjmp(WasmRunnerBase::jump_buffer); + // setjmp returns 0 on the first return, 1 (passed to longjmp) after trap. + if (setjmp_ret == 0) { + DoCall(static_cast<void*>(&p)..., static_cast<void*>(&return_value)); } + 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(); @@ -753,49 +764,59 @@ 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_; } - - 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; + private: + // Don't inline this function. The setjmp above should be followed immediately + // by a call. + template <typename... Ptrs> + V8_NOINLINE void DoCall(Ptrs... ptrs) { + 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()); + int32_t result = runner.Call(ptrs...); + // If we arrive here, no trap happened. + CHECK_EQ(WASM_WRAPPER_RETURN_VALUE, result); } }; +// Declare static variable. +jmp_buf WasmRunnerBase::jump_buffer; + // A macro to define tests that run in different engine configurations. -// 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(WasmExecutionMode execution_mode); \ TEST(RunWasmCompiled_##name) { RunWasm_##name(kExecuteCompiled); } \ TEST(RunWasmInterpreted_##name) { RunWasm_##name(kExecuteInterpreted); } \ void RunWasm_##name(WasmExecutionMode execution_mode) +#define WASM_EXEC_TEST_WITH_TRAP(name) \ + void RunWasm_##name(WasmExecutionMode execution_mode); \ + TEST(RunWasmCompiled_##name) { RunWasm_##name(kExecuteCompiled); } \ + void RunWasm_##name(WasmExecutionMode execution_mode); \ + TEST(RunWasmCompiledWithTrapIf_##name) { \ + bool trap_if = FLAG_wasm_trap_if; \ + FLAG_wasm_trap_if = true; \ + RunWasm_##name(kExecuteCompiled); \ + FLAG_wasm_trap_if = trap_if; \ + } \ + TEST(RunWasmInterpreted_##name) { RunWasm_##name(kExecuteInterpreted); } \ + void RunWasm_##name(WasmExecutionMode execution_mode) + +#define WASM_EXEC_COMPILED_TEST(name) \ + void RunWasm_##name(WasmExecutionMode execution_mode); \ + TEST(RunWasmCompiled_##name) { RunWasm_##name(kExecuteCompiled); } \ + void RunWasm_##name(WasmExecutionMode execution_mode) + } // namespace #endif |