Commit 7ed565b6 authored by jpp's avatar jpp Committed by Commit bot

[WASM] Implements catch for the wasm low level exception mechanism.

This is essentially CL/2275293002, with the difference that the effect
dependencies are now updated correctly.

BUG=

Review-Url: https://codereview.chromium.org/2378773013
Cr-Commit-Position: refs/heads/master@{#39919}
parent 80d40064
......@@ -1089,6 +1089,7 @@ void CEntryStub::Generate(MacroAssembler* masm) {
__ Ldr(cp, MemOperand(cp));
__ Mov(jssp, Operand(pending_handler_sp_address));
__ Ldr(jssp, MemOperand(jssp));
__ Mov(csp, jssp);
__ Mov(fp, Operand(pending_handler_fp_address));
__ Ldr(fp, MemOperand(fp));
......
......@@ -1707,13 +1707,17 @@ void InstructionSelector::VisitParameter(Node* node) {
Emit(kArchNop, op);
}
namespace {
LinkageLocation ExceptionLocation() {
return LinkageLocation::ForRegister(kReturnRegister0.code(),
MachineType::IntPtr());
}
}
void InstructionSelector::VisitIfException(Node* node) {
OperandGenerator g(this);
Node* call = node->InputAt(1);
DCHECK_EQ(IrOpcode::kCall, call->opcode());
const CallDescriptor* descriptor = CallDescriptorOf(call->op());
Emit(kArchNop, g.DefineAsLocation(node, descriptor->GetReturnLocation(0)));
DCHECK_EQ(IrOpcode::kCall, node->InputAt(1)->opcode());
Emit(kArchNop, g.DefineAsLocation(node, ExceptionLocation()));
}
......
......@@ -337,6 +337,19 @@ bool WasmGraphBuilder::IsPhiWithMerge(Node* phi, Node* merge) {
NodeProperties::GetControlInput(phi) == merge;
}
bool WasmGraphBuilder::ThrowsException(Node* node, Node** if_success,
Node** if_exception) {
if (node->op()->HasProperty(compiler::Operator::kNoThrow)) {
return false;
}
*if_success = graph()->NewNode(jsgraph()->common()->IfSuccess(), node);
*if_exception =
graph()->NewNode(jsgraph()->common()->IfException(), node, node);
return true;
}
void WasmGraphBuilder::AppendToMerge(Node* merge, Node* from) {
DCHECK(IrOpcode::IsMergeOpcode(merge->opcode()));
merge->AppendInput(jsgraph()->zone(), from);
......@@ -1724,6 +1737,50 @@ Node* WasmGraphBuilder::Throw(Node* input) {
arraysize(parameters), effect_, *control_);
}
Node* WasmGraphBuilder::Catch(Node* input, wasm::WasmCodePosition position) {
CommonOperatorBuilder* common = jsgraph()->common();
Node* parameters[] = {input}; // caught value
Node* value =
BuildCallToRuntime(Runtime::kWasmGetCaughtExceptionValue, jsgraph(),
module_->instance->context, parameters,
arraysize(parameters), effect_, *control_);
Node* is_smi;
Node* is_heap;
Branch(BuildTestNotSmi(value), &is_heap, &is_smi);
// is_smi
Node* smi_i32 = BuildChangeSmiToInt32(value);
Node* is_smi_effect = *effect_;
// is_heap
*control_ = is_heap;
Node* heap_f64 = BuildLoadHeapNumberValue(value, is_heap);
// *control_ needs to point to the current control dependency (is_heap) in
// case BuildI32SConvertF64 needs to insert nodes that depend on the "current"
// control node.
Node* heap_i32 = BuildI32SConvertF64(heap_f64, position);
// *control_ contains the control node that should be used when merging the
// result for the catch clause. It may be different than *control_ because
// BuildI32SConvertF64 may introduce a new control node (used for trapping if
// heap_f64 cannot be converted to an i32.
is_heap = *control_;
Node* is_heap_effect = *effect_;
Node* merge = graph()->NewNode(common->Merge(2), is_heap, is_smi);
Node* effect_merge = graph()->NewNode(common->EffectPhi(2), is_heap_effect,
is_smi_effect, merge);
Node* value_i32 = graph()->NewNode(
common->Phi(MachineRepresentation::kWord32, 2), heap_i32, smi_i32, merge);
*control_ = merge;
*effect_ = effect_merge;
return value_i32;
}
Node* WasmGraphBuilder::BuildI32DivS(Node* left, Node* right,
wasm::WasmCodePosition position) {
MachineOperatorBuilder* m = jsgraph()->machine();
......@@ -1992,8 +2049,9 @@ Node* WasmGraphBuilder::BuildCCall(MachineSignature* sig, Node** args) {
return call;
}
Node** WasmGraphBuilder::BuildWasmCall(wasm::FunctionSig* sig, Node** args,
wasm::WasmCodePosition position) {
Node* WasmGraphBuilder::BuildWasmCall(wasm::FunctionSig* sig, Node** args,
Node*** rets,
wasm::WasmCodePosition position) {
const size_t params = sig->parameter_count();
const size_t extra = 2; // effect and control inputs.
const size_t count = 1 + params + extra;
......@@ -2013,24 +2071,24 @@ Node** WasmGraphBuilder::BuildWasmCall(wasm::FunctionSig* sig, Node** args,
*effect_ = call;
size_t ret_count = sig->return_count();
if (ret_count == 0) return nullptr; // No return value.
if (ret_count == 0) return call; // No return value.
Node** rets = Buffer(ret_count);
*rets = Buffer(ret_count);
if (ret_count == 1) {
// Only a single return value.
rets[0] = call;
(*rets)[0] = call;
} else {
// Create projections for all return values.
for (size_t i = 0; i < ret_count; i++) {
rets[i] = graph()->NewNode(jsgraph()->common()->Projection(i), call,
graph()->start());
(*rets)[i] = graph()->NewNode(jsgraph()->common()->Projection(i), call,
graph()->start());
}
}
return rets;
return call;
}
Node** WasmGraphBuilder::CallDirect(uint32_t index, Node** args,
wasm::WasmCodePosition position) {
Node* WasmGraphBuilder::CallDirect(uint32_t index, Node** args, Node*** rets,
wasm::WasmCodePosition position) {
DCHECK_NULL(args[0]);
// Add code object as constant.
......@@ -2039,11 +2097,11 @@ Node** WasmGraphBuilder::CallDirect(uint32_t index, Node** args,
args[0] = HeapConstant(code);
wasm::FunctionSig* sig = module_->GetFunctionSignature(index);
return BuildWasmCall(sig, args, position);
return BuildWasmCall(sig, args, rets, position);
}
Node** WasmGraphBuilder::CallIndirect(uint32_t index, Node** args,
wasm::WasmCodePosition position) {
Node* WasmGraphBuilder::CallIndirect(uint32_t index, Node** args, Node*** rets,
wasm::WasmCodePosition position) {
DCHECK_NOT_NULL(args[0]);
DCHECK(module_ && module_->instance);
......@@ -2066,11 +2124,11 @@ Node** WasmGraphBuilder::CallIndirect(uint32_t index, Node** args,
} else {
// No function table. Generate a trap and return a constant.
trap_->AddTrapIfFalse(wasm::kTrapFuncInvalid, Int32Constant(0), position);
Node** rets = Buffer(sig->return_count());
(*rets) = Buffer(sig->return_count());
for (size_t i = 0; i < sig->return_count(); i++) {
rets[i] = trap_->GetTrapValue(sig->GetReturn(i));
(*rets)[i] = trap_->GetTrapValue(sig->GetReturn(i));
}
return rets;
return trap_->GetTrapValue(sig);
}
Node* table = FunctionTable(0);
......@@ -2104,7 +2162,7 @@ Node** WasmGraphBuilder::CallIndirect(uint32_t index, Node** args,
*effect_, *control_);
args[0] = load_code;
return BuildWasmCall(sig, args, position);
return BuildWasmCall(sig, args, rets, position);
}
Node* WasmGraphBuilder::BuildI32Rol(Node* left, Node* right) {
......
......@@ -135,8 +135,10 @@ class WasmGraphBuilder {
wasm::WasmCodePosition position = wasm::kNoCodePosition);
Node* GrowMemory(Node* input);
Node* Throw(Node* input);
Node* Catch(Node* input, wasm::WasmCodePosition position);
unsigned InputCount(Node* node);
bool IsPhiWithMerge(Node* phi, Node* merge);
bool ThrowsException(Node* node, Node** if_success, Node** if_exception);
void AppendToMerge(Node* merge, Node* from);
void AppendToPhi(Node* phi, Node* from);
......@@ -153,10 +155,10 @@ class WasmGraphBuilder {
Node* ReturnVoid();
Node* Unreachable(wasm::WasmCodePosition position);
Node** CallDirect(uint32_t index, Node** args,
wasm::WasmCodePosition position);
Node** CallIndirect(uint32_t index, Node** args,
wasm::WasmCodePosition position);
Node* CallDirect(uint32_t index, Node** args, Node*** rets,
wasm::WasmCodePosition position);
Node* CallIndirect(uint32_t index, Node** args, Node*** rets,
wasm::WasmCodePosition position);
void BuildJSToWasmWrapper(Handle<Code> wasm_code, wasm::FunctionSig* sig);
void BuildWasmToJSWrapper(Handle<JSReceiver> target, wasm::FunctionSig* sig);
......@@ -240,8 +242,8 @@ class WasmGraphBuilder {
Node* MaskShiftCount64(Node* node);
Node* BuildCCall(MachineSignature* sig, Node** args);
Node** BuildWasmCall(wasm::FunctionSig* sig, Node** args,
wasm::WasmCodePosition position);
Node* BuildWasmCall(wasm::FunctionSig* sig, Node** args, Node*** rets,
wasm::WasmCodePosition position);
Node* BuildF32CopySign(Node* left, Node* right);
Node* BuildF64CopySign(Node* left, Node* right);
......
......@@ -1477,6 +1477,15 @@ Script* WasmFrame::script() const {
return wasm::WasmDebugInfo::GetFunctionScript(debug_info, function_index());
}
int WasmFrame::LookupExceptionHandlerInTable(int* stack_slots) {
DCHECK_NOT_NULL(stack_slots);
Code* code = LookupCode();
HandlerTable* table = HandlerTable::cast(code->handler_table());
int pc_offset = static_cast<int>(pc() - code->entry());
*stack_slots = code->stack_slots();
return table->LookupReturn(pc_offset);
}
namespace {
......
......@@ -1056,6 +1056,10 @@ class WasmFrame : public StandardFrame {
void Print(StringStream* accumulator, PrintMode mode,
int index) const override;
// Lookup exception handler for current {pc}, returns -1 if none found. Also
// returns the stack slot count of the entire frame.
int LookupExceptionHandlerInTable(int* data);
// Determine the code for the frame.
Code* unchecked_code() const override;
......
......@@ -76,6 +76,11 @@ bool Isolate::is_catchable_by_javascript(Object* exception) {
return exception != heap()->termination_exception();
}
bool Isolate::is_catchable_by_wasm(Object* exception) {
return is_catchable_by_javascript(exception) &&
(exception->IsNumber() || exception->IsSmi());
}
void Isolate::FireBeforeCallEnteredCallback() {
for (int i = 0; i < before_call_entered_callbacks_.length(); i++) {
before_call_entered_callbacks_.at(i)(reinterpret_cast<v8::Isolate*>(this));
......
......@@ -1149,8 +1149,8 @@ Object* Isolate::UnwindAndFindHandler() {
Address handler_sp = nullptr;
Address handler_fp = nullptr;
// Special handling of termination exceptions, uncatchable by JavaScript code,
// we unwind the handlers until the top ENTRY handler is found.
// Special handling of termination exceptions, uncatchable by JavaScript and
// Wasm code, we unwind the handlers until the top ENTRY handler is found.
bool catchable_by_js = is_catchable_by_javascript(exception);
// Compute handler and stack unwinding information by performing a full walk
......@@ -1172,6 +1172,28 @@ Object* Isolate::UnwindAndFindHandler() {
break;
}
if (FLAG_wasm_eh_prototype) {
if (frame->is_wasm() && is_catchable_by_wasm(exception)) {
int stack_slots = 0; // Will contain stack slot count of frame.
WasmFrame* wasm_frame = static_cast<WasmFrame*>(frame);
offset = wasm_frame->LookupExceptionHandlerInTable(&stack_slots);
if (offset >= 0) {
// Compute the stack pointer from the frame pointer. This ensures that
// argument slots on the stack are dropped as returning would.
Address return_sp = frame->fp() +
StandardFrameConstants::kFixedFrameSizeAboveFp -
stack_slots * kPointerSize;
// Gather information from the frame.
code = frame->LookupCode();
handler_sp = return_sp;
handler_fp = frame->fp();
break;
}
}
}
// For optimized frames we perform a lookup in the handler table.
if (frame->is_optimized() && catchable_by_js) {
OptimizedFrame* js_frame = static_cast<OptimizedFrame*>(frame);
......
......@@ -621,6 +621,7 @@ class Isolate {
bool IsExternalHandlerOnTop(Object* exception);
inline bool is_catchable_by_javascript(Object* exception);
inline bool is_catchable_by_wasm(Object* exception);
// JS execution stack (see frames.h).
static Address c_entry_fp(ThreadLocalTop* thread) {
......
......@@ -129,5 +129,16 @@ RUNTIME_FUNCTION(Runtime_WasmThrow) {
return isolate->Throw(*isolate->factory()->NewNumberFromInt(thrown_value));
}
RUNTIME_FUNCTION(Runtime_WasmGetCaughtExceptionValue) {
HandleScope scope(isolate);
DCHECK_EQ(1, args.length());
Object* exception = args[0];
// The unwinder will only deliver exceptions to wasm if the exception is a
// Number or a Smi (which we have just converted to a Number.) This logic
// lives in Isolate::is_catchable_by_wasm(Object*).
CHECK(exception->IsNumber());
return exception;
}
} // namespace internal
} // namespace v8
......@@ -918,7 +918,8 @@ namespace internal {
#define FOR_EACH_INTRINSIC_WASM(F) \
F(WasmGrowMemory, 1, 1) \
F(WasmThrowTypeError, 0, 1) \
F(WasmThrow, 2, 1)
F(WasmThrow, 2, 1) \
F(WasmGetCaughtExceptionValue, 1, 1)
#define FOR_EACH_INTRINSIC_RETURN_PAIR(F) \
F(LoadLookupSlotForCall, 1, 2)
......
This diff is collapsed.
This diff is collapsed.
......@@ -144,6 +144,8 @@ var kExprBrIf = 0x07;
var kExprBrTable = 0x08;
var kExprReturn = 0x09;
var kExprThrow = 0xfa;
var kExprTry = 0xfb;
var kExprCatch = 0xfe;
var kExprEnd = 0x0f;
var kExprTeeLocal = 0x19;
var kExprDrop = 0x0b;
......
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