Commit 73d45c96 authored by Andreas Haas's avatar Andreas Haas Committed by Commit Bot

[wasm] Use builtins wrappers for traps

With this CL the out-of-line code of TrapIf will call a builtin instead
of doing a direct runtime call, which is cheaper. In the best case, the
out-of-line code now consists of a single call instruction. The builtin
will load the trapID and then call the runtime to throw a trap.

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

Change-Id: I8a75ba09f40236a3d5cbdc17c1317baf0a3c915a
Reviewed-on: https://chromium-review.googlesource.com/445937
Commit-Queue: Andreas Haas <ahaas@chromium.org>
Reviewed-by: 's avatarBen Titzer <titzer@chromium.org>
Reviewed-by: 's avatarClemens Hammacher <clemensh@chromium.org>
Cr-Commit-Position: refs/heads/master@{#43366}
parent 14de196a
......@@ -4,6 +4,7 @@
#include "src/builtins/builtins-utils.h"
#include "src/code-stub-assembler.h"
#include "src/wasm/wasm-opcodes.h"
namespace v8 {
namespace internal {
......@@ -15,5 +16,14 @@ TF_BUILTIN(WasmStackGuard, CodeStubAssembler) {
TailCallRuntime(Runtime::kWasmStackGuard, context);
}
#define DECLARE_ENUM(name) \
TF_BUILTIN(ThrowWasm##name, CodeStubAssembler) { \
int message_id = wasm::WasmOpcodes::TrapReasonToMessageId(wasm::k##name); \
TailCallRuntime(Runtime::kThrowWasmErrorFromTrapIf, \
SmiConstant(Smi::kZero), SmiConstant(message_id)); \
}
FOREACH_WASM_TRAPREASON(DECLARE_ENUM)
#undef DECLARE_ENUM
} // namespace internal
} // namespace v8
......@@ -834,7 +834,19 @@ class Isolate;
CPP(TypedArrayPrototypeCopyWithin) \
\
/* Wasm */ \
TFS(WasmStackGuard, BUILTIN, kNoExtraICState, WasmStackGuard, 1)
TFS(WasmStackGuard, BUILTIN, kNoExtraICState, WasmRuntimeCall, 1) \
TFS(ThrowWasmTrapUnreachable, BUILTIN, kNoExtraICState, WasmRuntimeCall, 1) \
TFS(ThrowWasmTrapMemOutOfBounds, BUILTIN, kNoExtraICState, WasmRuntimeCall, \
1) \
TFS(ThrowWasmTrapDivByZero, BUILTIN, kNoExtraICState, WasmRuntimeCall, 1) \
TFS(ThrowWasmTrapDivUnrepresentable, BUILTIN, kNoExtraICState, \
WasmRuntimeCall, 1) \
TFS(ThrowWasmTrapRemByZero, BUILTIN, kNoExtraICState, WasmRuntimeCall, 1) \
TFS(ThrowWasmTrapFloatUnrepresentable, BUILTIN, kNoExtraICState, \
WasmRuntimeCall, 1) \
TFS(ThrowWasmTrapFuncInvalid, BUILTIN, kNoExtraICState, WasmRuntimeCall, 1) \
TFS(ThrowWasmTrapFuncSigMismatch, BUILTIN, kNoExtraICState, WasmRuntimeCall, \
1)
#define IGNORE_BUILTIN(...)
......@@ -874,7 +886,7 @@ class Builtins {
// Disassembler support.
const char* Lookup(byte* pc);
enum Name {
enum Name : int32_t {
#define DEF_ENUM(Name, ...) k##Name,
BUILTIN_LIST_ALL(DEF_ENUM)
#undef DEF_ENUM
......
......@@ -2035,8 +2035,8 @@ void CodeGenerator::AssembleArchTrap(Instruction* instr,
void Generate() final {
ArmOperandConverter i(gen_, instr_);
Runtime::FunctionId trap_id = static_cast<Runtime::FunctionId>(
i.InputInt32(instr_->InputCount() - 1));
Builtins::Name trap_id =
static_cast<Builtins::Name>(i.InputInt32(instr_->InputCount() - 1));
bool old_has_frame = __ has_frame();
if (frame_elided_) {
__ set_has_frame(true);
......@@ -2049,8 +2049,8 @@ void CodeGenerator::AssembleArchTrap(Instruction* instr,
}
private:
void GenerateCallToTrap(Runtime::FunctionId trap_id) {
if (trap_id == Runtime::kNumFunctions) {
void GenerateCallToTrap(Builtins::Name trap_id) {
if (trap_id == Builtins::builtin_count) {
// We cannot test calls to the runtime in cctest/test-run-wasm.
// Therefore we emit a call to C here instead of a call to the runtime.
// We use the context register as the scratch register, because we do
......@@ -2062,9 +2062,9 @@ void CodeGenerator::AssembleArchTrap(Instruction* instr,
__ LeaveFrame(StackFrame::WASM_COMPILED);
__ Ret();
} else {
__ Move(cp, Smi::kZero);
gen_->AssembleSourcePosition(instr_);
__ CallRuntime(trap_id);
__ Call(handle(isolate()->builtins()->builtin(trap_id), isolate()),
RelocInfo::CODE_TARGET);
ReferenceMap* reference_map =
new (gen_->zone()) ReferenceMap(gen_->zone());
gen_->RecordSafepoint(reference_map, Safepoint::kSimple, 0,
......
......@@ -1711,8 +1711,8 @@ void CodeGenerator::AssembleArchTrap(Instruction* instr,
gen_(gen) {}
void Generate() final {
Arm64OperandConverter i(gen_, instr_);
Runtime::FunctionId trap_id = static_cast<Runtime::FunctionId>(
i.InputInt32(instr_->InputCount() - 1));
Builtins::Name trap_id =
static_cast<Builtins::Name>(i.InputInt32(instr_->InputCount() - 1));
bool old_has_frame = __ has_frame();
if (frame_elided_) {
__ set_has_frame(true);
......@@ -1725,8 +1725,8 @@ void CodeGenerator::AssembleArchTrap(Instruction* instr,
}
private:
void GenerateCallToTrap(Runtime::FunctionId trap_id) {
if (trap_id == Runtime::kNumFunctions) {
void GenerateCallToTrap(Builtins::Name trap_id) {
if (trap_id == Builtins::builtin_count) {
// We cannot test calls to the runtime in cctest/test-run-wasm.
// Therefore we emit a call to C here instead of a call to the runtime.
__ CallCFunction(
......@@ -1736,11 +1736,11 @@ void CodeGenerator::AssembleArchTrap(Instruction* instr,
__ Ret();
} else {
DCHECK(csp.Is(__ StackPointer()));
__ Move(cp, Smi::kZero);
// Initialize the jssp because it is required for the runtime call.
__ Mov(jssp, csp);
gen_->AssembleSourcePosition(instr_);
__ CallRuntime(trap_id);
__ Call(handle(isolate()->builtins()->builtin(trap_id), isolate()),
RelocInfo::CODE_TARGET);
ReferenceMap* reference_map =
new (gen_->zone()) ReferenceMap(gen_->zone());
gen_->RecordSafepoint(reference_map, Safepoint::kSimple, 0,
......
......@@ -662,8 +662,8 @@ struct CommonOperatorGlobalCache final {
1, 1, 1, 0, 0, 1, // counts
trap_id) {} // parameter
};
#define CACHED_TRAP_IF(Trap) \
TrapIfOperator<static_cast<int32_t>(Runtime::kThrowWasm##Trap)> \
#define CACHED_TRAP_IF(Trap) \
TrapIfOperator<static_cast<int32_t>(Builtins::kThrowWasm##Trap)> \
kTrapIf##Trap##Operator;
CACHED_TRAP_IF_LIST(CACHED_TRAP_IF)
#undef CACHED_TRAP_IF
......@@ -678,8 +678,8 @@ struct CommonOperatorGlobalCache final {
1, 1, 1, 0, 0, 1, // counts
trap_id) {} // parameter
};
#define CACHED_TRAP_UNLESS(Trap) \
TrapUnlessOperator<static_cast<int32_t>(Runtime::kThrowWasm##Trap)> \
#define CACHED_TRAP_UNLESS(Trap) \
TrapUnlessOperator<static_cast<int32_t>(Builtins::kThrowWasm##Trap)> \
kTrapUnless##Trap##Operator;
CACHED_TRAP_UNLESS_LIST(CACHED_TRAP_UNLESS)
#undef CACHED_TRAP_UNLESS
......@@ -884,8 +884,8 @@ const Operator* CommonOperatorBuilder::DeoptimizeUnless(
const Operator* CommonOperatorBuilder::TrapIf(int32_t trap_id) {
switch (trap_id) {
#define CACHED_TRAP_IF(Trap) \
case Runtime::kThrowWasm##Trap: \
#define CACHED_TRAP_IF(Trap) \
case Builtins::kThrowWasm##Trap: \
return &cache_.kTrapIf##Trap##Operator;
CACHED_TRAP_IF_LIST(CACHED_TRAP_IF)
#undef CACHED_TRAP_IF
......@@ -903,8 +903,8 @@ const Operator* CommonOperatorBuilder::TrapIf(int32_t trap_id) {
const Operator* CommonOperatorBuilder::TrapUnless(int32_t trap_id) {
switch (trap_id) {
#define CACHED_TRAP_UNLESS(Trap) \
case Runtime::kThrowWasm##Trap: \
#define CACHED_TRAP_UNLESS(Trap) \
case Builtins::kThrowWasm##Trap: \
return &cache_.kTrapUnless##Trap##Operator;
CACHED_TRAP_UNLESS_LIST(CACHED_TRAP_UNLESS)
#undef CACHED_TRAP_UNLESS
......
......@@ -2048,8 +2048,8 @@ void CodeGenerator::AssembleArchTrap(Instruction* instr,
void Generate() final {
IA32OperandConverter i(gen_, instr_);
Runtime::FunctionId trap_id = static_cast<Runtime::FunctionId>(
i.InputInt32(instr_->InputCount() - 1));
Builtins::Name trap_id =
static_cast<Builtins::Name>(i.InputInt32(instr_->InputCount() - 1));
bool old_has_frame = __ has_frame();
if (frame_elided_) {
__ set_has_frame(true);
......@@ -2062,8 +2062,8 @@ void CodeGenerator::AssembleArchTrap(Instruction* instr,
}
private:
void GenerateCallToTrap(Runtime::FunctionId trap_id) {
if (trap_id == Runtime::kNumFunctions) {
void GenerateCallToTrap(Builtins::Name trap_id) {
if (trap_id == Builtins::builtin_count) {
// We cannot test calls to the runtime in cctest/test-run-wasm.
// Therefore we emit a call to C here instead of a call to the runtime.
__ PrepareCallCFunction(0, esi);
......@@ -2073,9 +2073,9 @@ void CodeGenerator::AssembleArchTrap(Instruction* instr,
__ LeaveFrame(StackFrame::WASM_COMPILED);
__ Ret();
} else {
__ Move(esi, Smi::kZero);
gen_->AssembleSourcePosition(instr_);
__ CallRuntime(trap_id);
__ Call(handle(isolate()->builtins()->builtin(trap_id), isolate()),
RelocInfo::CODE_TARGET);
ReferenceMap* reference_map =
new (gen_->zone()) ReferenceMap(gen_->zone());
gen_->RecordSafepoint(reference_map, Safepoint::kSimple, 0,
......
......@@ -1748,8 +1748,8 @@ void CodeGenerator::AssembleArchTrap(Instruction* instr,
void Generate() final {
MipsOperandConverter i(gen_, instr_);
Runtime::FunctionId trap_id = static_cast<Runtime::FunctionId>(
i.InputInt32(instr_->InputCount() - 1));
Builtins::Name trap_id =
static_cast<Builtins::Name>(i.InputInt32(instr_->InputCount() - 1));
bool old_has_frame = __ has_frame();
if (frame_elided_) {
__ set_has_frame(true);
......@@ -1762,8 +1762,8 @@ void CodeGenerator::AssembleArchTrap(Instruction* instr,
}
private:
void GenerateCallToTrap(Runtime::FunctionId trap_id) {
if (trap_id == Runtime::kNumFunctions) {
void GenerateCallToTrap(Builtins::Name trap_id) {
if (trap_id == Builtins::builtin_count) {
// We cannot test calls to the runtime in cctest/test-run-wasm.
// Therefore we emit a call to C here instead of a call to the runtime.
// We use the context register as the scratch register, because we do
......@@ -1775,9 +1775,9 @@ void CodeGenerator::AssembleArchTrap(Instruction* instr,
__ LeaveFrame(StackFrame::WASM_COMPILED);
__ Ret();
} else {
__ Move(cp, Smi::kZero);
gen_->AssembleSourcePosition(instr_);
__ CallRuntime(trap_id);
__ Call(handle(isolate()->builtins()->builtin(trap_id), isolate()),
RelocInfo::CODE_TARGET);
ReferenceMap* reference_map =
new (gen_->zone()) ReferenceMap(gen_->zone());
gen_->RecordSafepoint(reference_map, Safepoint::kSimple, 0,
......
......@@ -2083,8 +2083,8 @@ void CodeGenerator::AssembleArchTrap(Instruction* instr,
gen_(gen) {}
void Generate() final {
MipsOperandConverter i(gen_, instr_);
Runtime::FunctionId trap_id = static_cast<Runtime::FunctionId>(
i.InputInt32(instr_->InputCount() - 1));
Builtins::Name trap_id =
static_cast<Builtins::Name>(i.InputInt32(instr_->InputCount() - 1));
bool old_has_frame = __ has_frame();
if (frame_elided_) {
__ set_has_frame(true);
......@@ -2097,8 +2097,8 @@ void CodeGenerator::AssembleArchTrap(Instruction* instr,
}
private:
void GenerateCallToTrap(Runtime::FunctionId trap_id) {
if (trap_id == Runtime::kNumFunctions) {
void GenerateCallToTrap(Builtins::Name trap_id) {
if (trap_id == Builtins::builtin_count) {
// We cannot test calls to the runtime in cctest/test-run-wasm.
// Therefore we emit a call to C here instead of a call to the runtime.
// We use the context register as the scratch register, because we do
......@@ -2110,9 +2110,9 @@ void CodeGenerator::AssembleArchTrap(Instruction* instr,
__ LeaveFrame(StackFrame::WASM_COMPILED);
__ Ret();
} else {
__ Move(cp, Smi::kZero);
gen_->AssembleSourcePosition(instr_);
__ CallRuntime(trap_id);
__ Call(handle(isolate()->builtins()->builtin(trap_id), isolate()),
RelocInfo::CODE_TARGET);
ReferenceMap* reference_map =
new (gen_->zone()) ReferenceMap(gen_->zone());
gen_->RecordSafepoint(reference_map, Safepoint::kSimple, 0,
......
......@@ -11,6 +11,8 @@
#include "src/base/platform/elapsed-timer.h"
#include "src/base/platform/platform.h"
#include "src/builtins/builtins.h"
#include "src/compiler/access-builder.h"
#include "src/compiler/common-operator.h"
#include "src/compiler/compiler-source-position-table.h"
......@@ -172,23 +174,23 @@ class WasmTrapHelper : public ZoneObject {
return TrapIfEq64(reason, node, 0, position);
}
Runtime::FunctionId GetFunctionIdForTrap(wasm::TrapReason reason) {
Builtins::Name GetBuiltinIdForTrap(wasm::TrapReason reason) {
if (builder_->module_ && !builder_->module_->instance->context.is_null()) {
switch (reason) {
#define TRAPREASON_TO_MESSAGE(name) \
case wasm::k##name: \
return Runtime::kThrowWasm##name;
return Builtins::kThrowWasm##name;
FOREACH_WASM_TRAPREASON(TRAPREASON_TO_MESSAGE)
#undef TRAPREASON_TO_MESSAGE
default:
UNREACHABLE();
return Runtime::kNumFunctions;
return Builtins::builtin_count;
}
} else {
// We use Runtime::kNumFunctions as a marker to tell the code generator
// to generate a call to a testing c-function instead of a runtime
// function. This code should only be called from a cctest.
return Runtime::kNumFunctions;
return Builtins::builtin_count;
}
}
......@@ -204,7 +206,7 @@ class WasmTrapHelper : public ZoneObject {
wasm::WasmCodePosition position) {
#ifdef WASM_TRAP_IF_SUPPORTED
if (FLAG_wasm_trap_if) {
int32_t trap_id = GetFunctionIdForTrap(reason);
int32_t trap_id = GetBuiltinIdForTrap(reason);
Node* node = graph()->NewNode(common()->TrapIf(trap_id), cond,
builder_->Effect(), builder_->Control());
*builder_->control_ = node;
......@@ -220,7 +222,7 @@ class WasmTrapHelper : public ZoneObject {
wasm::WasmCodePosition position) {
#ifdef WASM_TRAP_IF_SUPPORTED
if (FLAG_wasm_trap_if) {
int32_t trap_id = GetFunctionIdForTrap(reason);
int32_t trap_id = GetBuiltinIdForTrap(reason);
Node* node = graph()->NewNode(common()->TrapUnless(trap_id), cond,
builder_->Effect(), builder_->Control());
......@@ -495,7 +497,7 @@ void WasmGraphBuilder::StackCheck(wasm::WasmCodePosition position,
Handle<Code> code = jsgraph()->isolate()->builtins()->WasmStackGuard();
CallInterfaceDescriptor idesc =
WasmStackGuardDescriptor(jsgraph()->isolate());
WasmRuntimeCallDescriptor(jsgraph()->isolate());
CallDescriptor* desc = Linkage::GetStubCallDescriptor(
jsgraph()->isolate(), jsgraph()->zone(), idesc, 0,
CallDescriptor::kNoFlags, Operator::kNoProperties);
......
......@@ -2317,8 +2317,8 @@ void CodeGenerator::AssembleArchTrap(Instruction* instr,
void Generate() final {
X64OperandConverter i(gen_, instr_);
Runtime::FunctionId trap_id = static_cast<Runtime::FunctionId>(
i.InputInt32(instr_->InputCount() - 1));
Builtins::Name trap_id =
static_cast<Builtins::Name>(i.InputInt32(instr_->InputCount() - 1));
bool old_has_frame = __ has_frame();
if (frame_elided_) {
__ set_has_frame(true);
......@@ -2331,8 +2331,8 @@ void CodeGenerator::AssembleArchTrap(Instruction* instr,
}
private:
void GenerateCallToTrap(Runtime::FunctionId trap_id) {
if (trap_id == Runtime::kNumFunctions) {
void GenerateCallToTrap(Builtins::Name trap_id) {
if (trap_id == Builtins::builtin_count) {
// We cannot test calls to the runtime in cctest/test-run-wasm.
// Therefore we emit a call to C here instead of a call to the runtime.
__ PrepareCallCFunction(0);
......@@ -2342,9 +2342,9 @@ void CodeGenerator::AssembleArchTrap(Instruction* instr,
__ LeaveFrame(StackFrame::WASM_COMPILED);
__ Ret();
} else {
__ Move(rsi, Smi::kZero);
gen_->AssembleSourcePosition(instr_);
__ CallRuntime(trap_id);
__ Call(handle(isolate()->builtins()->builtin(trap_id), isolate()),
RelocInfo::CODE_TARGET);
ReferenceMap* reference_map =
new (gen_->zone()) ReferenceMap(gen_->zone());
gen_->RecordSafepoint(reference_map, Safepoint::kSimple, 0,
......
......@@ -96,7 +96,7 @@ class PlatformInterfaceDescriptor;
V(ResumeGenerator) \
V(FrameDropperTrampoline) \
V(PromiseHandleReject) \
V(WasmStackGuard)
V(WasmRuntimeCall)
class V8_EXPORT_PRIVATE CallInterfaceDescriptorData {
public:
......@@ -953,9 +953,9 @@ class PromiseHandleRejectDescriptor final : public CallInterfaceDescriptor {
CallInterfaceDescriptor, kParameterCount)
};
class WasmStackGuardDescriptor final : public CallInterfaceDescriptor {
class WasmRuntimeCallDescriptor final : public CallInterfaceDescriptor {
public:
DECLARE_DEFAULT_DESCRIPTOR(WasmStackGuardDescriptor, CallInterfaceDescriptor,
DECLARE_DEFAULT_DESCRIPTOR(WasmRuntimeCallDescriptor, CallInterfaceDescriptor,
0)
};
......
......@@ -117,6 +117,12 @@ Object* ThrowRuntimeError(Isolate* isolate, int message_id, int byte_offset,
return isolate->Throw(*error_obj);
}
RUNTIME_FUNCTION(Runtime_ThrowWasmErrorFromTrapIf) {
DCHECK_EQ(1, args.length());
CONVERT_SMI_ARG_CHECKED(message_id, 0);
return ThrowRuntimeError(isolate, message_id, 0, false);
}
RUNTIME_FUNCTION(Runtime_ThrowWasmError) {
DCHECK_EQ(2, args.length());
CONVERT_SMI_ARG_CHECKED(message_id, 0);
......@@ -124,14 +130,6 @@ RUNTIME_FUNCTION(Runtime_ThrowWasmError) {
return ThrowRuntimeError(isolate, message_id, byte_offset, true);
}
#define DECLARE_ENUM(name) \
RUNTIME_FUNCTION(Runtime_ThrowWasm##name) { \
int message_id = wasm::WasmOpcodes::TrapReasonToMessageId(wasm::k##name); \
return ThrowRuntimeError(isolate, message_id, 0, false); \
}
FOREACH_WASM_TRAPREASON(DECLARE_ENUM)
#undef DECLARE_ENUM
RUNTIME_FUNCTION(Runtime_WasmThrowTypeError) {
HandleScope scope(isolate);
DCHECK_EQ(0, args.length());
......
......@@ -620,22 +620,15 @@ namespace internal {
F(IsSharedIntegerTypedArray, 1, 1) \
F(IsSharedInteger32TypedArray, 1, 1)
#define FOR_EACH_INTRINSIC_WASM(F) \
F(WasmGrowMemory, 1, 1) \
F(WasmMemorySize, 0, 1) \
F(ThrowWasmError, 2, 1) \
F(WasmThrowTypeError, 0, 1) \
F(WasmThrow, 2, 1) \
F(WasmGetCaughtExceptionValue, 1, 1) \
F(ThrowWasmTrapUnreachable, 0, 1) \
F(ThrowWasmTrapMemOutOfBounds, 0, 1) \
F(ThrowWasmTrapDivByZero, 0, 1) \
F(ThrowWasmTrapDivUnrepresentable, 0, 1) \
F(ThrowWasmTrapRemByZero, 0, 1) \
F(ThrowWasmTrapFloatUnrepresentable, 0, 1) \
F(ThrowWasmTrapFuncInvalid, 0, 1) \
F(ThrowWasmTrapFuncSigMismatch, 0, 1) \
F(WasmRunInterpreter, 3, 1) \
#define FOR_EACH_INTRINSIC_WASM(F) \
F(WasmGrowMemory, 1, 1) \
F(WasmMemorySize, 0, 1) \
F(ThrowWasmError, 2, 1) \
F(ThrowWasmErrorFromTrapIf, 1, 1) \
F(WasmThrowTypeError, 0, 1) \
F(WasmThrow, 2, 1) \
F(WasmGetCaughtExceptionValue, 1, 1) \
F(WasmRunInterpreter, 3, 1) \
F(WasmStackGuard, 0, 1)
#define FOR_EACH_INTRINSIC_RETURN_PAIR(F) \
......
......@@ -43,6 +43,7 @@ void CheckExceptionInfos(Handle<Object> exc,
// Check that it's indeed an Error object.
CHECK(exc->IsJSError());
exc->Print();
// Extract stack frame from the exception.
Local<v8::Value> localExc = Utils::ToLocal(exc);
v8::Local<v8::StackTrace> stack = v8::Exception::GetStackTrace(localExc);
......
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