diff --git a/BUILD.gn b/BUILD.gn index fc96fb6048..739d39d328 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -213,7 +213,7 @@ config("toolchain") { } if (v8_target_arch == "s390") { defines += [ "V8_TARGET_ARCH_S390" ] - } + } if (v8_target_arch == "s390x") { defines += [ "V8_TARGET_ARCH_S390", @@ -226,7 +226,7 @@ config("toolchain") { if (v8_target_arch == "x64") { defines += [ "V8_TARGET_ARCH_X64" ] } - + if (is_win) { defines += [ "WIN32" ] # TODO(jochen): Support v8_enable_prof. @@ -1425,6 +1425,8 @@ source_set("v8_base") { "src/wasm/switch-logic.h", "src/wasm/wasm-external-refs.cc", "src/wasm/wasm-external-refs.h", + "src/wasm/wasm-function-name-table.cc", + "src/wasm/wasm-function-name-table.h", "src/wasm/wasm-js.cc", "src/wasm/wasm-js.h", "src/wasm/wasm-macro-gen.h", diff --git a/src/compiler/wasm-compiler.cc b/src/compiler/wasm-compiler.cc index 8c11d9628b..4fa1cdd223 100644 --- a/src/compiler/wasm-compiler.cc +++ b/src/compiler/wasm-compiler.cc @@ -2977,7 +2977,24 @@ Handle CompileWasmFunction(wasm::ErrorThrower* thrower, Isolate* isolate, } buffer.Dispose(); + if (!code.is_null()) { + DCHECK(code->deoptimization_data() == nullptr || + code->deoptimization_data()->length() == 0); + Handle deopt_data = + isolate->factory()->NewFixedArray(2, TENURED); + if (!module_env->instance->js_object.is_null()) { + deopt_data->set(0, *module_env->instance->js_object); + deopt_data->set(1, Smi::FromInt(function->func_index)); + } else if (func_name.start() != nullptr) { + MaybeHandle maybe_name = + isolate->factory()->NewStringFromUtf8(func_name); + if (!maybe_name.is_null()) + deopt_data->set(0, *maybe_name.ToHandleChecked()); + } + deopt_data->set_length(2); + code->set_deoptimization_data(*deopt_data); + RecordFunctionCompilation( Logger::FUNCTION_TAG, &info, "WASM_function", function->func_index, module_env->module->GetName(function->name_offset, diff --git a/src/objects-inl.h b/src/objects-inl.h index a629326262..60a140b79c 100644 --- a/src/objects-inl.h +++ b/src/objects-inl.h @@ -3906,7 +3906,6 @@ void StringCharacterStream::VisitTwoByteString( int ByteArray::Size() { return RoundUp(length() + kHeaderSize, kPointerSize); } - byte ByteArray::get(int index) { DCHECK(index >= 0 && index < this->length()); return READ_BYTE_FIELD(this, kHeaderSize + index * kCharSize); @@ -3918,12 +3917,29 @@ void ByteArray::set(int index, byte value) { WRITE_BYTE_FIELD(this, kHeaderSize + index * kCharSize, value); } +void ByteArray::copy_in(int index, const byte* buffer, int length) { + DCHECK(index >= 0 && length >= 0 && index + length >= index && + index + length <= this->length()); + byte* dst_addr = FIELD_ADDR(this, kHeaderSize + index * kCharSize); + memcpy(dst_addr, buffer, length); +} + +void ByteArray::copy_out(int index, byte* buffer, int length) { + DCHECK(index >= 0 && length >= 0 && index + length >= index && + index + length <= this->length()); + const byte* src_addr = FIELD_ADDR(this, kHeaderSize + index * kCharSize); + memcpy(buffer, src_addr, length); +} int ByteArray::get_int(int index) { - DCHECK(index >= 0 && (index * kIntSize) < this->length()); + DCHECK(index >= 0 && index < this->length() / kIntSize); return READ_INT_FIELD(this, kHeaderSize + index * kIntSize); } +void ByteArray::set_int(int index, int value) { + DCHECK(index >= 0 && index < this->length() / kIntSize); + WRITE_INT_FIELD(this, kHeaderSize + index * kIntSize, value); +} ByteArray* ByteArray::FromDataStartAddress(Address address) { DCHECK_TAG_ALIGNED(address); diff --git a/src/objects.h b/src/objects.h index f2b5139f9b..4574850093 100644 --- a/src/objects.h +++ b/src/objects.h @@ -4394,8 +4394,13 @@ class ByteArray: public FixedArrayBase { inline byte get(int index); inline void set(int index, byte value); + // Copy in / copy out whole byte slices. + inline void copy_out(int index, byte* buffer, int length); + inline void copy_in(int index, const byte* buffer, int length); + // Treat contents as an int array. inline int get_int(int index); + inline void set_int(int index, int value); static int SizeFor(int length) { return OBJECT_POINTER_ALIGN(kHeaderSize + length); diff --git a/src/v8.gyp b/src/v8.gyp index 68ae83bcbe..c5b261f2cd 100644 --- a/src/v8.gyp +++ b/src/v8.gyp @@ -1137,8 +1137,6 @@ 'version.h', 'vm-state-inl.h', 'vm-state.h', - 'wasm/switch-logic.h', - 'wasm/switch-logic.cc', 'wasm/asm-wasm-builder.cc', 'wasm/asm-wasm-builder.h', 'wasm/ast-decoder.cc', @@ -1146,11 +1144,15 @@ 'wasm/decoder.h', 'wasm/encoder.cc', 'wasm/encoder.h', - 'wasm/wasm-external-refs.cc', - 'wasm/wasm-external-refs.h', 'wasm/leb-helper.h', 'wasm/module-decoder.cc', 'wasm/module-decoder.h', + 'wasm/switch-logic.h', + 'wasm/switch-logic.cc', + 'wasm/wasm-external-refs.cc', + 'wasm/wasm-external-refs.h', + 'wasm/wasm-function-name-table.cc', + 'wasm/wasm-function-name-table.h', 'wasm/wasm-js.cc', 'wasm/wasm-js.h', 'wasm/wasm-macro-gen.h', diff --git a/src/wasm/wasm-function-name-table.cc b/src/wasm/wasm-function-name-table.cc new file mode 100644 index 0000000000..9abcd9b091 --- /dev/null +++ b/src/wasm/wasm-function-name-table.cc @@ -0,0 +1,71 @@ +// Copyright 2016 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 "src/wasm/wasm-function-name-table.h" + +#include "src/wasm/wasm-module.h" + +namespace v8 { +namespace internal { +namespace wasm { + +// Build an array with all function names. If there are N functions in the +// module, then the first (kIntSize * (N+1)) bytes are integer entries. +// The first integer entry encodes the number of functions in the module. +// The entries 1 to N contain offsets into the second part of this array. +// After these N+1 integer entries, the second part begins, which holds a +// concatenation of all function names. +// +// Returns undefined if the array length would not fit in an integer value +Handle BuildFunctionNamesTable(Isolate* isolate, WasmModule* module) { + uint64_t func_names_length = 0; + for (auto& func : module->functions) func_names_length += func.name_length; + int num_funcs_int = static_cast(module->functions.size()); + int current_offset = (num_funcs_int + 1) * kIntSize; + uint64_t total_array_length = current_offset + func_names_length; + int total_array_length_int = static_cast(total_array_length); + // Check for overflow. Just skip function names if it happens. + if (total_array_length_int != total_array_length || num_funcs_int < 0 || + num_funcs_int != module->functions.size()) + return isolate->factory()->undefined_value(); + Handle func_names_array = + isolate->factory()->NewByteArray(total_array_length_int, TENURED); + if (func_names_array.is_null()) return isolate->factory()->undefined_value(); + func_names_array->set_int(0, num_funcs_int); + int func_index = 0; + for (WasmFunction& fun : module->functions) { + WasmName name = module->GetNameOrNull(&fun); + func_names_array->copy_in(current_offset, + reinterpret_cast(name.start()), + name.length()); + func_names_array->set_int(func_index + 1, current_offset); + current_offset += name.length(); + ++func_index; + } + return func_names_array; +} + +Handle GetWasmFunctionNameFromTable(Handle func_names_array, + uint32_t func_index) { + uint32_t num_funcs = static_cast(func_names_array->get_int(0)); + DCHECK(static_cast(num_funcs) >= 0); + auto undefined = [&func_names_array]() -> Handle { + return func_names_array->GetIsolate()->factory()->undefined_value(); + }; + if (func_index >= num_funcs) return undefined(); + int offset = func_names_array->get_int(func_index + 1); + int next_offset = func_index == num_funcs - 1 + ? func_names_array->length() + : func_names_array->get_int(func_index + 2); + ScopedVector buffer(next_offset - offset); + func_names_array->copy_out(offset, buffer.start(), next_offset - offset); + MaybeHandle maybe_name = + func_names_array->GetIsolate()->factory()->NewStringFromUtf8( + Vector::cast(buffer)); + return maybe_name.is_null() ? undefined() : maybe_name.ToHandleChecked(); +} + +} // namespace wasm +} // namespace internal +} // namespace v8 diff --git a/src/wasm/wasm-function-name-table.h b/src/wasm/wasm-function-name-table.h new file mode 100644 index 0000000000..1a713723f8 --- /dev/null +++ b/src/wasm/wasm-function-name-table.h @@ -0,0 +1,30 @@ +// Copyright 2016 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. + +#ifndef V8_WASM_FUNCTION_NAME_TABLE_H_ +#define V8_WASM_FUNCTION_NAME_TABLE_H_ + +#include "src/handles.h" +#include "src/objects.h" + +namespace v8 { +namespace internal { +namespace wasm { + +// Forward declarations for some WASM data structures. +struct WasmModule; + +// Encode all function names of the WasmModule into one ByteArray. +Handle BuildFunctionNamesTable(Isolate* isolate, WasmModule* module); + +// Extract the function name for the given func_index from the wasm module. +// Returns undefined if the function index is invalid. +Handle GetWasmFunctionNameFromTable(Handle wasm_names_table, + uint32_t func_index); + +} // namespace wasm +} // namespace internal +} // namespace v8 + +#endif // V8_WASM_FUNCTION_NAME_TABLE_H_ diff --git a/src/wasm/wasm-module.cc b/src/wasm/wasm-module.cc index a02bb2a3c3..0e15401623 100644 --- a/src/wasm/wasm-module.cc +++ b/src/wasm/wasm-module.cc @@ -11,6 +11,7 @@ #include "src/wasm/ast-decoder.h" #include "src/wasm/module-decoder.h" +#include "src/wasm/wasm-function-name-table.h" #include "src/wasm/wasm-module.h" #include "src/wasm/wasm-result.h" @@ -201,11 +202,12 @@ class WasmLinker { namespace { // Internal constants for the layout of the module object. -const int kWasmModuleInternalFieldCount = 4; +const int kWasmModuleInternalFieldCount = 5; const int kWasmModuleFunctionTable = 0; const int kWasmModuleCodeTable = 1; const int kWasmMemArrayBuffer = 2; const int kWasmGlobalsArrayBuffer = 3; +const int kWasmFunctionNamesArray = 4; size_t AllocateGlobalsOffsets(std::vector& globals) { uint32_t offset = 0; @@ -630,6 +632,15 @@ MaybeHandle WasmModule::Instantiate(Isolate* isolate, } } + //------------------------------------------------------------------------- + // Attach an array with function names and an array with offsets into that + // first array. + //------------------------------------------------------------------------- + { + Handle arr = BuildFunctionNamesTable(isolate, module_env.module); + instance.js_object->SetInternalField(kWasmFunctionNamesArray, *arr); + } + if (FLAG_print_wasm_code_size) printf("Total generated wasm code: %u bytes\n", total_code_size); @@ -783,6 +794,16 @@ int32_t CompileAndRunWasmModule(Isolate* isolate, WasmModule* module) { thrower.Error("WASM.compileRun() failed: Return value should be number"); return -1; } + +Handle GetWasmFunctionName(Handle wasm, uint32_t func_index) { + Handle func_names_arr_obj = handle( + wasm->GetInternalField(kWasmFunctionNamesArray), wasm->GetIsolate()); + if (func_names_arr_obj->IsUndefined()) + return func_names_arr_obj; // Return undefined. + return GetWasmFunctionNameFromTable( + Handle::cast(func_names_arr_obj), func_index); +} + } // namespace wasm } // namespace internal } // namespace v8 diff --git a/src/wasm/wasm-module.h b/src/wasm/wasm-module.h index 635c76322f..a53f86dc99 100644 --- a/src/wasm/wasm-module.h +++ b/src/wasm/wasm-module.h @@ -195,6 +195,11 @@ struct WasmModule { static_cast(length)}; } + // Get a string stored in the module bytes representing a function name. + WasmName GetName(WasmFunction* function) const { + return GetName(function->name_offset, function->name_length); + } + // Get a string stored in the module bytes representing a name. WasmName GetNameOrNull(uint32_t offset, uint32_t length) const { if (length == 0) return {NULL, 0}; // no name. @@ -204,6 +209,11 @@ struct WasmModule { static_cast(length)}; } + // Get a string stored in the module bytes representing a function name. + WasmName GetNameOrNull(WasmFunction* function) const { + return GetNameOrNull(function->name_offset, function->name_length); + } + // Checks the given offset range is contained within the module bytes. bool BoundsCheck(uint32_t start, uint32_t end) const { size_t size = module_end - module_start; @@ -321,6 +331,11 @@ int32_t CompileAndRunWasmModule(Isolate* isolate, const byte* module_start, // given decoded module. int32_t CompileAndRunWasmModule(Isolate* isolate, WasmModule* module); +// Extract a function name from the given wasm object. +// Returns undefined if the function is unnamed or the function index is +// invalid. +Handle GetWasmFunctionName(Handle wasm, uint32_t func_index); + } // namespace wasm } // namespace internal } // namespace v8 diff --git a/test/cctest/wasm/wasm-run-utils.h b/test/cctest/wasm/wasm-run-utils.h index ce7ccb7dc0..1d0fd7110d 100644 --- a/test/cctest/wasm/wasm-run-utils.h +++ b/test/cctest/wasm/wasm-run-utils.h @@ -487,13 +487,29 @@ class WasmFunctionCompiler : public HandleAndZoneScope, Code::ComputeFlags(Code::WASM_FUNCTION)); v8::base::SmartPointer job(Pipeline::NewWasmCompilationJob( &info, graph(), desc, &source_position_table_)); - Handle code = Handle::null(); - if (job->OptimizeGraph() == CompilationJob::SUCCEEDED && - job->GenerateCode() == CompilationJob::SUCCEEDED) { - code = info.code(); + if (job->OptimizeGraph() != CompilationJob::SUCCEEDED || + job->GenerateCode() != CompilationJob::SUCCEEDED) + return Handle::null(); + + Handle code = info.code(); + + // Length is always 2, since usually is stored in the + // deopt data. Here, we store instead. + DCHECK(code->deoptimization_data() == nullptr || + code->deoptimization_data()->length() == 0); + Handle deopt_data = + isolate()->factory()->NewFixedArray(2, TENURED); + if (debug_name_.start() != nullptr) { + MaybeHandle maybe_name = + isolate()->factory()->NewStringFromUtf8(debug_name_, TENURED); + if (!maybe_name.is_null()) + deopt_data->set(0, *maybe_name.ToHandleChecked()); } + deopt_data->set_length(2); + code->set_deoptimization_data(*deopt_data); + #ifdef ENABLE_DISASSEMBLER - if (!code.is_null() && FLAG_print_opt_code) { + if (FLAG_print_opt_code) { OFStream os(stdout); code->Disassemble("wasm code", os); }