Commit 94cd46b5 authored by clemensh's avatar clemensh Committed by Commit bot

[wasm] Fix location for error in asm.js ToNumber conversion

In the asm.js code translated to wasm, we call imported functions via a
WASM_TO_JS stub, which first calls the function and then calls ToNumber
on the return value. Exceptions can happen in both calls.
We were only ever reporting the location of the function call, whereas
asm.js code executed via turbofan reported the location of the type
coercion operator ("+" on "+foo()" or "|" on "foo()|0").

This CL implements the same behaviour for asm.js code translated to
wasm. The following is changed:
- the AsmWasmBuilder records the parent node when descending on a binary
  operator (also "+foo()" is represented by a binary operation).
- it stores not one location per call in the source position side
  table, but two (one for the call, one for the parent which does the
  type coercion).
- the wasm compiler annotates the source positions "0" and "1" to the
  two calls in the WASM_TO_JS wrapper (only if the module origin is
  asm.js).
- during stack trace generation (in the StackTraceIterator), when we
  move from the WASM_TO_JS frame to the WASM frame, we remember at which
  call inside the WASM_TO_JS wrapper we are, and encode this information
  in the generated caller state, used for the WASM frame.
- the same information is also stored in the FrameArray which is used
  to reconstruct the stack trace later.

R=titzer@chromium.org, bradnelson@chromium.org
CC=jgruber@chromium.org
BUG=v8:4203,v8:5724

Review-Url: https://codereview.chromium.org/2555243002
Cr-Commit-Position: refs/heads/master@{#41599}
parent 987f6386
......@@ -71,7 +71,8 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
foreign_init_function_(nullptr),
function_tables_(ZoneHashMap::kDefaultHashMapCapacity,
ZoneAllocationPolicy(zone)),
imported_function_table_(this) {
imported_function_table_(this),
parent_binop_(nullptr) {
InitializeAstVisitor(isolate);
}
......@@ -1398,6 +1399,10 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
bool VisitCallExpression(Call* expr) {
Call::CallType call_type = expr->GetCallType();
bool returns_value = true;
// Save the parent now, it might be overwritten in VisitCallArgs.
BinaryOperation* parent_binop = parent_binop_;
switch (call_type) {
case Call::OTHER_CALL: {
VariableProxy* proxy = expr->expression()->AsVariableProxy();
......@@ -1428,13 +1433,20 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
uint32_t index = imported_function_table_.LookupOrInsertImportUse(
vp->var(), sig.Build());
VisitCallArgs(expr);
current_function_builder_->AddAsmWasmOffset(expr->position());
// For non-void functions, we must know the parent node.
DCHECK_IMPLIES(returns_value, parent_binop != nullptr);
DCHECK_IMPLIES(returns_value, parent_binop->left() == expr ||
parent_binop->right() == expr);
int pos = expr->position();
int parent_pos = returns_value ? parent_binop->position() : pos;
current_function_builder_->AddAsmWasmOffset(pos, parent_pos);
current_function_builder_->Emit(kExprCallFunction);
current_function_builder_->EmitVarInt(index);
} else {
WasmFunctionBuilder* function = LookupOrInsertFunction(vp->var());
VisitCallArgs(expr);
current_function_builder_->AddAsmWasmOffset(expr->position());
current_function_builder_->AddAsmWasmOffset(expr->position(),
expr->position());
current_function_builder_->Emit(kExprCallFunction);
current_function_builder_->EmitDirectCallIndex(
function->func_index());
......@@ -1460,7 +1472,8 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
VisitCallArgs(expr);
current_function_builder_->EmitGetLocal(tmp.index());
current_function_builder_->AddAsmWasmOffset(expr->position());
current_function_builder_->AddAsmWasmOffset(expr->position(),
expr->position());
current_function_builder_->Emit(kExprCallIndirect);
current_function_builder_->EmitVarInt(indices->signature_index);
current_function_builder_->EmitVarInt(0); // table index
......@@ -1632,6 +1645,7 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
void VisitBinaryOperation(BinaryOperation* expr) {
ConvertOperation convertOperation = MatchBinaryOperation(expr);
static const bool kDontIgnoreSign = false;
parent_binop_ = expr;
if (convertOperation == kToDouble) {
RECURSE(Visit(expr->left()));
TypeIndex type = TypeIndexOf(expr->left(), kDontIgnoreSign);
......@@ -1935,6 +1949,9 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
uint32_t next_table_index_;
ZoneHashMap function_tables_;
ImportedFunctionTable imported_function_table_;
// Remember the parent node for reporting the correct location for ToNumber
// conversions after calls.
BinaryOperation* parent_binop_;
DEFINE_AST_VISITOR_SUBCLASS_MEMBERS();
......
......@@ -137,14 +137,14 @@ class PipelineData {
// For machine graph testing entry point.
PipelineData(ZoneStats* zone_stats, CompilationInfo* info, Graph* graph,
Schedule* schedule)
Schedule* schedule, SourcePositionTable* source_positions)
: isolate_(info->isolate()),
info_(info),
debug_name_(info_->GetDebugName()),
zone_stats_(zone_stats),
graph_zone_scope_(zone_stats_, ZONE_NAME),
graph_(graph),
source_positions_(new (info->zone()) SourcePositionTable(graph_)),
source_positions_(source_positions),
schedule_(schedule),
instruction_zone_scope_(zone_stats_, ZONE_NAME),
instruction_zone_(instruction_zone_scope_.zone()),
......@@ -1641,7 +1641,8 @@ Handle<Code> Pipeline::GenerateCodeForCodeStub(Isolate* isolate,
// Construct a pipeline for scheduling and code generation.
ZoneStats zone_stats(isolate->allocator());
PipelineData data(&zone_stats, &info, graph, schedule);
SourcePositionTable source_positions(graph);
PipelineData data(&zone_stats, &info, graph, schedule, &source_positions);
std::unique_ptr<PipelineStatistics> pipeline_statistics;
if (FLAG_turbo_stats || FLAG_turbo_stats_nvp) {
pipeline_statistics.reset(new PipelineStatistics(&info, &zone_stats));
......@@ -1695,13 +1696,16 @@ Handle<Code> Pipeline::GenerateCodeForTesting(CompilationInfo* info,
}
// static
Handle<Code> Pipeline::GenerateCodeForTesting(CompilationInfo* info,
CallDescriptor* call_descriptor,
Graph* graph,
Schedule* schedule) {
Handle<Code> Pipeline::GenerateCodeForTesting(
CompilationInfo* info, CallDescriptor* call_descriptor, Graph* graph,
Schedule* schedule, SourcePositionTable* source_positions) {
// Construct a pipeline for scheduling and code generation.
ZoneStats zone_stats(info->isolate()->allocator());
PipelineData data(&zone_stats, info, graph, schedule);
// TODO(wasm): Refactor code generation to check for non-existing source
// table, then remove this conditional allocation.
if (!source_positions)
source_positions = new (info->zone()) SourcePositionTable(graph);
PipelineData data(&zone_stats, info, graph, schedule, source_positions);
std::unique_ptr<PipelineStatistics> pipeline_statistics;
if (FLAG_turbo_stats || FLAG_turbo_stats_nvp) {
pipeline_statistics.reset(new PipelineStatistics(info, &zone_stats));
......
......@@ -68,10 +68,10 @@ class Pipeline : public AllStatic {
// Run the pipeline on a machine graph and generate code. If {schedule} is
// {nullptr}, then compute a new schedule for code generation.
static Handle<Code> GenerateCodeForTesting(CompilationInfo* info,
CallDescriptor* call_descriptor,
Graph* graph,
Schedule* schedule = nullptr);
static Handle<Code> GenerateCodeForTesting(
CompilationInfo* info, CallDescriptor* call_descriptor, Graph* graph,
Schedule* schedule = nullptr,
SourcePositionTable* source_positions = nullptr);
private:
DISALLOW_IMPLICIT_CONSTRUCTORS(Pipeline);
......
......@@ -2451,6 +2451,8 @@ Node* WasmGraphBuilder::BuildJavaScriptToNumber(Node* node, Node* context) {
Node* result = graph()->NewNode(jsgraph()->common()->Call(desc), stub_code,
node, context, *effect_, *control_);
SetSourcePosition(result, 1);
*effect_ = result;
return result;
......@@ -2850,6 +2852,7 @@ void WasmGraphBuilder::BuildWasmToJSWrapper(Handle<JSReceiver> target,
}
*effect_ = call;
SetSourcePosition(call, 0);
// Convert the return value back.
Node* i32_zero = jsgraph()->Int32Constant(0);
......@@ -3324,7 +3327,8 @@ Handle<Code> CompileJSToWasmWrapper(Isolate* isolate,
Handle<Code> CompileWasmToJSWrapper(Isolate* isolate, Handle<JSReceiver> target,
wasm::FunctionSig* sig, uint32_t index,
Handle<String> module_name,
MaybeHandle<String> import_name) {
MaybeHandle<String> import_name,
wasm::ModuleOrigin origin) {
//----------------------------------------------------------------------------
// Create the Graph
//----------------------------------------------------------------------------
......@@ -3337,7 +3341,11 @@ Handle<Code> CompileWasmToJSWrapper(Isolate* isolate, Handle<JSReceiver> target,
Node* control = nullptr;
Node* effect = nullptr;
WasmGraphBuilder builder(&zone, &jsgraph, sig);
SourcePositionTable* source_position_table =
origin == wasm::kAsmJsOrigin ? new (&zone) SourcePositionTable(&graph)
: nullptr;
WasmGraphBuilder builder(&zone, &jsgraph, sig, source_position_table);
builder.set_control_ptr(&control);
builder.set_effect_ptr(&effect);
builder.BuildWasmToJSWrapper(target, sig);
......@@ -3373,7 +3381,8 @@ Handle<Code> CompileWasmToJSWrapper(Isolate* isolate, Handle<JSReceiver> target,
}
CompilationInfo info(func_name, isolate, &zone, flags);
code = Pipeline::GenerateCodeForTesting(&info, incoming, &graph, nullptr);
code = Pipeline::GenerateCodeForTesting(&info, incoming, &graph, nullptr,
source_position_table);
#ifdef ENABLE_DISASSEMBLER
if (FLAG_print_opt_code && !code.is_null()) {
OFStream os(stdout);
......
......@@ -94,7 +94,8 @@ class WasmCompilationUnit final {
Handle<Code> CompileWasmToJSWrapper(Isolate* isolate, Handle<JSReceiver> target,
wasm::FunctionSig* sig, uint32_t index,
Handle<String> module_name,
MaybeHandle<String> import_name);
MaybeHandle<String> import_name,
wasm::ModuleOrigin origin);
// Wraps a given wasm code object, producing a code object.
Handle<Code> CompileJSToWasmWrapper(Isolate* isolate,
......
......@@ -1580,11 +1580,20 @@ int WasmFrame::position() const {
isolate());
DCHECK_LE(0, position);
position = WasmCompiledModule::GetAsmJsSourcePosition(
compiled_module, function_index(), static_cast<uint32_t>(position));
compiled_module, function_index(), static_cast<uint32_t>(position),
at_to_number_conversion());
}
return position;
}
bool WasmFrame::at_to_number_conversion() const {
// WasmToJsFrame::ComputeCallerState encoded this for us in the constant pool
// address. If there was no WasmToJsFrame above us, we just return false here,
// but this information is not relevant in this case anyway.
intptr_t addr_int = reinterpret_cast<intptr_t>(constant_pool_address());
return addr_int == 1;
}
int WasmFrame::LookupExceptionHandlerInTable(int* stack_slots) {
DCHECK_NOT_NULL(stack_slots);
Code* code = LookupCode();
......@@ -1594,6 +1603,20 @@ int WasmFrame::LookupExceptionHandlerInTable(int* stack_slots) {
return table->LookupReturn(pc_offset);
}
void WasmToJsFrame::ComputeCallerState(State* state) const {
// Remember at which of the two calls inside it we are, and transfer this
// information to the subsequent WASM frame.
Code* code = unchecked_code();
AbstractCode* abstract_code = AbstractCode::cast(code);
int offset = static_cast<int>(pc() - code->instruction_start());
int at_to_number_call = abstract_code->SourcePosition(offset);
DCHECK(at_to_number_call == 0 || at_to_number_call == 1);
StubFrame::ComputeCallerState(state);
// Use a little hack here: The constant pool address is not used for wasm
// frames, so use it to encode whether we are at the to_number call.
state->constant_pool_address = reinterpret_cast<Address*>(at_to_number_call);
}
namespace {
......
......@@ -1111,6 +1111,7 @@ class WasmFrame : public StandardFrame {
uint32_t function_index() const;
Script* script() const override;
int position() const override;
bool at_to_number_conversion() const;
static WasmFrame* cast(StackFrame* frame) {
DCHECK(frame->is_wasm());
......@@ -1134,6 +1135,8 @@ class WasmToJsFrame : public StubFrame {
inline explicit WasmToJsFrame(StackFrameIteratorBase* iterator);
private:
void ComputeCallerState(State* state) const override;
friend class StackFrameIteratorBase;
};
......
......@@ -523,9 +523,15 @@ Handle<Object> Isolate::CaptureSimpleStackTrace(Handle<JSReceiver> error_object,
// be a wasm object.
DCHECK(wasm::IsWasmInstance(*instance) || instance->IsUndefined(this));
int flags = wasm::WasmIsAsmJs(*instance, this)
? FrameArray::kIsAsmJsWasmFrame
: FrameArray::kIsWasmFrame;
int flags = 0;
if (wasm::WasmIsAsmJs(*instance, this)) {
flags |= FrameArray::kIsAsmJsWasmFrame;
if (wasm_frame->at_to_number_conversion()) {
flags |= FrameArray::kAsmJsAtNumberConversion;
}
} else {
flags |= FrameArray::kIsWasmFrame;
}
elements =
FrameArray::AppendWasmFrame(elements, instance, wasm_function_index,
......@@ -1557,8 +1563,10 @@ bool Isolate::ComputeLocationFromStackTrace(MessageLocation* target,
int func_index = elements->WasmFunctionIndex(i)->value();
int code_offset = elements->Offset(i)->value();
int byte_pos = elements->Code(i)->SourcePosition(code_offset);
bool at_to_number_conversion =
elements->Flags(i)->value() & FrameArray::kAsmJsAtNumberConversion;
int source_pos = WasmCompiledModule::GetAsmJsSourcePosition(
compiled_module, func_index, byte_pos);
compiled_module, func_index, byte_pos, at_to_number_conversion);
Handle<Script> script = compiled_module->script();
*target = MessageLocation(script, source_pos, source_pos + 1);
......
......@@ -680,6 +680,15 @@ Handle<Object> WasmStackFrame::Null() const {
return isolate_->factory()->null_value();
}
void AsmJsWasmStackFrame::FromFrameArray(Isolate* isolate,
Handle<FrameArray> array,
int frame_ix) {
DCHECK(array->IsAsmJsWasmFrame(frame_ix));
WasmStackFrame::FromFrameArray(isolate, array, frame_ix);
is_at_number_conversion_ =
array->Flags(frame_ix)->value() & FrameArray::kAsmJsAtNumberConversion;
}
Handle<Object> AsmJsWasmStackFrame::GetReceiver() const {
return isolate_->global_proxy();
}
......@@ -711,7 +720,8 @@ int AsmJsWasmStackFrame::GetPosition() const {
isolate_);
DCHECK_LE(0, byte_offset);
return WasmCompiledModule::GetAsmJsSourcePosition(
compiled_module, wasm_func_index_, static_cast<uint32_t>(byte_offset));
compiled_module, wasm_func_index_, static_cast<uint32_t>(byte_offset),
is_at_number_conversion_);
}
int AsmJsWasmStackFrame::GetLineNumber() {
......
......@@ -163,6 +163,7 @@ class WasmStackFrame : public StackFrameBase {
void FromFrameArray(Isolate* isolate, Handle<FrameArray> array, int frame_ix);
friend class FrameArrayIterator;
friend class AsmJsWasmStackFrame;
};
class AsmJsWasmStackFrame : public WasmStackFrame {
......@@ -180,6 +181,12 @@ class AsmJsWasmStackFrame : public WasmStackFrame {
int GetColumnNumber() override;
MaybeHandle<String> ToString() override;
private:
friend class FrameArrayIterator;
void FromFrameArray(Isolate* isolate, Handle<FrameArray> array, int frame_ix);
bool is_at_number_conversion_;
};
class FrameArrayIterator {
......
......@@ -3132,6 +3132,7 @@ class FrameArray : public FixedArray {
static const int kIsAsmJsWasmFrame = 1 << 1;
static const int kIsStrict = 1 << 2;
static const int kForceConstructor = 1 << 3;
static const int kAsmJsAtNumberConversion = 1 << 4;
static Handle<FrameArray> AppendJSFrame(Handle<FrameArray> in,
Handle<Object> receiver,
......
......@@ -1204,7 +1204,7 @@ AsmJsOffsetsResult DecodeAsmJsOffsets(const byte* tables_start,
for (uint32_t i = 0; i < functions_count && decoder.ok(); ++i) {
uint32_t size = decoder.consume_u32v("table size");
if (size == 0) {
table.push_back(std::vector<std::pair<int, int>>());
table.push_back(std::vector<AsmJsOffsetEntry>());
continue;
}
if (!decoder.checkAvailable(size)) {
......@@ -1214,12 +1214,17 @@ AsmJsOffsetsResult DecodeAsmJsOffsets(const byte* tables_start,
uint32_t locals_size = decoder.consume_u32("locals size");
int last_byte_offset = locals_size;
int last_asm_position = 0;
std::vector<std::pair<int, int>> func_asm_offsets;
std::vector<AsmJsOffsetEntry> func_asm_offsets;
func_asm_offsets.reserve(size / 4); // conservative estimation
while (decoder.ok() && decoder.pc() < table_end) {
last_byte_offset += decoder.consume_u32v("byte offset delta");
last_asm_position += decoder.consume_i32v("asm position delta");
func_asm_offsets.push_back({last_byte_offset, last_asm_position});
int call_position =
last_asm_position + decoder.consume_i32v("call position delta");
int to_number_position =
call_position + decoder.consume_i32v("to_number position delta");
last_asm_position = to_number_position;
func_asm_offsets.push_back(
{last_byte_offset, call_position, to_number_position});
}
if (decoder.pc() != table_end) {
decoder.error("broken asm offset table");
......
......@@ -18,7 +18,12 @@ typedef Result<const WasmModule*> ModuleResult;
typedef Result<WasmFunction*> FunctionResult;
typedef std::vector<std::pair<int, int>> FunctionOffsets;
typedef Result<FunctionOffsets> FunctionOffsetsResult;
typedef std::vector<std::vector<std::pair<int, int>>> AsmJsOffsets;
struct AsmJsOffsetEntry {
int byte_offset;
int source_position_call;
int source_position_number_conversion;
};
typedef std::vector<std::vector<AsmJsOffsetEntry>> AsmJsOffsets;
typedef Result<AsmJsOffsets> AsmJsOffsetsResult;
// Decodes the bytes of a WASM module between {module_start} and {module_end}.
......
......@@ -150,7 +150,8 @@ void WasmFunctionBuilder::SetName(Vector<const char> name) {
memcpy(name_.data(), name.start(), name.length());
}
void WasmFunctionBuilder::AddAsmWasmOffset(int asm_position) {
void WasmFunctionBuilder::AddAsmWasmOffset(int call_position,
int to_number_position) {
// We only want to emit one mapping per byte offset:
DCHECK(asm_offsets_.size() == 0 || body_.size() > last_asm_byte_offset_);
......@@ -159,9 +160,12 @@ void WasmFunctionBuilder::AddAsmWasmOffset(int asm_position) {
asm_offsets_.write_u32v(byte_offset - last_asm_byte_offset_);
last_asm_byte_offset_ = byte_offset;
DCHECK_GE(asm_position, 0);
asm_offsets_.write_i32v(asm_position - last_asm_source_position_);
last_asm_source_position_ = asm_position;
DCHECK_GE(call_position, 0);
asm_offsets_.write_i32v(call_position - last_asm_source_position_);
DCHECK_GE(to_number_position, 0);
asm_offsets_.write_i32v(to_number_position - call_position);
last_asm_source_position_ = to_number_position;
}
void WasmFunctionBuilder::WriteSignature(ZoneBuffer& buffer) const {
......
......@@ -134,7 +134,7 @@ class V8_EXPORT_PRIVATE WasmFunctionBuilder : public ZoneObject {
void EmitDirectCallIndex(uint32_t index);
void ExportAs(Vector<const char> name);
void SetName(Vector<const char> name);
void AddAsmWasmOffset(int asm_position);
void AddAsmWasmOffset(int call_position, int to_number_position);
void WriteSignature(ZoneBuffer& buffer) const;
void WriteExports(ZoneBuffer& buffer) const;
......
......@@ -977,7 +977,8 @@ static Handle<Code> CompileImportWrapper(Isolate* isolate, int index,
FunctionSig* sig,
Handle<JSReceiver> target,
Handle<String> module_name,
MaybeHandle<String> import_name) {
MaybeHandle<String> import_name,
ModuleOrigin origin) {
Handle<Code> code;
WasmFunction* other_func = GetWasmFunctionForImportWrapper(isolate, target);
if (other_func) {
......@@ -991,7 +992,7 @@ static Handle<Code> CompileImportWrapper(Isolate* isolate, int index,
} else {
// Signature mismatch. Compile a new wrapper for the new signature.
return compiler::CompileWasmToJSWrapper(isolate, target, sig, index,
module_name, import_name);
module_name, import_name, origin);
}
}
......@@ -1548,7 +1549,8 @@ class WasmInstanceBuilder {
Handle<Code> import_wrapper = CompileImportWrapper(
isolate_, index, module_->functions[import.index].sig,
Handle<JSReceiver>::cast(function), module_name, function_name);
Handle<JSReceiver>::cast(function), module_name, function_name,
module_->origin);
if (import_wrapper.is_null()) {
ReportFFIError("imported function does not match the expected type",
index, module_name, function_name);
......
......@@ -461,6 +461,14 @@ bool WasmCompiledModule::GetPositionInfo(uint32_t position,
}
namespace {
enum AsmJsOffsetTableEntryLayout {
kOTEByteOffset,
kOTECallPosition,
kOTENumberConvPosition,
kOTESize
};
Handle<ByteArray> GetDecodedAsmJsOffsetTable(
Handle<WasmCompiledModule> compiled_module, Isolate* isolate) {
DCHECK(compiled_module->has_asm_js_offset_table());
......@@ -488,13 +496,16 @@ Handle<ByteArray> GetDecodedAsmJsOffsetTable(
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;
int num_entries = 0;
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);
size_t new_size = asm_offsets.val[func].size();
DCHECK_LE(new_size, static_cast<size_t>(kMaxInt) - num_entries);
num_entries += static_cast<int>(new_size);
}
// One byte to encode that this is a decoded table.
DCHECK_GE(kMaxInt,
1 + static_cast<uint64_t>(num_entries) * kOTESize * kIntSize);
int total_size = 1 + num_entries * kOTESize * kIntSize;
Handle<ByteArray> decoded_table =
isolate->factory()->NewByteArray(total_size, TENURED);
decoded_table->set(total_size - 1, AsmJsTableType::Decoded);
......@@ -503,16 +514,19 @@ Handle<ByteArray> GetDecodedAsmJsOffsetTable(
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];
std::vector<AsmJsOffsetEntry>& 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) {
for (AsmJsOffsetEntry& e : 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_IMPLIES(idx > 0, func_offset + e.byte_offset >
decoded_table->get_int(idx - kOTESize));
decoded_table->set_int(idx + kOTEByteOffset, func_offset + e.byte_offset);
decoded_table->set_int(idx + kOTECallPosition, e.source_position_call);
decoded_table->set_int(idx + kOTENumberConvPosition,
e.source_position_number_conversion);
idx += kOTESize;
}
}
DCHECK_EQ(total_size, idx * kIntSize + 1);
......@@ -522,7 +536,7 @@ Handle<ByteArray> GetDecodedAsmJsOffsetTable(
int WasmCompiledModule::GetAsmJsSourcePosition(
Handle<WasmCompiledModule> compiled_module, uint32_t func_index,
uint32_t byte_offset) {
uint32_t byte_offset, bool is_at_number_conversion) {
Isolate* isolate = compiled_module->GetIsolate();
Handle<ByteArray> offset_table =
GetDecodedAsmJsOffsetTable(compiled_module, isolate);
......@@ -533,12 +547,12 @@ int WasmCompiledModule::GetAsmJsSourcePosition(
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
int left = 0; // inclusive
int right = offset_table->length() / kIntSize / kOTESize; // exclusive
DCHECK_LT(left, right);
while (right - left > 1) {
int mid = left + (right - left) / 2;
int mid_entry = offset_table->get_int(2 * mid);
int mid_entry = offset_table->get_int(kOTESize * mid);
DCHECK_GE(kMaxInt, mid_entry);
if (static_cast<uint32_t>(mid_entry) <= total_offset) {
left = mid;
......@@ -548,9 +562,9 @@ int WasmCompiledModule::GetAsmJsSourcePosition(
}
// 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);
DCHECK_EQ(total_offset, offset_table->get_int(kOTESize * left));
int idx = is_at_number_conversion ? kOTENumberConvPosition : kOTECallPosition;
return offset_table->get_int(kOTESize * left + idx);
}
v8::debug::WasmDisassembly WasmCompiledModule::DisassembleFunction(
......
......@@ -296,7 +296,8 @@ class WasmCompiledModule : public FixedArray {
// 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);
uint32_t func_index, uint32_t byte_offset,
bool is_at_number_conversion);
// Compute the disassembly of a wasm function.
// Returns the disassembly string and a list of <byte_offset, line, column>
......
......@@ -199,9 +199,9 @@ class TestingModule : public ModuleEnv {
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());
Handle<Code> code = CompileWasmToJSWrapper(
isolate_, jsfunc, sig, index, Handle<String>::null(),
Handle<String>::null(), module->origin);
instance->function_code[index] = code;
return index;
}
......
// 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.
// Flags: --validate-asm
var filename = '(?:[^ ]+/)?test/mjsunit/wasm/asm-wasm-exception-in-tonumber.js';
filename = filename.replace(/\//g, '[/\\\\]');
function verifyStack(frames, expected) {
assertTrue(frames.length >= expected.length, 'too few frames');
print('frames on detailed stack (' + frames.length + '):');
frames.forEach((fr, i) => print('[' + i + '] ' + fr));
expected.forEach(function(exp, i) {
assertEquals(
exp[0], frames[i].getFunctionName(), '[' + i + '].getFunctionName()');
assertEquals(
exp[1], frames[i].getColumnNumber(), '[' + i + '].getColumnNumber()');
});
}
function verifyPreformattedStack(e, expected_lines) {
print('preformatted stack: ' + e.stack);
var lines = e.stack.split('\n');
assertTrue(lines.length >= expected_lines.length, 'too few lines');
for (var i = 0; i < expected_lines.length; ++i) {
assertMatches(expected_lines[i], lines[i], 'line ' + i);
}
}
function sym(return_sym) {
if (return_sym) return Symbol();
throw Error("user-thrown");
}
function generateAsmJs(stdlib, foreign) {
'use asm';
var sym = foreign.sym;
function callSym(i) {
i=i|0;
return sym(i|0) | 0;
}
return callSym;
}
function testHelper(use_asm_js, check_detailed, expected, input) {
if (check_detailed) {
Error.prepareStackTrace = (error, frames) => frames;
} else {
delete Error.prepareStackTrace;
}
var fn_code = '(' + generateAsmJs.toString() + ')({}, {sym: sym})';
if (!use_asm_js) fn_code = fn_code.replace('use asm', '');
//print('executing:\n' + fn_code);
var asm_js_fn = eval(fn_code);
try {
asm_js_fn(input);
} catch (e) {
if (check_detailed) {
verifyStack(e.stack, expected);
} else {
verifyPreformattedStack(e, expected);
}
}
}
function testAll(expected_stack, expected_frames, input) {
for (use_asm_js = 0; use_asm_js <= 1; ++use_asm_js) {
for (test_detailed = 0; test_detailed <= 1; ++test_detailed) {
print('\nConfig: asm ' + use_asm_js + '; detailed ' + test_detailed);
testHelper(
use_asm_js, test_detailed,
test_detailed ? expected_frames : expected_stack, input);
}
}
}
(function testStackForThrowAtCall() {
var expected_stack = [
'^Error: user-thrown$',
'^ *at sym \\(' + filename + ':\\d+:9\\)$',
'^ *at callSym \\(.*<anonymous>:\\d+:12\\)$',
];
var expected_frames = [
// function pos
[ "sym", 9],
[ "callSym", 12],
];
testAll(expected_stack, expected_frames, 0);
})();
(function testStackForThrowAtConversion() {
var expected_stack = [
'^TypeError: Cannot convert a Symbol value to a number$',
'^ *at callSym \\(.*<anonymous>:\\d+:21\\)$',
];
var expected_frames = [
// function pos
[ "callSym", 21],
];
testAll(expected_stack, expected_frames, 1);
})();
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