Commit 7b5a4022 authored by Karl Schimpf's avatar Karl Schimpf Committed by Commit Bot

Add capability of throwing values in WASM

Extends the current implementation of WASM exceptions to be able to
throw exceptions with values (not just tags).

An JS typed array (uint_16) is used to hold thrown values, so that the
thrown values can be inspected in JS.

Bug: v8:6577
Change-Id: I1007e79ceaffd64386b62562919cfbb920fc10c5
Reviewed-on: https://chromium-review.googlesource.com/633866
Commit-Queue: Karl Schimpf <kschimpf@chromium.org>
Reviewed-by: 's avatarClemens Hammacher <clemensh@chromium.org>
Reviewed-by: 's avatarEric Holk <eholk@chromium.org>
Cr-Commit-Position: refs/heads/master@{#48001}
parent 2eb17ccc
......@@ -55,6 +55,8 @@ namespace compiler {
namespace {
constexpr uint32_t kBytesPerExceptionValuesArrayElement = 2;
void MergeControlToEnd(JSGraph* jsgraph, Node* node) {
Graph* g = jsgraph->graph();
if (g->end()) {
......@@ -1808,11 +1810,97 @@ Node* WasmGraphBuilder::GrowMemory(Node* input) {
return result;
}
Node* WasmGraphBuilder::Throw(Node* input) {
uint32_t WasmGraphBuilder::GetExceptionEncodedSize(
const wasm::WasmException* exception) const {
const wasm::WasmExceptionSig* sig = exception->sig;
uint32_t encoded_size = 0;
for (size_t i = 0; i < sig->parameter_count(); ++i) {
size_t byte_size = size_t(1) << ElementSizeLog2Of(sig->GetParam(i));
DCHECK_EQ(byte_size % kBytesPerExceptionValuesArrayElement, 0);
DCHECK_LE(1, byte_size / kBytesPerExceptionValuesArrayElement);
encoded_size += byte_size / kBytesPerExceptionValuesArrayElement;
}
return encoded_size;
}
Node* WasmGraphBuilder::Throw(uint32_t tag,
const wasm::WasmException* exception,
const Vector<Node*> values) {
SetNeedsStackCheck();
Node* parameters[] = {BuildChangeInt32ToSmi(input)};
return BuildCallToRuntime(Runtime::kWasmThrow, parameters,
arraysize(parameters));
uint32_t encoded_size = GetExceptionEncodedSize(exception);
Node* create_parameters[] = {
BuildChangeUint32ToSmi(ConvertExceptionTagToRuntimeId(tag)),
BuildChangeUint32ToSmi(Uint32Constant(encoded_size))};
Node* except =
BuildCallToRuntime(Runtime::kWasmThrowCreate, create_parameters,
arraysize(create_parameters));
uint32_t index = 0;
const wasm::WasmExceptionSig* sig = exception->sig;
MachineOperatorBuilder* m = jsgraph()->machine();
for (size_t i = 0; i < sig->parameter_count(); ++i) {
Node* value = values[i];
switch (sig->GetParam(i)) {
case wasm::kWasmF32:
value = graph()->NewNode(m->BitcastFloat32ToInt32(), value);
// Intentionally fall to next case.
case wasm::kWasmI32:
except = BuildEncodeException32BitValue(except, &index, value);
break;
case wasm::kWasmF64:
value = graph()->NewNode(m->BitcastFloat64ToInt64(), value);
// Intentionally fall to next case.
case wasm::kWasmI64: {
Node* upper32 = graph()->NewNode(
m->TruncateInt64ToInt32(),
Binop(wasm::kExprI64ShrU, value, Int64Constant(32)));
except = BuildEncodeException32BitValue(except, &index, upper32);
Node* lower32 = graph()->NewNode(m->TruncateInt64ToInt32(), value);
except = BuildEncodeException32BitValue(except, &index, lower32);
break;
}
default:
CHECK(false);
break;
}
}
DCHECK_EQ(encoded_size, index);
Node* throw_parameters[] = {except};
return BuildCallToRuntime(Runtime::kWasmThrow, throw_parameters,
arraysize(throw_parameters));
}
Node* WasmGraphBuilder::BuildEncodeException32BitValue(Node* except,
uint32_t* index,
Node* value) {
MachineOperatorBuilder* machine = jsgraph()->machine();
Node* upper_parameters[] = {
except, BuildChangeUint32ToSmi(Int32Constant(*index)),
BuildChangeUint32ToSmi(
graph()->NewNode(machine->Word32Shr(), value, Int32Constant(16))),
};
(*index)++;
except = BuildCallToRuntime(Runtime::kWasmExceptionSetElement,
upper_parameters, arraysize(upper_parameters));
Node* lower_parameters[] = {
except, BuildChangeUint32ToSmi(Int32Constant(*index)),
BuildChangeUint32ToSmi(graph()->NewNode(machine->Word32And(), value,
Int32Constant(0xFFFFu))),
};
(*index)++;
return BuildCallToRuntime(Runtime::kWasmExceptionSetElement, lower_parameters,
arraysize(lower_parameters));
}
Node* WasmGraphBuilder::BuildDecodeException32BitValue(Node* const* values,
uint32_t* index) {
MachineOperatorBuilder* machine = jsgraph()->machine();
Node* upper = BuildChangeSmiToInt32(values[*index]);
(*index)++;
upper = graph()->NewNode(machine->Word32Shl(), upper, Int32Constant(16));
Node* lower = BuildChangeSmiToInt32(values[*index]);
(*index)++;
Node* value = graph()->NewNode(machine->Word32Or(), upper, lower);
return value;
}
Node* WasmGraphBuilder::Rethrow() {
......@@ -1821,15 +1909,80 @@ Node* WasmGraphBuilder::Rethrow() {
return result;
}
Node* WasmGraphBuilder::Catch(Node* input, wasm::WasmCodePosition position) {
Node* WasmGraphBuilder::Catch(Node* input) {
SetNeedsStackCheck();
Node* parameters[] = {input}; // caught value
Node* value = BuildCallToRuntime(Runtime::kWasmSetCaughtExceptionValue,
Node* parameters[] = {input};
return BuildCallToRuntime(Runtime::kWasmSetCaughtExceptionValue, parameters,
arraysize(parameters));
}
Node* WasmGraphBuilder::ConvertExceptionTagToRuntimeId(uint32_t tag) {
// TODO(kschimpf): Handle exceptions from different modules, when they are
// linked at runtime.
return Uint32Constant(tag);
}
Node* WasmGraphBuilder::GetExceptionRuntimeId(Node* exception) {
SetNeedsStackCheck();
Node* parameters[] = {exception};
return BuildChangeSmiToInt32(BuildCallToRuntime(
Runtime::kWasmGetExceptionRuntimeId, parameters, arraysize(parameters)));
}
Node** WasmGraphBuilder::GetExceptionValues(
const wasm::WasmException* except_decl, Node* exception,
wasm::WasmCodePosition position) {
// TODO(kschimpf): We need to move this code to the function-body-decoder.cc
// in order to build landing-pad (exception) edges in case the runtime
// call causes an exception.
// Start by getting the encoded values from the exception.
Node* parameters[] = {exception};
Node* enc_values = BuildCallToRuntime(Runtime::kWasmGetExceptionValuesArray,
parameters, arraysize(parameters));
uint32_t encoded_size = GetExceptionEncodedSize(except_decl);
Node** values = Buffer(encoded_size);
for (uint32_t i = 0; i < encoded_size; ++i) {
Node* parameters[] = {enc_values,
BuildChangeUint32ToSmi(Uint32Constant(i))};
values[i] = BuildCallToRuntime(Runtime::kWasmExceptionGetElement,
parameters, arraysize(parameters));
parameters[0] = value;
value = BuildCallToRuntime(Runtime::kWasmGetExceptionTag, parameters,
arraysize(parameters));
return BuildChangeSmiToInt32(value);
}
// Now convert the leading entries to the corresponding parameter values.
uint32_t index = 0;
const wasm::WasmExceptionSig* sig = except_decl->sig;
for (size_t i = 0; i < sig->parameter_count(); ++i) {
Node* value = BuildDecodeException32BitValue(values, &index);
switch (wasm::ValueType type = sig->GetParam(i)) {
case wasm::kWasmF32: {
value = Unop(wasm::kExprF32ReinterpretI32, value);
break;
}
case wasm::kWasmI32:
break;
case wasm::kWasmF64:
case wasm::kWasmI64: {
Node* upper = Binop(wasm::kExprI64Shl,
Unop(wasm::kExprI64UConvertI32, value, position),
Int64Constant(32), position);
Node* lower =
Unop(wasm::kExprI64UConvertI32,
BuildDecodeException32BitValue(values, &index), position);
value = Binop(wasm::kExprI64Ior, upper, lower);
if (type == wasm::kWasmF64) {
value = Unop(wasm::kExprF64ReinterpretI64, value);
}
break;
}
default:
CHECK(false);
break;
}
values[i] = value;
}
DCHECK_EQ(index, encoded_size);
return values;
}
Node* WasmGraphBuilder::BuildI32DivS(Node* left, Node* right,
......@@ -3103,7 +3256,7 @@ Node* WasmGraphBuilder::BuildCallToRuntimeWithContext(Runtime::FunctionId f,
inputs[count++] = jsgraph()->ExternalConstant(
ExternalReference(f, jsgraph()->isolate())); // ref
inputs[count++] = jsgraph()->Int32Constant(fun->nargs); // arity
inputs[count++] = context; // context
inputs[count++] = context;
inputs[count++] = *effect_;
inputs[count++] = *control_;
......
......@@ -212,9 +212,15 @@ class WasmGraphBuilder {
Node* Unop(wasm::WasmOpcode opcode, Node* input,
wasm::WasmCodePosition position = wasm::kNoCodePosition);
Node* GrowMemory(Node* input);
Node* Throw(Node* input);
Node* Throw(uint32_t tag, const wasm::WasmException* exception,
const Vector<Node*> values);
Node* Rethrow();
Node* Catch(Node* input, wasm::WasmCodePosition position);
Node* Catch(Node* input);
Node* ConvertExceptionTagToRuntimeId(uint32_t tag);
Node* GetExceptionRuntimeId(Node* exception);
Node** GetExceptionValues(
const wasm::WasmException* except_decl, Node* exception,
wasm::WasmCodePosition position = wasm::kNoCodePosition);
unsigned InputCount(Node* node);
bool IsPhiWithMerge(Node* phi, Node* merge);
bool ThrowsException(Node* node, Node** if_success, Node** if_exception);
......@@ -469,6 +475,11 @@ class WasmGraphBuilder {
Node* BuildAsmjsLoadMem(MachineType type, Node* index);
Node* BuildAsmjsStoreMem(MachineType type, Node* index, Node* val);
uint32_t GetExceptionEncodedSize(const wasm::WasmException* exception) const;
Node* BuildEncodeException32BitValue(Node* except, uint32_t* index,
Node* value);
Node* BuildDecodeException32BitValue(Node* const* values, uint32_t* index);
Node** Realloc(Node** buffer, size_t old_count, size_t new_count) {
Node** buf = Buffer(new_count);
if (buf != buffer) memcpy(buf, buffer, old_count * sizeof(Node*));
......
......@@ -202,8 +202,7 @@
V(will_handle_string, "willHandle") \
V(writable_string, "writable") \
V(year_string, "year") \
V(zero_string, "0") \
V(WasmExceptionTag_string, "WasmExceptionTag")
V(zero_string, "0")
#define PRIVATE_SYMBOL_LIST(V) \
V(array_iteration_kind_symbol) \
......
......@@ -92,8 +92,9 @@ bool Isolate::is_catchable_by_wasm(Object* exception) {
return false;
HandleScope scope(this);
Handle<Object> exception_handle(exception, this);
return JSReceiver::HasProperty(Handle<JSReceiver>::cast(exception_handle),
factory()->WasmExceptionTag_string())
return JSReceiver::HasProperty(
Handle<JSReceiver>::cast(exception_handle),
factory()->InternalizeUtf8String("WasmExceptionRuntimeId"))
.IsJust();
}
......
......@@ -26,6 +26,9 @@ namespace {
constexpr int kInvalidExceptionTag = -1;
constexpr const char* WasmExceptionRuntimeIdStr = "WasmExceptionRuntimeId";
constexpr const char* WasmExceptionValuesStr = "WasmExceptionValues";
WasmInstanceObject* GetWasmInstanceOnStackTop(Isolate* isolate) {
DisallowHeapAllocation no_allocation;
const Address entry = Isolate::c_entry_fp(isolate->thread_local_top());
......@@ -144,55 +147,134 @@ RUNTIME_FUNCTION(Runtime_WasmThrowTypeError) {
isolate, NewTypeError(MessageTemplate::kWasmTrapTypeError));
}
RUNTIME_FUNCTION(Runtime_WasmThrow) {
RUNTIME_FUNCTION(Runtime_WasmThrowCreate) {
HandleScope scope(isolate);
DCHECK_NULL(isolate->context());
isolate->set_context(GetWasmContextOnStackTop(isolate));
DCHECK_EQ(1, args.length());
Handle<Object> tag = args.at(0);
Handle<Object> except = isolate->factory()->NewWasmRuntimeError(
DCHECK_EQ(2, args.length());
Handle<Object> exception = isolate->factory()->NewWasmRuntimeError(
static_cast<MessageTemplate::Template>(
MessageTemplate::kWasmExceptionError));
DCHECK(tag->IsSmi());
CONVERT_ARG_HANDLE_CHECKED(Smi, id, 0);
CHECK(!JSReceiver::SetProperty(exception,
isolate->factory()->InternalizeUtf8String(
WasmExceptionRuntimeIdStr),
id, STRICT)
.is_null());
CONVERT_SMI_ARG_CHECKED(size, 1);
Handle<JSTypedArray> values =
isolate->factory()->NewJSTypedArray(ElementsKind::UINT16_ELEMENTS, size);
CHECK(!JSReceiver::SetProperty(
except, isolate->factory()->WasmExceptionTag_string(), tag, STRICT)
exception,
isolate->factory()->InternalizeUtf8String(WasmExceptionValuesStr),
values, STRICT)
.is_null());
return isolate->Throw(*except);
return *exception;
}
RUNTIME_FUNCTION(Runtime_WasmThrow) {
HandleScope scope(isolate);
DCHECK_NULL(isolate->context());
isolate->set_context(GetWasmContextOnStackTop(isolate));
DCHECK_EQ(1, args.length());
CONVERT_ARG_HANDLE_CHECKED(JSObject, exception, 0);
return isolate->Throw(*exception);
}
RUNTIME_FUNCTION(Runtime_WasmRethrow) {
HandleScope scope(isolate);
DCHECK_NULL(isolate->context());
isolate->set_context(GetWasmContextOnStackTop(isolate));
DCHECK_EQ(0, args.length());
Object* exception = isolate->get_wasm_caught_exception();
Handle<Object> exception(isolate->get_wasm_caught_exception(), isolate);
isolate->clear_wasm_caught_exception();
return isolate->Throw(exception);
return isolate->Throw(*exception);
}
RUNTIME_FUNCTION(Runtime_WasmGetExceptionTag) {
RUNTIME_FUNCTION(Runtime_WasmGetExceptionRuntimeId) {
HandleScope scope(isolate);
DCHECK_NULL(isolate->context());
isolate->set_context(GetWasmContextOnStackTop(isolate));
DCHECK_EQ(1, args.length());
Object* exception = args[0];
DCHECK(isolate->is_catchable_by_wasm(exception));
Handle<Object> exception_handle(exception, isolate);
Handle<Object> tag_handle;
if (JSReceiver::GetProperty(Handle<JSReceiver>::cast(exception_handle),
isolate->factory()->WasmExceptionTag_string())
.ToHandle(&tag_handle)) {
if (tag_handle->IsSmi()) return *tag_handle;
CONVERT_ARG_HANDLE_CHECKED(JSObject, exception, 0);
DCHECK(isolate->is_catchable_by_wasm(*exception));
Handle<Object> tag;
if (JSReceiver::GetProperty(
exception,
isolate->factory()->InternalizeUtf8String(WasmExceptionRuntimeIdStr))
.ToHandle(&tag)) {
if (tag->IsSmi()) {
return *tag;
}
}
return Smi::FromInt(kInvalidExceptionTag);
}
RUNTIME_FUNCTION(Runtime_WasmGetExceptionValuesArray) {
HandleScope scope(isolate);
DCHECK_NULL(isolate->context());
isolate->set_context(GetWasmContextOnStackTop(isolate));
DCHECK_EQ(1, args.length());
CONVERT_ARG_HANDLE_CHECKED(JSObject, exception, 0);
Handle<Object> values;
if (JSReceiver::GetProperty(
exception,
isolate->factory()->InternalizeUtf8String(WasmExceptionValuesStr))
.ToHandle(&values)) {
return *values;
}
return *isolate->factory()->undefined_value();
}
RUNTIME_FUNCTION(Runtime_WasmExceptionGetElement) {
// TODO(kschimpf): Can this be replaced with equivalent TurboFan code/calls.
HandleScope scope(isolate);
DCHECK_NULL(isolate->context());
isolate->set_context(GetWasmContextOnStackTop(isolate));
DCHECK_EQ(2, args.length());
CONVERT_ARG_CHECKED(JSTypedArray, values, 0);
CONVERT_SMI_ARG_CHECKED(index, 1);
CHECK_LT(index, Smi::ToInt(values->length()));
CHECK_EQ(values->type(), kExternalUint16Array);
auto* vals =
reinterpret_cast<uint16_t*>(values->GetBuffer()->allocation_base());
return Smi::FromInt(vals[index]);
}
RUNTIME_FUNCTION(Runtime_WasmExceptionSetElement) {
// TODO(kschimpf): Can this be replaced with equivalent TurboFan code/calls.
HandleScope scope(isolate);
DCHECK_EQ(3, args.length());
DCHECK_NULL(isolate->context());
isolate->set_context(GetWasmContextOnStackTop(isolate));
CONVERT_ARG_HANDLE_CHECKED(JSObject, exception, 0);
CONVERT_SMI_ARG_CHECKED(index, 1);
CONVERT_SMI_ARG_CHECKED(value, 2);
Handle<Object> values_obj;
CHECK(JSReceiver::GetProperty(
exception,
isolate->factory()->InternalizeUtf8String(WasmExceptionValuesStr))
.ToHandle(&values_obj));
Handle<JSTypedArray> values = Handle<JSTypedArray>::cast(values_obj);
CHECK_LT(index, Smi::ToInt(values->length()));
CHECK_EQ(values->type(), kExternalUint16Array);
auto* vals =
reinterpret_cast<uint16_t*>(values->GetBuffer()->allocation_base());
vals[index] = static_cast<uint16_t>(value);
return *exception;
}
RUNTIME_FUNCTION(Runtime_WasmSetCaughtExceptionValue) {
// TODO(kschimpf): Implement stack of caught exceptions, rather than
// just innermost.
HandleScope scope(isolate);
DCHECK_NULL(isolate->context());
isolate->set_context(GetWasmContextOnStackTop(isolate));
DCHECK_EQ(1, args.length());
Object* exception = args[0];
DCHECK(isolate->is_catchable_by_wasm(exception));
isolate->set_wasm_caught_exception(exception);
return exception;
CONVERT_ARG_HANDLE_CHECKED(Object, exception, 0);
DCHECK(isolate->is_catchable_by_wasm(*exception));
isolate->set_wasm_caught_exception(*exception);
return *exception;
}
RUNTIME_FUNCTION(Runtime_SetThreadInWasm) {
......
......@@ -644,10 +644,14 @@ namespace internal {
F(ThrowWasmErrorFromTrapIf, 1, 1) \
F(ThrowWasmStackOverflow, 0, 1) \
F(WasmThrowTypeError, 0, 1) \
F(WasmThrowCreate, 2, 1) \
F(WasmThrow, 1, 1) \
F(WasmRethrow, 0, 1) \
F(WasmGetExceptionTag, 1, 1) \
F(WasmGetExceptionRuntimeId, 1, 1) \
F(WasmGetExceptionValuesArray, 1, 1) \
F(WasmSetCaughtExceptionValue, 1, 1) \
F(WasmExceptionSetElement, 3, 1) \
F(WasmExceptionGetElement, 2, 1) \
F(WasmRunInterpreter, 3, 1) \
F(WasmStackGuard, 0, 1) \
F(SetThreadInWasm, 0, 1) \
......
......@@ -539,8 +539,11 @@ struct ControlWithNamedConstructors : public ControlBase<Value> {
const Value& input, Value* result) \
F(Simd8x16ShuffleOp, const Simd8x16ShuffleOperand<validate>& operand, \
const Value& input0, const Value& input1, Value* result) \
F(Throw, const ExceptionIndexOperand<validate>&) \
F(Catch, const ExceptionIndexOperand<validate>& operand, Control* block) \
F(Throw, const ExceptionIndexOperand<validate>&, Control* block, \
const Vector<Value>& args) \
F(CatchException, const ExceptionIndexOperand<validate>& operand, \
Control* block) \
F(SetCaughtValue, Value* value, size_t index) \
F(AtomicOp, WasmOpcode opcode, Vector<Value> args, Value* result)
// Generic Wasm bytecode decoder with utilities for decoding operands,
......@@ -1213,16 +1216,9 @@ class WasmFullDecoder : public WasmDecoder<validate> {
ExceptionIndexOperand<true> operand(this, this->pc_);
len = 1 + operand.length;
if (!this->Validate(this->pc_, operand)) break;
if (operand.exception->sig->parameter_count() > 0) {
// TODO(kschimpf): Fix to pull values off stack and build throw.
OPCODE_ERROR(opcode, "can't handle exceptions with values yet");
break;
}
interface_.Throw(this, operand);
// TODO(titzer): Throw should end control, but currently we build a
// (reachable) runtime call instead of connecting it directly to
// end.
// EndControl();
std::vector<Value> args;
PopArgs(operand.exception->ToFunctionSig(), &args);
interface_.Throw(this, operand, &control_.back(), vec2vec(args));
break;
}
case kExprTry: {
......@@ -1260,8 +1256,12 @@ class WasmFullDecoder : public WasmDecoder<validate> {
c->kind = kControlTryCatch;
FallThruTo(c);
stack_.resize(c->stack_depth);
interface_.Catch(this, operand, c);
interface_.CatchException(this, operand, c);
const WasmExceptionSig* sig = operand.exception->sig;
for (size_t i = 0, e = sig->parameter_count(); i < e; ++i) {
auto* value = Push(sig->GetParam(i));
interface_.SetCaughtValue(this, value, i);
}
break;
}
case kExprCatchAll: {
......
......@@ -27,6 +27,11 @@ namespace wasm {
namespace {
template <typename T>
Vector<T> vec2vec(ZoneVector<T>& vec) {
return Vector<T>(vec.data(), vec.size());
}
// An SsaEnv environment carries the current local variable renaming
// as well as the current effect and control dependency in the TF graph.
// It maintains a control state that tracks whether the environment
......@@ -140,8 +145,11 @@ class WasmGraphBuildingInterface {
void Try(Decoder* decoder, Control* block) {
SsaEnv* outer_env = ssa_env_;
SsaEnv* catch_env = Split(decoder, outer_env);
// Mark catch environment as unreachable, since only accessable
// through catch unwinding (i.e. landing pads).
catch_env->state = SsaEnv::kUnreachable;
SsaEnv* try_env = Steal(decoder->zone(), outer_env);
SsaEnv* catch_env = UnreachableEnv(decoder->zone());
SetEnv(try_env);
TryInfo* try_info = new (decoder->zone()) TryInfo(catch_env);
block->end_env = outer_env;
......@@ -369,40 +377,92 @@ class WasmGraphBuildingInterface {
return BUILD(Int32Constant, operand.index);
}
void Throw(Decoder* decoder, const ExceptionIndexOperand<true>& operand) {
BUILD(Throw, GetExceptionTag(decoder, operand));
void Throw(Decoder* decoder, const ExceptionIndexOperand<true>& operand,
Control* block, const Vector<Value>& value_args) {
int count = value_args.length();
ZoneVector<TFNode*> args(count, decoder->zone());
for (int i = 0; i < count; ++i) {
args[i] = value_args[i].node;
}
BUILD(Throw, operand.index, operand.exception, vec2vec(args));
Unreachable(decoder);
EndControl(decoder, block);
}
void Catch(Decoder* decoder, const ExceptionIndexOperand<true>& operand,
Control* block) {
void CatchException(Decoder* decoder,
const ExceptionIndexOperand<true>& operand,
Control* block) {
DCHECK(block->is_try_catch());
current_catch_ = block->previous_catch;
SsaEnv* catch_env = block->try_info->catch_env;
SetEnv(catch_env);
// Get the exception and see if wanted exception.
TFNode* exception_as_i32 =
BUILD(Catch, block->try_info->exception, decoder->position());
TFNode* exception_tag = GetExceptionTag(decoder, operand);
TFNode* compare_i32 = BUILD(Binop, kExprI32Eq, exception_as_i32,
exception_tag, decoder->position());
TFNode* if_true = nullptr;
TFNode* if_false = nullptr;
BUILD(BranchNoHint, compare_i32, &if_true, &if_false);
SsaEnv* false_env = Split(decoder, catch_env);
false_env->control = if_false;
SsaEnv* true_env = Steal(decoder->zone(), catch_env);
true_env->control = if_true;
block->try_info->catch_env = false_env;
TFNode* exception = nullptr;
TFNode* compare_i32 = nullptr;
if (block->try_info->exception == nullptr) {
// Catch not applicable, no possible throws in the try
// block. Create dummy code so that body of catch still
// compiles. Note: This only happens because the current
// implementation only builds a landing pad if some node in the
// try block can (possibly) throw.
//
// TODO(kschimpf): Always generate a landing pad for a try block.
compare_i32 = exception = BUILD(Int32Constant, 0);
caught_values_ = nullptr;
} else {
// Get the exception and see if wanted exception.
exception = BUILD(Catch, block->try_info->exception);
TFNode* caught_tag = BUILD(GetExceptionRuntimeId, exception);
TFNode* exception_tag =
BUILD(ConvertExceptionTagToRuntimeId, operand.index);
compare_i32 = BUILD(Binop, kExprI32Eq, caught_tag, exception_tag);
// TODO(kschimpf): Can't use BUILD() here, GetExceptionValues() returns
// TFNode** rather than TFNode*. Fix to add landing pads.
caught_values_ =
builder_->GetExceptionValues(operand.exception, exception);
}
TFNode* if_catch = nullptr;
TFNode* if_no_catch = nullptr;
BUILD(BranchNoHint, compare_i32, &if_catch, &if_no_catch);
// Generate code to re-throw the exception.
DCHECK_NOT_NULL(block->try_info->catch_env);
SetEnv(false_env);
SsaEnv* if_no_catch_env = Split(decoder, ssa_env_);
if_no_catch_env->control = if_no_catch;
SsaEnv* if_catch_env = Steal(decoder->zone(), ssa_env_);
if_catch_env->control = if_catch;
// TODO(kschimpf): Generalize to allow more catches. Will force
// moving no_catch code to END opcode.
SetEnv(if_no_catch_env);
BUILD(Rethrow);
FallThruTo(decoder, block);
Unreachable(decoder);
EndControl(decoder, block);
SetEnv(true_env);
// TODO(kschimpf): Add code to pop caught exception from isolate.
SetEnv(if_catch_env);
}
void SetCaughtValue(Decoder* decoder, Value* value, size_t index) {
if (caught_values_) {
value->node = caught_values_[index];
return;
}
// No caught value, make up filler node so that catch block still compiles.
switch (value->type) {
case kWasmI32:
I32Const(decoder, value, 0);
break;
case kWasmI64:
I64Const(decoder, value, 0);
break;
case kWasmF32:
F32Const(decoder, value, 0.0);
break;
case kWasmF64:
F64Const(decoder, value, 0.0);
break;
default:
CHECK(false); // Should not happen.
}
}
void AtomicOp(Decoder* decoder, WasmOpcode opcode, Vector<Value> args,
......@@ -416,6 +476,7 @@ class WasmGraphBuildingInterface {
SsaEnv* ssa_env_;
TFBuilder* builder_;
uint32_t current_catch_ = kNullCatch;
TFNode** caught_values_ = nullptr;
bool build(Decoder* decoder) { return ssa_env_->go() && decoder->ok(); }
......
......@@ -118,6 +118,7 @@ typedef FunctionSig WasmExceptionSig;
struct WasmException {
explicit WasmException(const WasmExceptionSig* sig = &empty_sig_)
: sig(sig) {}
FunctionSig* ToFunctionSig() const { return const_cast<FunctionSig*>(sig); }
const WasmExceptionSig* sig; // type signature of the exception.
......
......@@ -21,8 +21,10 @@ class FlagScope {
T previous_value_;
};
#define EXPERIMENTAL_FLAG_SCOPE(flag) \
FlagScope<bool> __scope_##__LINE__(&FLAG_experimental_wasm_##flag, true)
#define FLAG_SCOPE(flag) \
FlagScope<bool> __scope_##flag##__LINE__(&FLAG_##flag, true)
#define EXPERIMENTAL_FLAG_SCOPE(flag) FLAG_SCOPE(experimental_wasm_##flag)
} // namespace internal
} // namespace v8
......
This diff is collapsed.
......@@ -124,6 +124,10 @@ let kSig_v_d = makeSig([kWasmF64], []);
let kSig_v_dd = makeSig([kWasmF64, kWasmF64], []);
let kSig_v_ddi = makeSig([kWasmF64, kWasmF64, kWasmI32], []);
let kSig_v_f = makeSig([kWasmF32], []);
let kSig_f_f = makeSig([kWasmF32], [kWasmF32]);
let kSig_d_d = makeSig([kWasmF64], [kWasmF64]);
function makeSig(params, results) {
return {params: params, results: results};
}
......@@ -388,7 +392,7 @@ function assertTraps(trap, code) {
throw new MjsUnitAssertionError('Did not trap, expected: ' + kTrapMsgs[trap]);
}
function assertWasmThrows(values, code) {
function assertWasmThrows(runtime_id, values, code) {
try {
if (typeof code === 'function') {
code();
......@@ -397,13 +401,16 @@ function assertWasmThrows(values, code) {
}
} catch (e) {
assertTrue(e instanceof WebAssembly.RuntimeError);
assertNotEquals(e['WasmExceptionTag'], undefined);
assertTrue(Number.isInteger(e['WasmExceptionTag']));
// TODO(kschimpf): Extract values from the exception.
let e_values = [];
assertEquals(values, e_values);
var e_runtime_id = e['WasmExceptionRuntimeId'];
assertEquals(e_runtime_id, runtime_id);
assertTrue(Number.isInteger(e_runtime_id));
var e_values = e['WasmExceptionValues'];
assertEquals(values.length, e_values.length);
for (i = 0; i < values.length; ++i) {
assertEquals(values[i], e_values[i]);
}
// Success.
return;
}
throw new MjsUnitAssertionError('Did not throw, expected: ' + values);
throw new MjsUnitAssertionError('Did not throw expected: ' + runtime_id + values);
}
......@@ -511,7 +511,7 @@ class WasmModuleBuilder {
for (let type of wasm.exceptions) {
section.emit_u32v(type.params.length);
for (let param of type.params) {
section.enit_u8(param);
section.emit_u8(param);
}
}
});
......
......@@ -2273,8 +2273,7 @@ TEST_F(FunctionBodyDecoderTest, Throw) {
// exception index out of range.
EXPECT_FAILURE(v_v, kExprThrow, 2);
// TODO(kschimpf): Fix when we can create exceptions with values.
EXPECT_FAILURE(v_v, WASM_I32V(0), kExprThrow, 1);
EXPECT_VERIFIES(v_v, WASM_I32V(0), kExprThrow, 1);
// TODO(kschimpf): Add more tests.
}
......
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