Commit 890d28f3 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).
- the StackFrame::State struct now also holds the callee_pc_address,
  which is set in ComputeCallerState. The WASM frame uses this
  information to determine whether the callee frame is WASM_TO_JS, and
  whether that frame is at the ToNumber conversion call.
- 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

Committed: https://crrev.com/94cd46b55e24fa2bb7b06b3da4d5ba7f029bc262
Review-Url: https://codereview.chromium.org/2555243002
Cr-Original-Commit-Position: refs/heads/master@{#41599}
Cr-Commit-Position: refs/heads/master@{#41613}
parent 54656518
......@@ -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,
......
......@@ -574,6 +574,7 @@ void ExitFrame::ComputeCallerState(State* state) const {
state->fp = Memory::Address_at(fp() + ExitFrameConstants::kCallerFPOffset);
state->pc_address = ResolveReturnAddressLocation(
reinterpret_cast<Address*>(fp() + ExitFrameConstants::kCallerPCOffset));
state->callee_pc_address = nullptr;
if (FLAG_enable_embedded_constant_pool) {
state->constant_pool_address = reinterpret_cast<Address*>(
fp() + ExitFrameConstants::kConstantPoolOffset);
......@@ -603,7 +604,7 @@ StackFrame::Type ExitFrame::GetStateForFramePointer(Address fp, State* state) {
if (fp == 0) return NONE;
Address sp = ComputeStackPointer(fp);
FillState(fp, sp, state);
DCHECK(*state->pc_address != NULL);
DCHECK_NOT_NULL(*state->pc_address);
return ComputeFrameType(fp);
}
......@@ -637,11 +638,12 @@ void ExitFrame::FillState(Address fp, Address sp, State* state) {
state->fp = fp;
state->pc_address = ResolveReturnAddressLocation(
reinterpret_cast<Address*>(sp - 1 * kPCOnStackSize));
state->callee_pc_address = nullptr;
// The constant pool recorded in the exit frame is not associated
// with the pc in this state (the return address into a C entry
// stub). ComputeCallerState will retrieve the constant pool
// together with the associated caller pc.
state->constant_pool_address = NULL;
state->constant_pool_address = nullptr;
}
JSFunction* BuiltinExitFrame::function() const {
......@@ -745,6 +747,7 @@ void StandardFrame::ComputeCallerState(State* state) const {
state->fp = caller_fp();
state->pc_address = ResolveReturnAddressLocation(
reinterpret_cast<Address*>(ComputePCAddress(fp())));
state->callee_pc_address = pc_address();
state->constant_pool_address =
reinterpret_cast<Address*>(ComputeConstantPoolAddress(fp()));
}
......@@ -879,7 +882,7 @@ void StubFrame::Iterate(ObjectVisitor* v) const {
Code* StubFrame::unchecked_code() const {
return static_cast<Code*>(isolate()->FindCodeObject(pc()));
return isolate()->FindCodeObject(pc());
}
......@@ -1580,11 +1583,25 @@ 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 {
// Check whether our callee is a WASM_TO_JS frame, and this frame is at the
// ToNumber conversion call.
Address callee_pc = reinterpret_cast<Address>(this->callee_pc());
Code* code = callee_pc ? isolate()->FindCodeObject(callee_pc) : nullptr;
if (!code || code->kind() != Code::WASM_TO_JS_FUNCTION) return false;
int offset = static_cast<int>(callee_pc - code->instruction_start());
int pos = AbstractCode::cast(code)->SourcePosition(offset);
DCHECK(pos == 0 || pos == 1);
// The imported call has position 0, ToNumber has position 1.
return !!pos;
}
int WasmFrame::LookupExceptionHandlerInTable(int* stack_slots) {
DCHECK_NOT_NULL(stack_slots);
Code* code = LookupCode();
......
......@@ -441,12 +441,11 @@ class StackFrame BASE_EMBEDDED {
};
struct State {
State() : sp(NULL), fp(NULL), pc_address(NULL),
constant_pool_address(NULL) { }
Address sp;
Address fp;
Address* pc_address;
Address* constant_pool_address;
Address sp = nullptr;
Address fp = nullptr;
Address* pc_address = nullptr;
Address* callee_pc_address = nullptr;
Address* constant_pool_address = nullptr;
};
// Copy constructor; it breaks the connection to host iterator
......@@ -485,6 +484,7 @@ class StackFrame BASE_EMBEDDED {
// Accessors.
Address sp() const { return state_.sp; }
Address fp() const { return state_.fp; }
Address callee_pc() const { return *state_.callee_pc_address; }
Address caller_sp() const { return GetCallerStackPointer(); }
// If this frame is optimized and was dynamically aligned return its old
......@@ -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());
......
......@@ -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);
......@@ -3038,7 +3046,7 @@ int Isolate::GenerateIdentityHash(uint32_t mask) {
return hash != 0 ? hash : 1;
}
Object* Isolate::FindCodeObject(Address a) {
Code* Isolate::FindCodeObject(Address a) {
return inner_pointer_to_code_cache()->GcSafeFindCodeForInnerPointer(a);
}
......
......@@ -1071,7 +1071,7 @@ class Isolate {
int GenerateIdentityHash(uint32_t mask);
// Given an address occupied by a live code object, return that object.
Object* FindCodeObject(Address a);
Code* FindCodeObject(Address a);
int NextOptimizationId() {
int id = next_optimization_id_++;
......
......@@ -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);
}
}
......@@ -1552,7 +1553,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