| // Copyright 2017 the V8 project authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "test/cctest/wasm/wasm-run-utils.h" |
| |
| #include "src/codegen/assembler-inl.h" |
| #include "src/diagnostics/code-tracer.h" |
| #include "src/heap/heap-inl.h" |
| #include "src/wasm/graph-builder-interface.h" |
| #include "src/wasm/module-compiler.h" |
| #include "src/wasm/wasm-import-wrapper-cache.h" |
| #include "src/wasm/wasm-memory.h" |
| #include "src/wasm/wasm-objects-inl.h" |
| |
| namespace v8 { |
| namespace internal { |
| namespace wasm { |
| |
| TestingModuleBuilder::TestingModuleBuilder( |
| Zone* zone, ManuallyImportedJSFunction* maybe_import, ExecutionTier tier, |
| RuntimeExceptionSupport exception_support, LowerSimd lower_simd) |
| : test_module_(std::make_shared<WasmModule>()), |
| test_module_ptr_(test_module_.get()), |
| isolate_(CcTest::InitIsolateOnce()), |
| enabled_features_(WasmFeaturesFromIsolate(isolate_)), |
| execution_tier_(tier), |
| runtime_exception_support_(exception_support), |
| lower_simd_(lower_simd) { |
| WasmJs::Install(isolate_, true); |
| test_module_->untagged_globals_buffer_size = kMaxGlobalsSize; |
| memset(globals_data_, 0, sizeof(globals_data_)); |
| |
| uint32_t maybe_import_index = 0; |
| if (maybe_import) { |
| // Manually add an imported function before any other functions. |
| // This must happen before the instance object is created, since the |
| // instance object allocates import entries. |
| maybe_import_index = AddFunction(maybe_import->sig, nullptr, kImport); |
| DCHECK_EQ(0, maybe_import_index); |
| } |
| |
| instance_object_ = InitInstanceObject(); |
| Handle<FixedArray> tables(isolate_->factory()->NewFixedArray(0)); |
| instance_object_->set_tables(*tables); |
| |
| if (maybe_import) { |
| // Manually compile an import wrapper and insert it into the instance. |
| CodeSpaceMemoryModificationScope modification_scope(isolate_->heap()); |
| auto resolved = compiler::ResolveWasmImportCall(maybe_import->js_function, |
| maybe_import->sig, false); |
| compiler::WasmImportCallKind kind = resolved.first; |
| Handle<JSReceiver> callable = resolved.second; |
| WasmImportWrapperCache::ModificationScope cache_scope( |
| native_module_->import_wrapper_cache()); |
| WasmImportWrapperCache::CacheKey key(kind, maybe_import->sig); |
| auto import_wrapper = cache_scope[key]; |
| if (import_wrapper == nullptr) { |
| import_wrapper = CompileImportWrapper( |
| isolate_->wasm_engine(), native_module_, isolate_->counters(), kind, |
| maybe_import->sig, &cache_scope); |
| } |
| |
| ImportedFunctionEntry(instance_object_, maybe_import_index) |
| .SetWasmToJs(isolate_, callable, import_wrapper); |
| } |
| |
| if (tier == ExecutionTier::kInterpreter) { |
| interpreter_ = WasmDebugInfo::SetupForTesting(instance_object_); |
| } |
| } |
| |
| byte* TestingModuleBuilder::AddMemory(uint32_t size, SharedFlag shared) { |
| CHECK(!test_module_->has_memory); |
| CHECK_NULL(mem_start_); |
| CHECK_EQ(0, mem_size_); |
| DCHECK(!instance_object_->has_memory_object()); |
| DCHECK_IMPLIES(test_module_->origin == kWasmOrigin, |
| size % kWasmPageSize == 0); |
| test_module_->has_memory = true; |
| uint32_t max_size = |
| (test_module_->maximum_pages != 0) ? test_module_->maximum_pages : size; |
| uint32_t alloc_size = RoundUp(size, kWasmPageSize); |
| Handle<JSArrayBuffer> new_buffer; |
| if (shared == SharedFlag::kShared) { |
| CHECK(NewSharedArrayBuffer(isolate_, alloc_size, max_size) |
| .ToHandle(&new_buffer)); |
| } else { |
| CHECK(NewArrayBuffer(isolate_, alloc_size).ToHandle(&new_buffer)); |
| } |
| CHECK(!new_buffer.is_null()); |
| mem_start_ = reinterpret_cast<byte*>(new_buffer->backing_store()); |
| mem_size_ = size; |
| CHECK(size == 0 || mem_start_); |
| memset(mem_start_, 0, size); |
| |
| // Create the WasmMemoryObject. |
| Handle<WasmMemoryObject> memory_object = |
| WasmMemoryObject::New(isolate_, new_buffer, max_size); |
| instance_object_->set_memory_object(*memory_object); |
| WasmMemoryObject::AddInstance(isolate_, memory_object, instance_object_); |
| // TODO(wasm): Delete the following two lines when test-run-wasm will use a |
| // multiple of kPageSize as memory size. At the moment, the effect of these |
| // two lines is used to shrink the memory for testing purposes. |
| instance_object_->SetRawMemory(mem_start_, mem_size_); |
| return mem_start_; |
| } |
| |
| uint32_t TestingModuleBuilder::AddFunction(FunctionSig* sig, const char* name, |
| FunctionType type) { |
| if (test_module_->functions.size() == 0) { |
| // TODO(titzer): Reserving space here to avoid the underlying WasmFunction |
| // structs from moving. |
| test_module_->functions.reserve(kMaxFunctions); |
| } |
| uint32_t index = static_cast<uint32_t>(test_module_->functions.size()); |
| test_module_->functions.push_back({sig, index, 0, {0, 0}, false, false}); |
| if (type == kImport) { |
| DCHECK_EQ(0, test_module_->num_declared_functions); |
| ++test_module_->num_imported_functions; |
| test_module_->functions.back().imported = true; |
| } else { |
| ++test_module_->num_declared_functions; |
| } |
| DCHECK_EQ(test_module_->functions.size(), |
| test_module_->num_imported_functions + |
| test_module_->num_declared_functions); |
| if (name) { |
| Vector<const byte> name_vec = Vector<const byte>::cast(CStrVector(name)); |
| test_module_->AddFunctionNameForTesting( |
| index, {AddBytes(name_vec), static_cast<uint32_t>(name_vec.length())}); |
| } |
| if (interpreter_) { |
| interpreter_->AddFunctionForTesting(&test_module_->functions.back()); |
| // Patch the jump table to call the interpreter for this function. |
| wasm::WasmCompilationResult result = compiler::CompileWasmInterpreterEntry( |
| isolate_->wasm_engine(), native_module_->enabled_features(), index, |
| sig); |
| std::unique_ptr<wasm::WasmCode> code = native_module_->AddCode( |
| index, result.code_desc, result.frame_slot_count, |
| result.tagged_parameter_slots, std::move(result.protected_instructions), |
| std::move(result.source_positions), wasm::WasmCode::kInterpreterEntry, |
| wasm::ExecutionTier::kInterpreter); |
| native_module_->PublishCode(std::move(code)); |
| } |
| DCHECK_LT(index, kMaxFunctions); // limited for testing. |
| return index; |
| } |
| |
| void TestingModuleBuilder::FreezeSignatureMapAndInitializeWrapperCache() { |
| if (test_module_->signature_map.is_frozen()) return; |
| test_module_->signature_map.Freeze(); |
| size_t max_num_sigs = MaxNumExportWrappers(test_module_.get()); |
| Handle<FixedArray> export_wrappers = |
| isolate_->factory()->NewFixedArray(static_cast<int>(max_num_sigs)); |
| instance_object_->module_object().set_export_wrappers(*export_wrappers); |
| } |
| |
| Handle<JSFunction> TestingModuleBuilder::WrapCode(uint32_t index) { |
| FreezeSignatureMapAndInitializeWrapperCache(); |
| SetExecutable(); |
| return WasmInstanceObject::GetOrCreateWasmExportedFunction( |
| isolate_, instance_object(), index); |
| } |
| |
| void TestingModuleBuilder::AddIndirectFunctionTable( |
| const uint16_t* function_indexes, uint32_t table_size) { |
| auto instance = instance_object(); |
| uint32_t table_index = static_cast<uint32_t>(test_module_->tables.size()); |
| test_module_->tables.emplace_back(); |
| WasmTable& table = test_module_->tables.back(); |
| table.initial_size = table_size; |
| table.maximum_size = table_size; |
| table.has_maximum_size = true; |
| table.type = kWasmFuncRef; |
| |
| { |
| // Allocate the indirect function table. |
| Handle<FixedArray> old_tables = |
| table_index == 0 |
| ? isolate_->factory()->empty_fixed_array() |
| : handle(instance_object_->indirect_function_tables(), isolate_); |
| Handle<FixedArray> new_tables = |
| isolate_->factory()->CopyFixedArrayAndGrow(old_tables, 1); |
| Handle<WasmIndirectFunctionTable> table_obj = |
| WasmIndirectFunctionTable::New(isolate_, table.initial_size); |
| new_tables->set(table_index, *table_obj); |
| instance_object_->set_indirect_function_tables(*new_tables); |
| } |
| |
| WasmInstanceObject::EnsureIndirectFunctionTableWithMinimumSize( |
| instance_object(), table_index, table_size); |
| Handle<WasmTableObject> table_obj = |
| WasmTableObject::New(isolate_, table.type, table.initial_size, |
| table.has_maximum_size, table.maximum_size, nullptr); |
| |
| WasmTableObject::AddDispatchTable(isolate_, table_obj, instance_object_, |
| table_index); |
| |
| if (function_indexes) { |
| for (uint32_t i = 0; i < table_size; ++i) { |
| WasmFunction& function = test_module_->functions[function_indexes[i]]; |
| int sig_id = test_module_->signature_map.Find(*function.sig); |
| IndirectFunctionTableEntry(instance, table_index, i) |
| .Set(sig_id, instance, function.func_index); |
| WasmTableObject::SetFunctionTablePlaceholder( |
| isolate_, table_obj, i, instance_object_, function_indexes[i]); |
| } |
| } |
| |
| Handle<FixedArray> old_tables(instance_object_->tables(), isolate_); |
| Handle<FixedArray> new_tables = |
| isolate_->factory()->CopyFixedArrayAndGrow(old_tables, 1); |
| new_tables->set(old_tables->length(), *table_obj); |
| instance_object_->set_tables(*new_tables); |
| } |
| |
| uint32_t TestingModuleBuilder::AddBytes(Vector<const byte> bytes) { |
| Vector<const uint8_t> old_bytes = native_module_->wire_bytes(); |
| uint32_t old_size = static_cast<uint32_t>(old_bytes.size()); |
| // Avoid placing strings at offset 0, this might be interpreted as "not |
| // set", e.g. for function names. |
| uint32_t bytes_offset = old_size ? old_size : 1; |
| size_t new_size = bytes_offset + bytes.size(); |
| OwnedVector<uint8_t> new_bytes = OwnedVector<uint8_t>::New(new_size); |
| if (old_size > 0) { |
| memcpy(new_bytes.start(), old_bytes.begin(), old_size); |
| } |
| memcpy(new_bytes.start() + bytes_offset, bytes.begin(), bytes.length()); |
| native_module_->SetWireBytes(std::move(new_bytes)); |
| return bytes_offset; |
| } |
| |
| uint32_t TestingModuleBuilder::AddException(FunctionSig* sig) { |
| DCHECK_EQ(0, sig->return_count()); |
| uint32_t index = static_cast<uint32_t>(test_module_->exceptions.size()); |
| test_module_->exceptions.push_back(WasmException{sig}); |
| Handle<WasmExceptionTag> tag = WasmExceptionTag::New(isolate_, index); |
| Handle<FixedArray> table(instance_object_->exceptions_table(), isolate_); |
| table = isolate_->factory()->CopyFixedArrayAndGrow(table, 1); |
| instance_object_->set_exceptions_table(*table); |
| table->set(index, *tag); |
| return index; |
| } |
| |
| uint32_t TestingModuleBuilder::AddPassiveDataSegment(Vector<const byte> bytes) { |
| uint32_t index = static_cast<uint32_t>(test_module_->data_segments.size()); |
| DCHECK_EQ(index, test_module_->data_segments.size()); |
| DCHECK_EQ(index, data_segment_starts_.size()); |
| DCHECK_EQ(index, data_segment_sizes_.size()); |
| DCHECK_EQ(index, dropped_data_segments_.size()); |
| |
| // Add a passive data segment. This isn't used by function compilation, but |
| // but it keeps the index in sync. The data segment's source will not be |
| // correct, since we don't store data in the module wire bytes. |
| test_module_->data_segments.emplace_back(); |
| |
| // The num_declared_data_segments (from the DataCount section) is used |
| // to validate the segment index, during function compilation. |
| test_module_->num_declared_data_segments = index + 1; |
| |
| Address old_data_address = |
| reinterpret_cast<Address>(data_segment_data_.data()); |
| size_t old_data_size = data_segment_data_.size(); |
| data_segment_data_.resize(old_data_size + bytes.length()); |
| Address new_data_address = |
| reinterpret_cast<Address>(data_segment_data_.data()); |
| |
| memcpy(data_segment_data_.data() + old_data_size, bytes.begin(), |
| bytes.length()); |
| |
| // The data_segment_data_ offset may have moved, so update all the starts. |
| for (Address& start : data_segment_starts_) { |
| start += new_data_address - old_data_address; |
| } |
| data_segment_starts_.push_back(new_data_address + old_data_size); |
| data_segment_sizes_.push_back(bytes.length()); |
| dropped_data_segments_.push_back(0); |
| |
| // The vector pointers may have moved, so update the instance object. |
| instance_object_->set_data_segment_starts(data_segment_starts_.data()); |
| instance_object_->set_data_segment_sizes(data_segment_sizes_.data()); |
| instance_object_->set_dropped_data_segments(dropped_data_segments_.data()); |
| return index; |
| } |
| |
| uint32_t TestingModuleBuilder::AddPassiveElementSegment( |
| const std::vector<uint32_t>& entries) { |
| uint32_t index = static_cast<uint32_t>(test_module_->elem_segments.size()); |
| DCHECK_EQ(index, dropped_elem_segments_.size()); |
| |
| test_module_->elem_segments.emplace_back(); |
| auto& elem_segment = test_module_->elem_segments.back(); |
| elem_segment.entries = entries; |
| |
| // The vector pointers may have moved, so update the instance object. |
| dropped_elem_segments_.push_back(0); |
| instance_object_->set_dropped_elem_segments(dropped_elem_segments_.data()); |
| return index; |
| } |
| |
| CompilationEnv TestingModuleBuilder::CreateCompilationEnv() { |
| // This is a hack so we don't need to call |
| // trap_handler::IsTrapHandlerEnabled(). |
| const bool is_trap_handler_enabled = |
| V8_TRAP_HANDLER_SUPPORTED && i::FLAG_wasm_trap_handler; |
| return {test_module_ptr_, |
| is_trap_handler_enabled ? kUseTrapHandler : kNoTrapHandler, |
| runtime_exception_support_, enabled_features_, lower_simd()}; |
| } |
| |
| const WasmGlobal* TestingModuleBuilder::AddGlobal(ValueType type) { |
| byte size = ValueTypes::MemSize(ValueTypes::MachineTypeFor(type)); |
| global_offset = (global_offset + size - 1) & ~(size - 1); // align |
| test_module_->globals.push_back( |
| {type, true, WasmInitExpr(), {global_offset}, false, false}); |
| global_offset += size; |
| // limit number of globals. |
| CHECK_LT(global_offset, kMaxGlobalsSize); |
| return &test_module_->globals.back(); |
| } |
| |
| Handle<WasmInstanceObject> TestingModuleBuilder::InitInstanceObject() { |
| Handle<Script> script = |
| isolate_->factory()->NewScript(isolate_->factory()->empty_string()); |
| script->set_type(Script::TYPE_WASM); |
| Handle<WasmModuleObject> module_object = |
| WasmModuleObject::New(isolate_, enabled_features_, test_module_, {}, |
| script, Handle<ByteArray>::null()); |
| // This method is called when we initialize TestEnvironment. We don't |
| // have a memory yet, so we won't create it here. We'll update the |
| // interpreter when we get a memory. We do have globals, though. |
| native_module_ = module_object->native_module(); |
| native_module_->ReserveCodeTableForTesting(kMaxFunctions); |
| |
| auto instance = WasmInstanceObject::New(isolate_, module_object); |
| instance->set_exceptions_table(*isolate_->factory()->empty_fixed_array()); |
| instance->set_globals_start(globals_data_); |
| return instance; |
| } |
| |
| void TestBuildingGraphWithBuilder(compiler::WasmGraphBuilder* builder, |
| Zone* zone, FunctionSig* sig, |
| const byte* start, const byte* end) { |
| WasmFeatures unused_detected_features; |
| FunctionBody body(sig, 0, start, end); |
| DecodeResult result = |
| BuildTFGraph(zone->allocator(), kAllWasmFeatures, nullptr, builder, |
| &unused_detected_features, body, nullptr); |
| if (result.failed()) { |
| #ifdef DEBUG |
| 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(), kAllWasmFeatures, nullptr, |
| builder, &unused_detected_features, body, nullptr); |
| } |
| #endif |
| |
| FATAL("Verification failed; pc = +%x, msg = %s", result.error().offset(), |
| result.error().message().c_str()); |
| } |
| builder->LowerInt64(); |
| if (!CpuFeatures::SupportsWasmSimd128()) { |
| builder->SimdScalarLoweringForTesting(); |
| } |
| } |
| |
| void TestBuildingGraph(Zone* zone, compiler::JSGraph* jsgraph, |
| CompilationEnv* module, FunctionSig* sig, |
| compiler::SourcePositionTable* source_position_table, |
| const byte* start, const byte* end) { |
| compiler::WasmGraphBuilder builder(module, zone, jsgraph, sig, |
| source_position_table); |
| TestBuildingGraphWithBuilder(&builder, zone, sig, start, end); |
| } |
| |
| WasmFunctionWrapper::WasmFunctionWrapper(Zone* zone, int num_params) |
| : GraphAndBuilders(zone), |
| inner_code_node_(nullptr), |
| context_address_(nullptr), |
| signature_(nullptr) { |
| // One additional parameter for the pointer to the return value memory. |
| Signature<MachineType>::Builder sig_builder(zone, 1, num_params + 1); |
| |
| sig_builder.AddReturn(MachineType::Int32()); |
| for (int i = 0; i < num_params + 1; i++) { |
| sig_builder.AddParam(MachineType::Pointer()); |
| } |
| signature_ = sig_builder.Build(); |
| } |
| |
| void WasmFunctionWrapper::Init(CallDescriptor* call_descriptor, |
| MachineType return_type, |
| Vector<MachineType> param_types) { |
| DCHECK_NOT_NULL(call_descriptor); |
| DCHECK_EQ(signature_->parameter_count(), param_types.length() + 1); |
| |
| // Create the TF graph for the wrapper. |
| |
| // Function, context_address, effect, and control. |
| Node** parameters = zone()->NewArray<Node*>(param_types.length() + 4); |
| int start_value_output_count = |
| static_cast<int>(signature_->parameter_count()) + 1; |
| graph()->SetStart( |
| graph()->NewNode(common()->Start(start_value_output_count))); |
| Node* effect = graph()->start(); |
| int parameter_count = 0; |
| |
| // Dummy node which gets replaced in SetInnerCode. |
| inner_code_node_ = graph()->NewNode(common()->Int32Constant(0)); |
| parameters[parameter_count++] = inner_code_node_; |
| |
| // Dummy node that gets replaced in SetContextAddress. |
| context_address_ = graph()->NewNode(IntPtrConstant(0)); |
| parameters[parameter_count++] = context_address_; |
| |
| int param_idx = 0; |
| for (MachineType t : param_types) { |
| DCHECK_NE(MachineType::None(), t); |
| parameters[parameter_count] = graph()->NewNode( |
| machine()->Load(t), |
| graph()->NewNode(common()->Parameter(param_idx++), graph()->start()), |
| graph()->NewNode(common()->Int32Constant(0)), effect, graph()->start()); |
| effect = parameters[parameter_count++]; |
| } |
| |
| parameters[parameter_count++] = effect; |
| parameters[parameter_count++] = graph()->start(); |
| Node* call = graph()->NewNode(common()->Call(call_descriptor), |
| parameter_count, parameters); |
| |
| if (!return_type.IsNone()) { |
| effect = graph()->NewNode( |
| machine()->Store(compiler::StoreRepresentation( |
| return_type.representation(), |
| compiler::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, |
| graph()->NewNode(common()->Int32Constant(WASM_WRAPPER_RETURN_VALUE)), |
| effect, graph()->start()); |
| graph()->SetEnd(graph()->NewNode(common()->End(1), r)); |
| } |
| |
| Handle<Code> WasmFunctionWrapper::GetWrapperCode() { |
| Handle<Code> code; |
| if (!code_.ToHandle(&code)) { |
| Isolate* isolate = CcTest::InitIsolateOnce(); |
| |
| auto call_descriptor = |
| compiler::Linkage::GetSimplifiedCDescriptor(zone(), signature_, true); |
| |
| if (kSystemPointerSize == 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); |
| |
| rep_builder.AddReturn(MachineRepresentation::kWord32); |
| for (size_t i = 0; i < num_params + 1; i++) { |
| rep_builder.AddParam(MachineRepresentation::kWord32); |
| } |
| compiler::Int64Lowering r(graph(), machine(), common(), zone(), |
| rep_builder.Build()); |
| r.LowerGraph(); |
| } |
| |
| OptimizedCompilationInfo info(ArrayVector("testing"), graph()->zone(), |
| Code::C_WASM_ENTRY); |
| code_ = compiler::Pipeline::GenerateCodeForTesting( |
| &info, isolate, call_descriptor, graph(), |
| AssemblerOptions::Default(isolate)); |
| code = code_.ToHandleChecked(); |
| #ifdef ENABLE_DISASSEMBLER |
| if (FLAG_print_opt_code) { |
| CodeTracer::Scope tracing_scope(isolate->GetCodeTracer()); |
| OFStream os(tracing_scope.file()); |
| |
| code->Disassemble("wasm wrapper", os); |
| } |
| #endif |
| } |
| |
| return code; |
| } |
| |
| void WasmFunctionCompiler::Build(const byte* start, const byte* 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 = {builder_->AddBytes(Vector<const byte>(start, len)), |
| static_cast<uint32_t>(len)}; |
| |
| if (interpreter_) { |
| // Add the code to the interpreter; do not generate compiled code. |
| interpreter_->SetFunctionCodeForTesting(function_, start, end); |
| return; |
| } |
| |
| Vector<const uint8_t> wire_bytes = builder_->instance_object() |
| ->module_object() |
| .native_module() |
| ->wire_bytes(); |
| |
| CompilationEnv env = builder_->CreateCompilationEnv(); |
| ScopedVector<uint8_t> func_wire_bytes(function_->code.length()); |
| memcpy(func_wire_bytes.begin(), wire_bytes.begin() + function_->code.offset(), |
| func_wire_bytes.length()); |
| |
| FunctionBody func_body{function_->sig, function_->code.offset(), |
| func_wire_bytes.begin(), func_wire_bytes.end()}; |
| NativeModule* native_module = |
| builder_->instance_object()->module_object().native_module(); |
| WasmCompilationUnit unit(function_->func_index, builder_->execution_tier()); |
| WasmFeatures unused_detected_features; |
| WasmCompilationResult result = unit.ExecuteCompilation( |
| isolate()->wasm_engine(), &env, |
| native_module->compilation_state()->GetWireBytesStorage(), |
| isolate()->counters(), &unused_detected_features); |
| WasmCode* code = native_module->AddCompiledCode(std::move(result)); |
| DCHECK_NOT_NULL(code); |
| if (WasmCode::ShouldBeLogged(isolate())) code->LogCode(isolate()); |
| } |
| |
| WasmFunctionCompiler::WasmFunctionCompiler(Zone* zone, FunctionSig* sig, |
| TestingModuleBuilder* builder, |
| const char* name) |
| : GraphAndBuilders(zone), |
| jsgraph(builder->isolate(), this->graph(), this->common(), nullptr, |
| nullptr, this->machine()), |
| sig(sig), |
| descriptor_(nullptr), |
| builder_(builder), |
| local_decls(zone, sig), |
| source_position_table_(this->graph()), |
| interpreter_(builder->interpreter()) { |
| // Get a new function from the testing module. |
| int index = builder->AddFunction(sig, name, TestingModuleBuilder::kWasm); |
| function_ = builder_->GetFunctionAt(index); |
| } |
| |
| WasmFunctionCompiler::~WasmFunctionCompiler() = default; |
| |
| FunctionSig* WasmRunnerBase::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++] = ValueTypes::ValueTypeFor(return_type); |
| for (MachineType param : param_types) { |
| CHECK_NE(MachineType::None(), param); |
| sig_types[idx++] = ValueTypes::ValueTypeFor(param); |
| } |
| return new (&zone_) FunctionSig(return_count, param_count, sig_types); |
| } |
| |
| // static |
| bool WasmRunnerBase::trap_happened; |
| |
| } // namespace wasm |
| } // namespace internal |
| } // namespace v8 |