Commit 916a5337 authored by clemensh's avatar clemensh Committed by Commit bot

[wasm] Move asm.js offset table to compiled module

Before, the encoded variant was stored in the compiled module, and the
decoded one in the debug info (per instance).
The decoded table was a FixedArray of ByteArrays.
Now, also the decoded table is a flat ByteArray, and it encodes whether
it is encoded or decoded. This saves memory and allows to store encoded
and decoded variant in the same field. The table is automatically
decoded on the first use.

This CL also removes some unused and unimplemented methods from
WasmDebugInfo (probably merge artifacts). That class is now pretty much
empty, but we might still need it for breakpoint support.

R=titzer@chromium.org, ahaas@chromium.org

Review-Url: https://codereview.chromium.org/2522953002
Cr-Commit-Position: refs/heads/master@{#41316}
parent c4914896
......@@ -1531,9 +1531,12 @@ Script* WasmFrame::script() const {
int WasmFrame::position() const {
int position = StandardFrame::position();
if (wasm::WasmIsAsmJs(wasm_instance(), isolate())) {
Handle<JSObject> instance(JSObject::cast(wasm_instance()), isolate());
position =
wasm::GetAsmWasmSourcePosition(instance, function_index(), position);
Handle<WasmCompiledModule> compiled_module(
WasmInstanceObject::cast(wasm_instance())->get_compiled_module(),
isolate());
DCHECK_LE(0, position);
position = WasmCompiledModule::GetAsmJsSourcePosition(
compiled_module, function_index(), static_cast<uint32_t>(position));
}
return position;
}
......
......@@ -706,8 +706,12 @@ Handle<Object> AsmJsWasmStackFrame::GetScriptNameOrSourceUrl() {
int AsmJsWasmStackFrame::GetPosition() const {
DCHECK_LE(0, offset_);
int byte_offset = code_->SourcePosition(offset_);
return wasm::GetAsmWasmSourcePosition(Handle<JSObject>::cast(wasm_instance_),
wasm_func_index_, byte_offset);
Handle<WasmCompiledModule> compiled_module(
WasmInstanceObject::cast(*wasm_instance_)->get_compiled_module(),
isolate_);
DCHECK_LE(0, byte_offset);
return WasmCompiledModule::GetAsmJsSourcePosition(
compiled_module, wasm_func_index_, static_cast<uint32_t>(byte_offset));
}
int AsmJsWasmStackFrame::GetLineNumber() {
......
......@@ -22,59 +22,6 @@ enum {
kWasmDebugInfoNumEntries
};
// TODO(clemensh): Move asm.js offset tables to the compiled module.
FixedArray *GetAsmJsOffsetTables(Handle<WasmDebugInfo> debug_info,
Isolate *isolate) {
Object *offset_tables = debug_info->get(kWasmDebugInfoAsmJsOffsets);
if (!offset_tables->IsUndefined(isolate)) {
return FixedArray::cast(offset_tables);
}
Handle<WasmInstanceObject> wasm_instance(debug_info->wasm_instance(),
isolate);
Handle<WasmCompiledModule> compiled_module(
wasm_instance->get_compiled_module(), isolate);
DCHECK(compiled_module->has_asm_js_offset_tables());
AsmJsOffsetsResult asm_offsets;
{
Handle<ByteArray> asm_offset_tables =
compiled_module->asm_js_offset_tables();
DisallowHeapAllocation no_gc;
const byte *bytes_start = asm_offset_tables->GetDataStartAddress();
const byte *bytes_end = bytes_start + asm_offset_tables->length();
asm_offsets = wasm::DecodeAsmJsOffsets(bytes_start, bytes_end);
}
// Wasm bytes must be valid and must contain asm.js offset table.
DCHECK(asm_offsets.ok());
DCHECK_GE(static_cast<size_t>(kMaxInt), asm_offsets.val.size());
int num_functions = static_cast<int>(asm_offsets.val.size());
DCHECK_EQ(
wasm::GetNumberOfFunctions(handle(debug_info->wasm_instance())),
static_cast<int>(num_functions +
compiled_module->module()->num_imported_functions));
Handle<FixedArray> all_tables =
isolate->factory()->NewFixedArray(num_functions);
debug_info->set(kWasmDebugInfoAsmJsOffsets, *all_tables);
for (int func = 0; func < num_functions; ++func) {
std::vector<std::pair<int, int>> &func_asm_offsets = asm_offsets.val[func];
if (func_asm_offsets.empty()) continue;
size_t array_size = 2 * kIntSize * func_asm_offsets.size();
CHECK_LE(array_size, static_cast<size_t>(kMaxInt));
ByteArray *arr =
*isolate->factory()->NewByteArray(static_cast<int>(array_size));
all_tables->set(func, arr);
int idx = 0;
for (std::pair<int, int> p : func_asm_offsets) {
// Byte offsets must be strictly monotonously increasing:
DCHECK(idx == 0 || p.first > arr->get_int(idx - 2));
arr->set_int(idx++, p.first);
arr->set_int(idx++, p.second);
}
DCHECK_EQ(arr->length(), idx * kIntSize);
}
return *all_tables;
}
} // namespace
Handle<WasmDebugInfo> WasmDebugInfo::New(Handle<WasmInstanceObject> instance) {
......@@ -113,35 +60,3 @@ WasmDebugInfo *WasmDebugInfo::cast(Object *object) {
WasmInstanceObject *WasmDebugInfo::wasm_instance() {
return WasmInstanceObject::cast(get(kWasmDebugInfoWasmObj));
}
int WasmDebugInfo::GetAsmJsSourcePosition(Handle<WasmDebugInfo> debug_info,
int func_index, int byte_offset) {
Isolate *isolate = debug_info->GetIsolate();
Handle<WasmInstanceObject> instance(debug_info->wasm_instance(), isolate);
FixedArray *offset_tables = GetAsmJsOffsetTables(debug_info, isolate);
WasmCompiledModule *compiled_module = instance->get_compiled_module();
int num_imported_functions =
compiled_module->module()->num_imported_functions;
DCHECK_LE(num_imported_functions, func_index);
func_index -= num_imported_functions;
DCHECK_LT(func_index, offset_tables->length());
ByteArray *offset_table = ByteArray::cast(offset_tables->get(func_index));
// Binary search for the current byte offset.
int left = 0; // inclusive
int right = offset_table->length() / kIntSize / 2; // exclusive
DCHECK_LT(left, right);
while (right - left > 1) {
int mid = left + (right - left) / 2;
if (offset_table->get_int(2 * mid) <= byte_offset) {
left = mid;
} else {
right = mid;
}
}
// There should be an entry for each position that could show up on the stack
// trace:
DCHECK_EQ(byte_offset, offset_table->get_int(2 * left));
return offset_table->get_int(2 * left + 1);
}
......@@ -534,6 +534,8 @@ void WasmModuleBuilder::WriteAsmJsOffsetTable(ZoneBuffer& buffer) const {
for (auto function : functions_) {
function->WriteAsmWasmOffsetTable(buffer);
}
// Append a 0 to indicate that this is an encoded table.
buffer.write_u8(0);
}
} // namespace wasm
} // namespace internal
......
......@@ -1974,9 +1974,9 @@ bool wasm::WasmIsAsmJs(Object* instance, Isolate* isolate) {
DCHECK(IsWasmInstance(instance));
WasmCompiledModule* compiled_module =
WasmInstanceObject::cast(instance)->get_compiled_module();
DCHECK_EQ(compiled_module->has_asm_js_offset_tables(),
DCHECK_EQ(compiled_module->has_asm_js_offset_table(),
compiled_module->script()->type() == Script::TYPE_NORMAL);
return compiled_module->has_asm_js_offset_tables();
return compiled_module->has_asm_js_offset_table();
}
Handle<Script> wasm::GetScript(Handle<JSObject> instance) {
......@@ -2003,12 +2003,6 @@ wasm::DisassembleFunction(Handle<WasmCompiledModule> compiled_module,
return {disassembly_os.str(), std::move(offset_table)};
}
int wasm::GetAsmWasmSourcePosition(Handle<JSObject> instance, int func_index,
int byte_offset) {
return WasmDebugInfo::GetAsmJsSourcePosition(GetDebugInfo(instance),
func_index, byte_offset);
}
Handle<WasmDebugInfo> wasm::GetDebugInfo(Handle<JSObject> object) {
auto instance = Handle<WasmInstanceObject>::cast(object);
if (instance->has_debug_info()) {
......@@ -2021,11 +2015,6 @@ Handle<WasmDebugInfo> wasm::GetDebugInfo(Handle<JSObject> object) {
return new_info;
}
int wasm::GetNumberOfFunctions(Handle<JSObject> object) {
return static_cast<int>(
Handle<WasmInstanceObject>::cast(object)->module()->functions.size());
}
// TODO(clemensh): origin can be inferred from asm_js_script; remove it.
MaybeHandle<WasmModuleObject> wasm::CreateModuleObjectFromBytes(
Isolate* isolate, const byte* start, const byte* end, ErrorThrower* thrower,
......@@ -2056,19 +2045,19 @@ MaybeHandle<WasmModuleObject> wasm::CreateModuleObjectFromBytes(
DCHECK_EQ(origin == kAsmJsOrigin, !asm_js_script.is_null());
DCHECK(!compiled_module->has_script());
DCHECK(!compiled_module->has_asm_js_offset_tables());
DCHECK(!compiled_module->has_asm_js_offset_table());
if (origin == kAsmJsOrigin) {
// Set script for the asm.js source, and the offset table mapping wasm byte
// offsets to source positions.
compiled_module->set_script(asm_js_script);
size_t offset_tables_len =
size_t offset_table_len =
asm_js_offset_tables_end - asm_js_offset_tables_start;
DCHECK_GE(static_cast<size_t>(kMaxInt), offset_tables_len);
Handle<ByteArray> offset_tables =
isolate->factory()->NewByteArray(static_cast<int>(offset_tables_len));
memcpy(offset_tables->GetDataStartAddress(), asm_js_offset_tables_start,
offset_tables_len);
compiled_module->set_asm_js_offset_tables(offset_tables);
DCHECK_GE(static_cast<size_t>(kMaxInt), offset_table_len);
Handle<ByteArray> offset_table =
isolate->factory()->NewByteArray(static_cast<int>(offset_table_len));
memcpy(offset_table->GetDataStartAddress(), asm_js_offset_tables_start,
offset_table_len);
compiled_module->set_asm_js_offset_table(offset_table);
} else {
// Create a new Script object representing this wasm module, store it in the
// compiled wasm module, and register it at the debugger.
......
......@@ -368,9 +368,6 @@ Handle<String> GetWasmFunctionName(Isolate* isolate, Handle<Object> instance,
// If no debug info exists yet, it is created automatically.
Handle<WasmDebugInfo> GetDebugInfo(Handle<JSObject> wasm);
// Return the number of functions in the given wasm object.
int GetNumberOfFunctions(Handle<JSObject> wasm);
// Check whether the given object represents a WebAssembly.Instance instance.
// This checks the number and type of internal fields, so it's not 100 percent
// secure. If it turns out that we need more complete checks, we could add a
......@@ -394,11 +391,6 @@ Handle<Script> GetScript(Handle<JSObject> instance);
std::pair<std::string, std::vector<std::tuple<uint32_t, int, int>>>
DisassembleFunction(Handle<WasmCompiledModule> compiled_module, int func_index);
// Get the asm.js source position for the given byte offset in the given
// function.
int GetAsmWasmSourcePosition(Handle<JSObject> instance, int func_index,
int byte_offset);
V8_EXPORT_PRIVATE MaybeHandle<WasmModuleObject> CreateModuleObjectFromBytes(
Isolate* isolate, const byte* start, const byte* end, ErrorThrower* thrower,
ModuleOrigin origin, Handle<Script> asm_js_script,
......
......@@ -3,6 +3,8 @@
// found in the LICENSE file.
#include "src/wasm/wasm-objects.h"
#include "src/wasm/module-decoder.h"
#include "src/wasm/wasm-module.h"
#define TRACE(...) \
......@@ -446,6 +448,99 @@ bool WasmCompiledModule::GetPositionInfo(uint32_t position,
return true;
}
namespace {
Handle<ByteArray> GetDecodedAsmJsOffsetTable(
Handle<WasmCompiledModule> compiled_module, Isolate* isolate) {
DCHECK(compiled_module->has_asm_js_offset_table());
Handle<ByteArray> offset_table = compiled_module->asm_js_offset_table();
// The last byte in the asm_js_offset_tables ByteArray tells whether it is
// still encoded (0) or decoded (1).
enum AsmJsTableType : int { Encoded = 0, Decoded = 1 };
int table_type = offset_table->get(offset_table->length() - 1);
DCHECK(table_type == Encoded || table_type == Decoded);
if (table_type == Decoded) return offset_table;
AsmJsOffsetsResult asm_offsets;
{
DisallowHeapAllocation no_gc;
const byte* bytes_start = offset_table->GetDataStartAddress();
const byte* bytes_end = bytes_start + offset_table->length() - 1;
asm_offsets = wasm::DecodeAsmJsOffsets(bytes_start, bytes_end);
}
// Wasm bytes must be valid and must contain asm.js offset table.
DCHECK(asm_offsets.ok());
DCHECK_GE(static_cast<size_t>(kMaxInt), asm_offsets.val.size());
int num_functions = static_cast<int>(asm_offsets.val.size());
int num_imported_functions =
static_cast<int>(compiled_module->module()->num_imported_functions);
DCHECK_EQ(compiled_module->module()->functions.size(),
static_cast<size_t>(num_functions) + num_imported_functions);
// One byte to encode that this is a decoded table.
int total_size = 1;
for (int func = 0; func < num_functions; ++func) {
size_t new_size = asm_offsets.val[func].size() * 2 * kIntSize;
DCHECK_LE(new_size, static_cast<size_t>(kMaxInt) - total_size);
total_size += static_cast<int>(new_size);
}
Handle<ByteArray> decoded_table =
isolate->factory()->NewByteArray(total_size, TENURED);
decoded_table->set(total_size - 1, AsmJsTableType::Decoded);
compiled_module->set_asm_js_offset_table(decoded_table);
int idx = 0;
std::vector<WasmFunction>& wasm_funs = compiled_module->module()->functions;
for (int func = 0; func < num_functions; ++func) {
std::vector<std::pair<int, int>>& func_asm_offsets = asm_offsets.val[func];
if (func_asm_offsets.empty()) continue;
int func_offset =
wasm_funs[num_imported_functions + func].code_start_offset;
for (std::pair<int, int> p : func_asm_offsets) {
// Byte offsets must be strictly monotonously increasing:
DCHECK(idx == 0 ||
func_offset + p.first > decoded_table->get_int(idx - 2));
decoded_table->set_int(idx++, func_offset + p.first);
decoded_table->set_int(idx++, p.second);
}
}
DCHECK_EQ(total_size, idx * kIntSize + 1);
return decoded_table;
}
} // namespace
int WasmCompiledModule::GetAsmJsSourcePosition(
Handle<WasmCompiledModule> compiled_module, uint32_t func_index,
uint32_t byte_offset) {
Isolate* isolate = compiled_module->GetIsolate();
Handle<ByteArray> offset_table =
GetDecodedAsmJsOffsetTable(compiled_module, isolate);
DCHECK_LT(func_index, compiled_module->module()->functions.size());
uint32_t func_code_offset =
compiled_module->module()->functions[func_index].code_start_offset;
uint32_t total_offset = func_code_offset + byte_offset;
// Binary search for the total byte offset.
int left = 0; // inclusive
int right = offset_table->length() / kIntSize / 2; // exclusive
DCHECK_LT(left, right);
while (right - left > 1) {
int mid = left + (right - left) / 2;
int mid_entry = offset_table->get_int(2 * mid);
DCHECK_GE(kMaxInt, mid_entry);
if (static_cast<uint32_t>(mid_entry) <= total_offset) {
left = mid;
} else {
right = mid;
}
}
// There should be an entry for each position that could show up on the stack
// trace:
DCHECK_EQ(total_offset,
static_cast<uint32_t>(offset_table->get_int(2 * left)));
return offset_table->get_int(2 * left + 1);
}
Handle<WasmInstanceWrapper> WasmInstanceWrapper::New(
Isolate* isolate, Handle<WasmInstanceObject> instance) {
Handle<FixedArray> array =
......
......@@ -203,7 +203,7 @@ class WasmCompiledModule : public FixedArray {
/* For debugging: */ \
MACRO(OBJECT, SeqOneByteString, module_bytes) \
MACRO(OBJECT, Script, script) \
MACRO(OBJECT, ByteArray, asm_js_offset_tables) \
MACRO(OBJECT, ByteArray, asm_js_offset_table) \
/* End of debugging stuff */ \
MACRO(OBJECT, FixedArray, function_tables) \
MACRO(OBJECT, FixedArray, empty_function_tables) \
......@@ -291,12 +291,18 @@ class WasmCompiledModule : public FixedArray {
// Returns true if the position is valid inside this module, false otherwise.
bool GetPositionInfo(uint32_t position, Script::PositionInfo* info);
// Get the asm.js source position from a byte offset.
// Must only be called if the associated wasm object was created from asm.js.
static int GetAsmJsSourcePosition(Handle<WasmCompiledModule> debug_info,
uint32_t func_index, uint32_t byte_offset);
private:
void InitId();
DISALLOW_IMPLICIT_CONSTRUCTORS(WasmCompiledModule);
};
// TODO(clemensh): Extend this object for breakpoint support, or remove it.
class WasmDebugInfo : public FixedArray {
public:
enum class Fields { kFieldCount };
......@@ -307,28 +313,6 @@ class WasmDebugInfo : public FixedArray {
static WasmDebugInfo* cast(Object* object);
WasmInstanceObject* wasm_instance();
bool SetBreakPoint(int byte_offset);
// Get the Script for the specified function.
static Script* GetFunctionScript(Handle<WasmDebugInfo> debug_info,
int func_index);
// Disassemble the specified function from this module.
static Handle<String> DisassembleFunction(Handle<WasmDebugInfo> debug_info,
int func_index);
// Get the offset table for the specified function, mapping from byte offsets
// to position in the disassembly.
// Returns an array with three entries per instruction: byte offset, line and
// column.
static Handle<FixedArray> GetFunctionOffsetTable(
Handle<WasmDebugInfo> debug_info, int func_index);
// Get the asm.js source position from a byte offset.
// Must only be called if the associated wasm object was created from asm.js.
static int GetAsmJsSourcePosition(Handle<WasmDebugInfo> debug_info,
int func_index, int byte_offset);
};
class WasmInstanceWrapper : public FixedArray {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment