Commit a12e9329 authored by Thibaud Michaud's avatar Thibaud Michaud Committed by Commit Bot

[wasm][eh] Fix catch_all encoding

In the latest spec, catch_all is encoded as 0x05. This is the same
opcode as "else", but they do not conflict because "else" is not valid
in the context of a try block.

The 0x0a opcode now corresponds to the "unwind" instruction, which
currently has the same semantics as "catch_all".

R=clemensb@chromium.org

Bug: v8:11392
Change-Id: Ie9cd06c9a2001a02d8bea5be7a3c016e3a58ee3d
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2674007
Commit-Queue: Thibaud Michaud <thibaudm@chromium.org>
Reviewed-by: 's avatarClemens Backes <clemensb@chromium.org>
Cr-Commit-Position: refs/heads/master@{#72531}
parent 63f7d83d
......@@ -1744,7 +1744,7 @@ class WasmDecoder : public Decoder {
case kExprReturnCallRef:
case kExprDrop:
case kExprSelect:
case kExprCatchAll:
case kExprUnwind:
return 1;
case kExprSelectWithType: {
SelectTypeImmediate<validate> imm(WasmFeatures::All(), decoder, pc + 1,
......@@ -2566,19 +2566,19 @@ class WasmFullDecoder : public WasmDecoder<validate> {
return 1 + imm.length;
}
DECODE(CatchAll) {
DECODE(Unwind) {
CHECK_PROTOTYPE_OPCODE(eh);
if (!VALIDATE(!control_.empty())) {
this->error("catch-all does not match any try");
this->DecodeError("unwind does not match any try");
return 0;
}
Control* c = &control_.back();
if (!VALIDATE(c->is_try())) {
this->error("catch-all does not match any try");
this->DecodeError("unwind does not match any try");
return 0;
}
if (!VALIDATE(!c->is_try_catchall())) {
this->error("catch-all already present for try");
if (!VALIDATE(!c->is_try_catch() && !c->is_try_catchall())) {
this->error("catch, catch-all or unwind already present for try");
return 0;
}
c->kind = kControlTryCatchAll;
......@@ -2685,26 +2685,41 @@ class WasmFullDecoder : public WasmDecoder<validate> {
return 1 + imm.length;
}
// Alias for "catch_all" if the current block is a try.
DECODE(Else) {
if (!VALIDATE(!control_.empty())) {
this->DecodeError("else does not match any if");
this->DecodeError("else/catch_all does not match any if/try");
return 0;
}
Control* c = &control_.back();
if (!VALIDATE(c->is_if())) {
this->DecodeError("else does not match an if");
if (!VALIDATE(c->is_if() || c->is_try())) {
this->DecodeError("else/catch_all does not match any if/try");
return 0;
}
if (!VALIDATE(c->is_onearmed_if())) {
this->DecodeError("else already present for if");
return 0;
if (c->is_if()) {
if (!VALIDATE(c->is_onearmed_if())) {
this->DecodeError("else already present for if");
return 0;
}
if (!TypeCheckFallThru()) return 0;
c->kind = kControlIfElse;
CALL_INTERFACE_IF_PARENT_REACHABLE(Else, c);
if (c->reachable()) c->end_merge.reached = true;
PushMergeValues(c, &c->start_merge);
c->reachability = control_at(1)->innerReachability();
} else {
CHECK_PROTOTYPE_OPCODE(eh);
DCHECK(c->is_try());
if (!VALIDATE(!c->is_try_catchall())) {
this->error("catch-all or unwind already present for try");
return 0;
}
c->kind = kControlTryCatchAll;
FallThruTo(c);
stack_end_ = stack_ + c->stack_depth;
c->reachability = control_at(1)->innerReachability();
CALL_INTERFACE_IF_PARENT_REACHABLE(CatchAll, c);
}
if (!TypeCheckFallThru()) return 0;
c->kind = kControlIfElse;
CALL_INTERFACE_IF_PARENT_REACHABLE(Else, c);
if (c->reachable()) c->end_merge.reached = true;
PushMergeValues(c, &c->start_merge);
c->reachability = control_at(1)->innerReachability();
current_code_reachable_ = this->ok() && c->reachable();
return 1;
}
......@@ -3300,7 +3315,7 @@ class WasmFullDecoder : public WasmDecoder<validate> {
DECODE_IMPL(Try);
DECODE_IMPL(Catch);
DECODE_IMPL(Delegate);
DECODE_IMPL(CatchAll);
DECODE_IMPL(Unwind);
DECODE_IMPL(BrOnNull);
DECODE_IMPL(Let);
DECODE_IMPL(Loop);
......
......@@ -187,8 +187,7 @@ bool PrintRawWasmCode(AccountingAllocator* allocator, const FunctionBody& body,
offset = 2;
}
if (line_numbers) line_numbers->push_back(i.position());
if (opcode == kExprElse || opcode == kExprCatch ||
opcode == kExprCatchAll) {
if (opcode == kExprElse || opcode == kExprCatch || opcode == kExprUnwind) {
control_depth--;
}
......@@ -238,7 +237,7 @@ bool PrintRawWasmCode(AccountingAllocator* allocator, const FunctionBody& body,
switch (opcode) {
case kExprElse:
case kExprCatch:
case kExprCatchAll:
case kExprUnwind:
os << " @" << i.pc_offset();
control_depth++;
break;
......
......@@ -188,7 +188,7 @@ constexpr const char* WasmOpcodes::OpcodeName(WasmOpcode opcode) {
CASE_OP(Delegate, "delegate")
CASE_OP(Throw, "throw")
CASE_OP(Rethrow, "rethrow")
CASE_OP(CatchAll, "catch-all")
CASE_OP(Unwind, "unwind")
// asm.js-only opcodes.
CASE_F64_OP(Acos, "acos")
......
......@@ -38,7 +38,7 @@ bool V8_EXPORT_PRIVATE IsJSCompatibleSignature(const FunctionSig* sig,
V(Catch, 0x07, _ /* eh_prototype */) \
V(Throw, 0x08, _ /* eh_prototype */) \
V(Rethrow, 0x09, _ /* eh_prototype */) \
V(CatchAll, 0x0a, _ /* eh_prototype */) \
V(Unwind, 0x0a, _ /* eh_prototype */) \
V(End, 0x0b, _) \
V(Br, 0x0c, _) \
V(BrIf, 0x0d, _) \
......
......@@ -185,8 +185,8 @@
except, catchstmt, kExprEnd
#define WASM_TRY_CATCH_R(t, trystmt, catchstmt) \
kExprTry, WASM_REF_TYPE(t), trystmt, kExprCatch, catchstmt, kExprEnd
#define WASM_TRY_CATCH_ALL_T(t, trystmt, catchstmt) \
kExprTry, static_cast<byte>((t).value_type_code()), trystmt, kExprCatchAll, \
#define WASM_TRY_CATCH_ALL_T(t, trystmt, catchstmt) \
kExprTry, static_cast<byte>((t).value_type_code()), trystmt, kExprElse, \
catchstmt, kExprEnd
#define WASM_TRY_DELEGATE(trystmt, depth) \
kExprTry, kVoidCode, trystmt, kExprDelegate, depth
......
......@@ -21,7 +21,7 @@ load("test/mjsunit/wasm/wasm-module-builder.js");
kExprCallFunction, f.index,
kExprCallFunction, f.index,
kExprLocalSet, 0,
kExprCatchAll,
kExprElse,
kExprLocalGet, 0,
kExprCallFunction, f.index,
kExprLocalSet, 0,
......
......@@ -49,7 +49,15 @@ load("test/mjsunit/wasm/exceptions-utils.js");
.addBody([
kExprTry, kWasmStmt,
kExprThrow, except,
kExprCatchAll,
kExprElse,
kExprRethrow, 0,
kExprEnd,
]).exportFunc();
builder.addFunction("rethrow0_unwind", kSig_v_v)
.addBody([
kExprTry, kWasmStmt,
kExprThrow, except,
kExprUnwind,
kExprRethrow, 0,
kExprEnd,
]).exportFunc();
......@@ -57,7 +65,20 @@ load("test/mjsunit/wasm/exceptions-utils.js");
.addBody([
kExprTry, kWasmI32,
kExprThrow, except,
kExprCatchAll,
kExprElse,
kExprLocalGet, 0,
kExprI32Eqz,
kExprIf, kWasmStmt,
kExprRethrow, 1,
kExprEnd,
kExprI32Const, 23,
kExprEnd
]).exportFunc();
builder.addFunction("rethrow1_unwind", kSig_i_i)
.addBody([
kExprTry, kWasmI32,
kExprThrow, except,
kExprUnwind,
kExprLocalGet, 0,
kExprI32Eqz,
kExprIf, kWasmStmt,
......@@ -69,8 +90,11 @@ load("test/mjsunit/wasm/exceptions-utils.js");
let instance = builder.instantiate();
assertWasmThrows(instance, except, [], () => instance.exports.rethrow0());
assertWasmThrows(instance, except, [], () => instance.exports.rethrow0_unwind());
assertWasmThrows(instance, except, [], () => instance.exports.rethrow1(0));
assertWasmThrows(instance, except, [], () => instance.exports.rethrow1_unwind(0));
assertEquals(23, instance.exports.rethrow1(1));
assertEquals(23, instance.exports.rethrow1_unwind(1));
})();
// Test that rethrow expression properly target the correct surrounding try
......
......@@ -76,12 +76,20 @@ load("test/mjsunit/wasm/exceptions-utils.js");
.addBody([
kExprTry, kWasmStmt,
kExprUnreachable,
kExprCatchAll,
kExprElse,
kExprEnd
]).exportFunc();
builder.addFunction('unreachable_in_try_unwind', kSig_v_v)
.addBody([
kExprTry, kWasmStmt,
kExprUnreachable,
kExprUnwind,
kExprEnd
]).exportFunc();
let instance = builder.instantiate();
assertTraps(kTrapUnreachable, () => instance.exports.unreachable_in_try());
assertTraps(kTrapUnreachable, () => instance.exports.unreachable_in_try_unwind());
})();
(function TestTrapInCalleeNotCaught() {
......@@ -98,7 +106,17 @@ load("test/mjsunit/wasm/exceptions-utils.js");
kExprLocalGet, 0,
kExprLocalGet, 1,
kExprCallFunction, func_div.index,
kExprCatchAll,
kExprElse,
kExprI32Const, 11,
kExprEnd
]).exportFunc();
builder.addFunction('trap_in_callee_unwind', kSig_i_ii)
.addBody([
kExprTry, kWasmI32,
kExprLocalGet, 0,
kExprLocalGet, 1,
kExprCallFunction, func_div.index,
kExprUnwind,
kExprI32Const, 11,
kExprEnd
]).exportFunc();
......@@ -106,6 +124,7 @@ load("test/mjsunit/wasm/exceptions-utils.js");
assertEquals(3, instance.exports.trap_in_callee(7, 2));
assertTraps(kTrapDivByZero, () => instance.exports.trap_in_callee(1, 0));
assertTraps(kTrapDivByZero, () => instance.exports.trap_in_callee_unwind(1, 0));
})();
(function TestTrapViaJSNotCaught() {
......@@ -122,7 +141,15 @@ load("test/mjsunit/wasm/exceptions-utils.js");
.addBody([
kExprTry, kWasmI32,
kExprCallFunction, imp,
kExprCatchAll,
kExprElse,
kExprI32Const, 11,
kExprEnd
]).exportFunc();
builder.addFunction('call_import_unwind', kSig_i_v)
.addBody([
kExprTry, kWasmI32,
kExprCallFunction, imp,
kExprUnwind,
kExprI32Const, 11,
kExprEnd
]).exportFunc();
......@@ -137,6 +164,7 @@ load("test/mjsunit/wasm/exceptions-utils.js");
throw exception;
}
instance = builder.instantiate({imp: {ort: js_import}});
let caught = undefined;
try {
let res = instance.exports.call_import();
......@@ -147,6 +175,18 @@ load("test/mjsunit/wasm/exceptions-utils.js");
assertSame(exception, caught);
assertInstanceof(exception, WebAssembly.RuntimeError);
assertEquals(exception.message, kTrapMsgs[kTrapDivByZero]);
// Same test with unwind instead of catch_all.
caught = undefined;
try {
let res = instance.exports.call_import_unwind();
assertUnreachable('call_import_unwind should trap, but returned with ' + res);
} catch (e) {
caught = e;
}
assertSame(exception, caught);
assertInstanceof(exception, WebAssembly.RuntimeError);
assertEquals(exception.message, kTrapMsgs[kTrapDivByZero]);
})();
(function TestManuallyThrownRuntimeErrorCaught() {
......@@ -157,7 +197,15 @@ load("test/mjsunit/wasm/exceptions-utils.js");
.addBody([
kExprTry, kWasmI32,
kExprCallFunction, imp,
kExprCatchAll,
kExprElse,
kExprI32Const, 11,
kExprEnd
]).exportFunc();
builder.addFunction('call_import_unwind', kSig_i_v)
.addBody([
kExprTry, kWasmI32,
kExprCallFunction, imp,
kExprUnwind,
kExprI32Const, 11,
kExprEnd
]).exportFunc();
......@@ -167,6 +215,7 @@ load("test/mjsunit/wasm/exceptions-utils.js");
let instance = builder.instantiate({imp: {ort: throw_exc}});
assertEquals(11, instance.exports.call_import());
assertEquals(11, instance.exports.call_import_unwind());
})();
(function TestExnWithWasmProtoNotCaught() {
......@@ -967,9 +1016,19 @@ load("test/mjsunit/wasm/exceptions-utils.js");
kExprTry, kWasmStmt,
kExprThrow, except,
kExprDelegate, 1,
kExprCatchAll,
kExprElse,
kExprEnd
]).exportFunc();
builder.addFunction('test_unwind', kSig_v_v)
.addBody([
kExprTry, kWasmStmt,
kExprTry, kWasmStmt,
kExprThrow, except,
kExprDelegate, 1,
kExprUnwind,
kExprEnd
]).exportFunc();
instance = builder.instantiate();
assertTraps(WebAssembly.RuntimeError, () => instance.exports.test());
assertTraps(WebAssembly.RuntimeError, () => instance.exports.test_unwind());
})();
......@@ -216,7 +216,7 @@ const kWasmOpcodes = {
'Catch': 0x07,
'Throw': 0x08,
'Rethrow': 0x09,
'CatchAll': 0x0a,
'Unwind': 0x0a,
'End': 0x0b,
'Br': 0x0c,
'BrIf': 0x0d,
......
......@@ -2871,19 +2871,30 @@ TEST_F(FunctionBodyDecoderTest, TryCatch) {
WASM_FEATURE_SCOPE(eh);
byte ex = builder.AddException(sigs.v_v());
ExpectValidates(sigs.v_v(), {WASM_TRY_OP, kExprCatch, ex, kExprEnd});
ExpectFailure(sigs.v_v(),
{WASM_TRY_OP, kExprCatchAll, kExprCatch, ex, kExprEnd});
ExpectFailure(sigs.v_v(),
{WASM_TRY_OP, kExprCatchAll, kExprCatchAll, kExprEnd});
ExpectValidates(sigs.v_v(),
{WASM_TRY_OP, kExprCatch, ex, kExprElse, kExprEnd});
ExpectFailure(sigs.v_v(), {WASM_TRY_OP, kExprElse, kExprCatch, ex, kExprEnd});
ExpectFailure(sigs.v_v(), {WASM_TRY_OP, kExprElse, kExprElse, kExprEnd});
ExpectFailure(sigs.v_v(), {WASM_TRY_OP, kExprEnd}); // Missing catch.
ExpectFailure(sigs.v_v(), {WASM_TRY_OP, kExprCatch, ex}); // Missing end.
ExpectFailure(sigs.v_v(), {kExprCatch, kExprEnd}); // Missing try.
}
TEST_F(FunctionBodyDecoderTest, TryUnwind) {
WASM_FEATURE_SCOPE(eh);
byte ex = builder.AddException(sigs.v_v());
ExpectValidates(sigs.v_v(), {WASM_TRY_OP, kExprUnwind, kExprEnd});
ExpectFailure(sigs.v_v(),
{WASM_TRY_OP, kExprUnwind, kExprCatch, ex, kExprEnd});
ExpectFailure(sigs.v_v(), {WASM_TRY_OP, kExprElse, kExprUnwind, kExprEnd});
ExpectFailure(sigs.v_v(),
{WASM_TRY_OP, kExprCatch, ex, kExprUnwind, kExprEnd});
}
TEST_F(FunctionBodyDecoderTest, Rethrow) {
WASM_FEATURE_SCOPE(eh);
ExpectValidates(sigs.v_v(),
{WASM_TRY_OP, kExprCatchAll, kExprRethrow, 0, kExprEnd});
{WASM_TRY_OP, kExprElse, kExprRethrow, 0, kExprEnd});
ExpectFailure(sigs.v_v(), {WASM_TRY_OP, kExprRethrow, kExprCatch, kExprEnd});
ExpectFailure(sigs.v_v(), {WASM_BLOCK(kExprRethrow)});
ExpectFailure(sigs.v_v(), {kExprRethrow});
......@@ -2923,7 +2934,7 @@ TEST_F(FunctionBodyDecoderTest, TryDelegate) {
kAppendEnd, "delegate does not match a try");
ExpectFailure(
sigs.v_v(),
{WASM_TRY_OP, WASM_TRY_OP, kExprCatchAll, kExprDelegate, 1, kExprEnd},
{WASM_TRY_OP, WASM_TRY_OP, kExprElse, kExprDelegate, 1, kExprEnd},
kAppendEnd, "delegate does not match a try");
}
......
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