[V8][Wasm] Wasm throws.
This CL implements the throw wasm opcode. This is a pre-requisite for implementing try-catches in wasm. BUG= Review-Url: https://codereview.chromium.org/2339053003 Cr-Commit-Position: refs/heads/master@{#39449}
This commit is contained in:
parent
c2cf8b11ed
commit
5855e44c1a
@ -1703,6 +1703,29 @@ Node* WasmGraphBuilder::GrowMemory(Node* input) {
|
||||
return result;
|
||||
}
|
||||
|
||||
Node* WasmGraphBuilder::Throw(Node* input) {
|
||||
MachineOperatorBuilder* machine = jsgraph()->machine();
|
||||
|
||||
// Pass the thrown value as two SMIs:
|
||||
//
|
||||
// upper = static_cast<uint32_t>(input) >> 16;
|
||||
// lower = input & 0xFFFF;
|
||||
//
|
||||
// This is needed because we can't safely call BuildChangeInt32ToTagged from
|
||||
// this method.
|
||||
//
|
||||
// TODO(wasm): figure out how to properly pass this to the runtime function.
|
||||
Node* upper = BuildChangeInt32ToSmi(
|
||||
graph()->NewNode(machine->Word32Shr(), input, Int32Constant(16)));
|
||||
Node* lower = BuildChangeInt32ToSmi(
|
||||
graph()->NewNode(machine->Word32And(), input, Int32Constant(0xFFFFu)));
|
||||
|
||||
Node* parameters[] = {lower, upper}; // thrown value
|
||||
return BuildCallToRuntime(Runtime::kWasmThrow, jsgraph(),
|
||||
module_->instance->context, parameters,
|
||||
arraysize(parameters), effect_, *control_);
|
||||
}
|
||||
|
||||
Node* WasmGraphBuilder::BuildI32DivS(Node* left, Node* right,
|
||||
wasm::WasmCodePosition position) {
|
||||
MachineOperatorBuilder* m = jsgraph()->machine();
|
||||
|
@ -134,6 +134,7 @@ class WasmGraphBuilder {
|
||||
Node* Unop(wasm::WasmOpcode opcode, Node* input,
|
||||
wasm::WasmCodePosition position = wasm::kNoCodePosition);
|
||||
Node* GrowMemory(Node* input);
|
||||
Node* Throw(Node* input);
|
||||
unsigned InputCount(Node* node);
|
||||
bool IsPhiWithMerge(Node* phi, Node* merge);
|
||||
void AppendToMerge(Node* merge, Node* from);
|
||||
@ -306,6 +307,7 @@ class WasmGraphBuilder {
|
||||
|
||||
Node* BuildJavaScriptToNumber(Node* node, Node* context, Node* effect,
|
||||
Node* control);
|
||||
|
||||
Node* BuildChangeInt32ToTagged(Node* value);
|
||||
Node* BuildChangeFloat64ToTagged(Node* value);
|
||||
Node* BuildChangeTaggedToFloat64(Node* value);
|
||||
|
@ -21,8 +21,7 @@ namespace internal {
|
||||
RUNTIME_FUNCTION(Runtime_WasmGrowMemory) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK_EQ(1, args.length());
|
||||
uint32_t delta_pages = 0;
|
||||
CHECK(args[0]->ToUint32(&delta_pages));
|
||||
CONVERT_UINT32_ARG_CHECKED(delta_pages, 0);
|
||||
Handle<JSObject> module_instance;
|
||||
|
||||
{
|
||||
@ -118,5 +117,17 @@ RUNTIME_FUNCTION(Runtime_WasmThrowTypeError) {
|
||||
THROW_NEW_ERROR_RETURN_FAILURE(
|
||||
isolate, NewTypeError(MessageTemplate::kWasmTrapTypeError));
|
||||
}
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_WasmThrow) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK_EQ(2, args.length());
|
||||
CONVERT_SMI_ARG_CHECKED(lower, 0);
|
||||
CONVERT_SMI_ARG_CHECKED(upper, 1);
|
||||
|
||||
const int32_t thrown_value = (upper << 16) | lower;
|
||||
|
||||
return isolate->Throw(*isolate->factory()->NewNumberFromInt(thrown_value));
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
@ -914,7 +914,8 @@ namespace internal {
|
||||
|
||||
#define FOR_EACH_INTRINSIC_WASM(F) \
|
||||
F(WasmGrowMemory, 1, 1) \
|
||||
F(WasmThrowTypeError, 0, 1)
|
||||
F(WasmThrowTypeError, 0, 1) \
|
||||
F(WasmThrow, 2, 1)
|
||||
|
||||
#define FOR_EACH_INTRINSIC_RETURN_PAIR(F) \
|
||||
F(LoadLookupSlotForCall, 1, 2)
|
||||
|
@ -667,9 +667,8 @@ class WasmFullDecoder : public WasmDecoder {
|
||||
}
|
||||
case kExprThrow: {
|
||||
CHECK_PROTOTYPE_OPCODE(wasm_eh_prototype);
|
||||
Pop(0, kAstI32);
|
||||
|
||||
// TODO(jpp): start exception propagation.
|
||||
Value value = Pop(0, kAstI32);
|
||||
BUILD(Throw, value.node);
|
||||
break;
|
||||
}
|
||||
case kExprTry: {
|
||||
|
67
test/mjsunit/wasm/exceptions.js
Normal file
67
test/mjsunit/wasm/exceptions.js
Normal file
@ -0,0 +1,67 @@
|
||||
// Copyright 2015 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: --expose-wasm --wasm-eh-prototype
|
||||
|
||||
load("test/mjsunit/wasm/wasm-constants.js");
|
||||
load("test/mjsunit/wasm/wasm-module-builder.js");
|
||||
|
||||
var module = (function () {
|
||||
var builder = new WasmModuleBuilder();
|
||||
|
||||
builder.addFunction("throw_param_if_not_zero", kSig_i_i)
|
||||
.addBody([
|
||||
kExprGetLocal, 0,
|
||||
kExprI32Const, 0,
|
||||
kExprI32Ne,
|
||||
kExprIf,
|
||||
kExprGetLocal, 0,
|
||||
kExprThrow,
|
||||
kExprEnd,
|
||||
kExprI32Const, 1
|
||||
])
|
||||
.exportFunc()
|
||||
|
||||
builder.addFunction("throw_20", kSig_v_v)
|
||||
.addBody([
|
||||
kExprI32Const, 20,
|
||||
kExprThrow
|
||||
])
|
||||
.exportFunc()
|
||||
|
||||
builder.addFunction("throw_expr_with_params", kSig_v_ddi)
|
||||
.addBody([
|
||||
// p2 * (p0 + min(p0, p1))|0 - 20
|
||||
kExprGetLocal, 2,
|
||||
kExprGetLocal, 0,
|
||||
kExprGetLocal, 0,
|
||||
kExprGetLocal, 1,
|
||||
kExprF64Min,
|
||||
kExprF64Add,
|
||||
kExprI32SConvertF64,
|
||||
kExprI32Mul,
|
||||
kExprI32Const, 20,
|
||||
kExprI32Sub,
|
||||
kExprThrow
|
||||
])
|
||||
.exportFunc()
|
||||
|
||||
return builder.instantiate();
|
||||
})();
|
||||
|
||||
// Check the module exists.
|
||||
assertFalse(module === undefined);
|
||||
assertFalse(module === null);
|
||||
assertFalse(module === 0);
|
||||
assertEquals("object", typeof module.exports);
|
||||
assertEquals("function", typeof module.exports.throw_param_if_not_zero);
|
||||
|
||||
assertEquals(1, module.exports.throw_param_if_not_zero(0));
|
||||
assertWasmThrows(10, function() { module.exports.throw_param_if_not_zero(10) });
|
||||
assertWasmThrows(-1, function() { module.exports.throw_param_if_not_zero(-1) });
|
||||
assertWasmThrows(20, module.exports.throw_20);
|
||||
assertWasmThrows(
|
||||
-8, function() { module.exports.throw_expr_with_params(1.5, 2.5, 4); });
|
||||
assertWasmThrows(
|
||||
12, function() { module.exports.throw_expr_with_params(5.7, 2.5, 4); });
|
@ -106,6 +106,7 @@ var kSig_v_ii = makeSig([kAstI32, kAstI32], []);
|
||||
var kSig_v_iii = makeSig([kAstI32, kAstI32, kAstI32], []);
|
||||
var kSig_v_d = makeSig([kAstF64], []);
|
||||
var kSig_v_dd = makeSig([kAstF64, kAstF64], []);
|
||||
var kSig_v_ddi = makeSig([kAstF64, kAstF64, kAstI32], []);
|
||||
|
||||
function makeSig(params, results) {
|
||||
return {params: params, results: results};
|
||||
@ -143,6 +144,7 @@ var kExprBrIf = 0x07;
|
||||
var kExprBrTable = 0x08;
|
||||
var kExprReturn = 0x09;
|
||||
var kExprUnreachable = 0x0a;
|
||||
var kExprThrow = 0xfa;
|
||||
var kExprEnd = 0x0f;
|
||||
|
||||
var kExprI32Const = 0x10;
|
||||
@ -348,3 +350,20 @@ function assertTraps(trap, code) {
|
||||
}
|
||||
throw new MjsUnitAssertionError("Did not trap, expected: " + kTrapMsgs[trap]);
|
||||
}
|
||||
|
||||
function assertWasmThrows(value, code) {
|
||||
assertEquals("number", typeof(value));
|
||||
try {
|
||||
if (typeof code === 'function') {
|
||||
code();
|
||||
} else {
|
||||
eval(code);
|
||||
}
|
||||
} catch (e) {
|
||||
assertEquals("number", typeof e);
|
||||
assertEquals(value, e);
|
||||
// Success.
|
||||
return;
|
||||
}
|
||||
throw new MjsUnitAssertionError("Did not throw at all, expected: " + value);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user