From 5855e44c1a193c389d9cd899461092bfbce923a0 Mon Sep 17 00:00:00 2001 From: jpp Date: Thu, 15 Sep 2016 08:04:03 -0700 Subject: [PATCH] [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} --- src/compiler/wasm-compiler.cc | 23 ++++++++++ src/compiler/wasm-compiler.h | 2 + src/runtime/runtime-wasm.cc | 15 ++++++- src/runtime/runtime.h | 3 +- src/wasm/ast-decoder.cc | 5 +-- test/mjsunit/wasm/exceptions.js | 67 +++++++++++++++++++++++++++++ test/mjsunit/wasm/wasm-constants.js | 19 ++++++++ 7 files changed, 128 insertions(+), 6 deletions(-) create mode 100644 test/mjsunit/wasm/exceptions.js diff --git a/src/compiler/wasm-compiler.cc b/src/compiler/wasm-compiler.cc index f9620ac349..6c50183bee 100644 --- a/src/compiler/wasm-compiler.cc +++ b/src/compiler/wasm-compiler.cc @@ -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(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(); diff --git a/src/compiler/wasm-compiler.h b/src/compiler/wasm-compiler.h index 965f1dcf4f..29b0afae3a 100644 --- a/src/compiler/wasm-compiler.h +++ b/src/compiler/wasm-compiler.h @@ -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); diff --git a/src/runtime/runtime-wasm.cc b/src/runtime/runtime-wasm.cc index 02073b77a0..d41be53a4e 100644 --- a/src/runtime/runtime-wasm.cc +++ b/src/runtime/runtime-wasm.cc @@ -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 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 diff --git a/src/runtime/runtime.h b/src/runtime/runtime.h index ff37e77411..e66a6dc168 100644 --- a/src/runtime/runtime.h +++ b/src/runtime/runtime.h @@ -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) diff --git a/src/wasm/ast-decoder.cc b/src/wasm/ast-decoder.cc index 4795f84e7a..7083279fe6 100644 --- a/src/wasm/ast-decoder.cc +++ b/src/wasm/ast-decoder.cc @@ -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: { diff --git a/test/mjsunit/wasm/exceptions.js b/test/mjsunit/wasm/exceptions.js new file mode 100644 index 0000000000..07b0bf4e0e --- /dev/null +++ b/test/mjsunit/wasm/exceptions.js @@ -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); }); diff --git a/test/mjsunit/wasm/wasm-constants.js b/test/mjsunit/wasm/wasm-constants.js index 3a9ac5042e..5bcdb64a95 100644 --- a/test/mjsunit/wasm/wasm-constants.js +++ b/test/mjsunit/wasm/wasm-constants.js @@ -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); +}