[wasm][eh] Implicit rethrow after unwind

Implicitly rethrow the exception when we reach the end of a
try..unwind..end. Also make it a validation error to rethrow
an exception caught by an unwind block.

R=clemensb@chromium.org

Bug: v8:8091
Change-Id: Ia149d2e81b1fbfa9209047b35ff0c9fedc1b8895
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2696662
Commit-Queue: Thibaud Michaud <thibaudm@chromium.org>
Reviewed-by: Clemens Backes <clemensb@chromium.org>
Cr-Commit-Position: refs/heads/master@{#72785}
This commit is contained in:
Thibaud Michaud 2021-02-16 18:36:18 +01:00 committed by Commit Bot
parent a326829e9a
commit 469788dff6
5 changed files with 41 additions and 36 deletions

View File

@ -944,7 +944,8 @@ enum ControlKind : uint8_t {
kControlLet,
kControlTry,
kControlTryCatch,
kControlTryCatchAll
kControlTryCatchAll,
kControlTryUnwind
};
enum Reachability : uint8_t {
@ -1004,8 +1005,10 @@ struct ControlBase : public PcForErrors<validate> {
bool is_incomplete_try() const { return kind == kControlTry; }
bool is_try_catch() const { return kind == kControlTryCatch; }
bool is_try_catchall() const { return kind == kControlTryCatchAll; }
bool is_try_unwind() const { return kind == kControlTryUnwind; }
bool is_try() const {
return is_incomplete_try() || is_try_catch() || is_try_catchall();
return is_incomplete_try() || is_try_catch() || is_try_catchall() ||
is_try_unwind();
}
inline Merge<Value>* br_merge() {
@ -2402,6 +2405,7 @@ class WasmFullDecoder : public WasmDecoder<validate> {
case kControlIfElse:
case kControlTryCatch:
case kControlTryCatchAll:
case kControlTryUnwind:
case kControlLet: // TODO(7748): Implement
break;
}
@ -2520,6 +2524,10 @@ class WasmFullDecoder : public WasmDecoder<validate> {
this->DecodeError("catch after catch-all for try");
return 0;
}
if (!VALIDATE(!c->is_try_unwind())) {
this->DecodeError("catch after unwind for try");
return 0;
}
c->kind = kControlTryCatch;
FallThruTo(c);
DCHECK_LE(stack_ + c->stack_depth, stack_end_);
@ -2553,7 +2561,8 @@ class WasmFullDecoder : public WasmDecoder<validate> {
"delegate target must be a try block or the function block");
return 0;
}
if (target->is_try_catch() || target->is_try_catchall()) {
if (target->is_try_catch() || target->is_try_catchall() ||
target->is_try_catchall()) {
this->DecodeError(
"cannot delegate inside the catch handler of the target");
}
@ -2573,12 +2582,13 @@ class WasmFullDecoder : public WasmDecoder<validate> {
this->DecodeError("unwind does not match a try");
return 0;
}
if (!VALIDATE(!c->is_try_catch() && !c->is_try_catchall())) {
if (!VALIDATE(!c->is_try_catch() && !c->is_try_catchall() &&
!c->is_try_unwind())) {
this->error("catch, catch-all or unwind already present for try");
return 0;
}
c->kind = kControlTryCatchAll;
FallThruTo(c);
c->kind = kControlTryUnwind;
stack_end_ = stack_ + c->stack_depth;
c->reachability = control_at(1)->innerReachability();
CALL_INTERFACE_IF_PARENT_REACHABLE(CatchAll, c);
@ -2704,7 +2714,11 @@ class WasmFullDecoder : public WasmDecoder<validate> {
CHECK_PROTOTYPE_OPCODE(eh);
DCHECK(c->is_try());
if (!VALIDATE(!c->is_try_catchall())) {
this->error("catch-all or unwind already present for try");
this->error("catch-all already present for try");
return 0;
}
if (!VALIDATE(!c->is_try_unwind())) {
this->error("cannot have a catch-all after unwind");
return 0;
}
c->kind = kControlTryCatchAll;
@ -2741,6 +2755,11 @@ class WasmFullDecoder : public WasmDecoder<validate> {
CALL_INTERFACE_IF_REACHABLE(Rethrow, c);
EndControl();
}
if (c->is_try_unwind()) {
// Unwind implicitly rethrows at the end.
CALL_INTERFACE_IF_REACHABLE(Rethrow, c);
EndControl();
}
if (c->is_let()) {
this->local_types_.erase(this->local_types_.begin(),

View File

@ -660,8 +660,10 @@ class WasmGraphBuildingInterface {
}
void Rethrow(FullDecoder* decoder, Control* block) {
DCHECK(block->is_try_catchall() || block->is_try_catch());
DCHECK(block->is_try_catchall() || block->is_try_catch() ||
block->is_try_unwind());
TFNode* exception = block->try_info->exception;
DCHECK_NOT_NULL(exception);
BUILD(Rethrow, exception);
TerminateThrow(decoder);
}
@ -747,7 +749,8 @@ class WasmGraphBuildingInterface {
}
void CatchAll(FullDecoder* decoder, Control* block) {
DCHECK(block->is_try_catchall() || block->is_try_catch());
DCHECK(block->is_try_catchall() || block->is_try_catch() ||
block->is_try_unwind());
DCHECK_EQ(decoder->control_at(0), block);
current_catch_ = block->previous_catch; // Pop try scope.

View File

@ -53,14 +53,6 @@ load("test/mjsunit/wasm/exceptions-utils.js");
kExprRethrow, 0,
kExprEnd,
]).exportFunc();
builder.addFunction("rethrow0_unwind", kSig_v_v)
.addBody([
kExprTry, kWasmStmt,
kExprThrow, except,
kExprUnwind,
kExprRethrow, 0,
kExprEnd,
]).exportFunc();
builder.addFunction("rethrow1", kSig_i_i)
.addBody([
kExprTry, kWasmI32,
@ -74,27 +66,11 @@ load("test/mjsunit/wasm/exceptions-utils.js");
kExprI32Const, 23,
kExprEnd
]).exportFunc();
builder.addFunction("rethrow1_unwind", kSig_i_i)
.addBody([
kExprTry, kWasmI32,
kExprThrow, except,
kExprUnwind,
kExprLocalGet, 0,
kExprI32Eqz,
kExprIf, kWasmStmt,
kExprRethrow, 1,
kExprEnd,
kExprI32Const, 23,
kExprEnd
]).exportFunc();
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

View File

@ -215,7 +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());
assertThrows(instance.exports.call_import_unwind, WebAssembly.RuntimeError, "My user text");
})();
(function TestExnWithWasmProtoNotCaught() {

View File

@ -2895,9 +2895,16 @@ TEST_F(FunctionBodyDecoderTest, Rethrow) {
WASM_FEATURE_SCOPE(eh);
ExpectValidates(sigs.v_v(),
{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});
ExpectFailure(sigs.v_v(),
{WASM_TRY_OP, kExprRethrow, 0, kExprCatch, kExprEnd},
kAppendEnd, "rethrow not targeting catch or catch-all");
ExpectFailure(sigs.v_v(), {WASM_BLOCK(kExprRethrow, 0)}, kAppendEnd,
"rethrow not targeting catch or catch-all");
ExpectFailure(sigs.v_v(), {kExprRethrow, 0}, kAppendEnd,
"rethrow not targeting catch or catch-all");
ExpectFailure(sigs.v_v(),
{WASM_TRY_OP, kExprUnwind, kExprRethrow, 0, kExprEnd},
kAppendEnd, "rethrow not targeting catch or catch-all");
}
TEST_F(FunctionBodyDecoderTest, TryDelegate) {