[wasm][eh] Update catch_all encoding

'catch_all' and 'else' use distinct opcodes now.

R=clemensb@chromium.org

Bug: v8:8091
Change-Id: If07e46b9ea23068953db1765d10c7e3746d21d99
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2699258
Commit-Queue: Thibaud Michaud <thibaudm@chromium.org>
Reviewed-by: Clemens Backes <clemensb@chromium.org>
Cr-Commit-Position: refs/heads/master@{#72810}
This commit is contained in:
Thibaud Michaud 2021-02-17 12:53:28 +01:00 committed by Commit Bot
parent 54ef2044a2
commit 5d618f1f81
13 changed files with 114 additions and 89 deletions

View File

@ -1747,6 +1747,7 @@ class WasmDecoder : public Decoder {
case kExprReturnCallRef:
case kExprDrop:
case kExprSelect:
case kExprCatchAll:
case kExprUnwind:
return 1;
case kExprSelectWithType: {
@ -2073,6 +2074,7 @@ class WasmDecoder : public Decoder {
case kExprElse:
case kExprTry:
case kExprCatch:
case kExprCatchAll:
case kExprDelegate:
case kExprUnwind:
case kExprRethrow:
@ -2575,6 +2577,31 @@ class WasmFullDecoder : public WasmDecoder<validate> {
return 1 + imm.length;
}
DECODE(CatchAll) {
CHECK_PROTOTYPE_OPCODE(eh);
DCHECK(!control_.empty());
Control* c = &control_.back();
if (!VALIDATE(c->is_try())) {
this->DecodeError("catch-all does not match a try");
return 0;
}
if (!VALIDATE(!c->is_try_catchall())) {
this->error("catch-all already present for try");
return 0;
}
if (!VALIDATE(!c->is_try_unwind())) {
this->error("cannot have catch-all after unwind");
return 0;
}
FallThruTo(c);
c->kind = kControlTryCatchAll;
stack_end_ = stack_ + c->stack_depth;
c->reachability = control_at(1)->innerReachability();
CALL_INTERFACE_IF_PARENT_REACHABLE(CatchAll, c);
current_code_reachable_ = this->ok() && c->reachable();
return 1;
}
DECODE(Unwind) {
CHECK_PROTOTYPE_OPCODE(eh);
DCHECK(!control_.empty());
@ -2692,42 +2719,23 @@ class WasmFullDecoder : public WasmDecoder<validate> {
return 1 + imm.length;
}
// Alias for "catch_all" if the current block is a try.
DECODE(Else) {
DCHECK(!control_.empty());
Control* c = &control_.back();
if (!VALIDATE(c->is_if() || c->is_try())) {
this->DecodeError("else/catch_all does not match an if/try");
if (!VALIDATE(c->is_if())) {
this->DecodeError("else does not match an 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 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;
FallThruTo(c);
stack_end_ = stack_ + c->stack_depth;
c->reachability = control_at(1)->innerReachability();
CALL_INTERFACE_IF_PARENT_REACHABLE(CatchAll, c);
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();
current_code_reachable_ = this->ok() && c->reachable();
return 1;
}
@ -3329,6 +3337,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);

View File

@ -190,7 +190,8 @@ bool PrintRawWasmCode(AccountingAllocator* allocator, const FunctionBody& body,
offset = 2;
}
if (line_numbers) line_numbers->push_back(i.position());
if (opcode == kExprElse || opcode == kExprCatch || opcode == kExprUnwind) {
if (opcode == kExprElse || opcode == kExprCatch ||
opcode == kExprCatchAll || opcode == kExprUnwind) {
control_depth--;
}
@ -240,6 +241,7 @@ bool PrintRawWasmCode(AccountingAllocator* allocator, const FunctionBody& body,
switch (opcode) {
case kExprElse:
case kExprCatch:
case kExprCatchAll:
case kExprUnwind:
os << " @" << i.pc_offset();
control_depth++;

View File

@ -188,6 +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.

View File

@ -46,8 +46,9 @@ bool V8_EXPORT_PRIVATE IsJSCompatibleSignature(const FunctionSig* sig,
V(Return, 0x0f, _) \
V(Let, 0x17, _ /* typed_funcref prototype */) \
V(Delegate, 0x18, _ /* eh_prototype */) \
V(CatchAll, 0x19, _ /* eh_prototype */) \
V(BrOnNull, 0xd4, _ /* gc prototype */) \
V(NopForTestingUnsupportedInLiftoff, 0x19, _)
V(NopForTestingUnsupportedInLiftoff, 0x16, _)
// Constants, locals, globals, and calls.
#define FOREACH_MISC_OPCODE(V) \

View File

@ -112,7 +112,7 @@ WASM_EXEC_TEST(TryCatchAllThrow) {
WASM_IF(WASM_I32_EQZ(WASM_LOCAL_GET(0)), WASM_THROW(except1)),
WASM_IF(WASM_I32_EQ(WASM_LOCAL_GET(0), WASM_I32V(1)),
WASM_THROW(except2))),
kExprCatch, except1, WASM_STMTS(WASM_I32V(kResult0)), kExprElse,
kExprCatch, except1, WASM_STMTS(WASM_I32V(kResult0)), kExprCatchAll,
WASM_STMTS(WASM_I32V(kResult1)), kExprEnd);
if (execution_tier != TestExecutionTier::kInterpreter) {

View File

@ -825,49 +825,50 @@ class SideTable : public ZoneObject {
break;
}
case kExprElse: {
// Alias for catch_all if the current block is a try.
TRACE("control @%u: Else\n", i.pc_offset());
Control* c = &control_stack.back();
if (*c->pc == kExprIf) {
copy_unreachable();
TRACE("control @%u: Else\n", i.pc_offset());
if (!unreachable) c->end_label->Ref(i.pc(), stack_height);
DCHECK_NOT_NULL(c->else_label);
c->else_label->Bind(i.pc() + 1);
c->else_label->Finish(&map_, code->start);
stack_height = c->else_label->target_stack_height;
c->else_label = nullptr;
DCHECK_IMPLIES(!unreachable,
stack_height >= c->end_label->target_stack_height);
} else {
DCHECK_EQ(*c->pc, kExprTry);
if (!exception_stack.empty() &&
exception_stack.back() == control_stack.size() - 1) {
// Only pop the exception stack if this is the only catch handler.
exception_stack.pop_back();
}
copy_unreachable();
TRACE("control @%u: CatchAll\n", i.pc_offset());
if (!unreachable) c->end_label->Ref(i.pc(), stack_height);
DCHECK_NOT_NULL(c->else_label);
int control_index = static_cast<int>(control_stack.size()) - 1;
c->else_label->Bind(i.pc() + 1, kCatchAllExceptionIndex,
control_index);
c->else_label->Finish(&map_, code->start);
c->else_label = nullptr;
DCHECK_IMPLIES(!unreachable,
stack_height >= c->end_label->target_stack_height);
stack_height = c->end_label->target_stack_height;
DCHECK_EQ(*c->pc, kExprIf);
copy_unreachable();
if (!unreachable) c->end_label->Ref(i.pc(), stack_height);
DCHECK_NOT_NULL(c->else_label);
c->else_label->Bind(i.pc() + 1);
c->else_label->Finish(&map_, code->start);
stack_height = c->else_label->target_stack_height;
c->else_label = nullptr;
DCHECK_IMPLIES(!unreachable,
stack_height >= c->end_label->target_stack_height);
break;
}
case kExprCatchAll: {
TRACE("control @%u: CatchAll\n", i.pc_offset());
Control* c = &control_stack.back();
DCHECK_EQ(*c->pc, kExprTry);
if (!exception_stack.empty() &&
exception_stack.back() == control_stack.size() - 1) {
// Only pop the exception stack if this is the only catch handler.
exception_stack.pop_back();
}
copy_unreachable();
if (!unreachable) c->end_label->Ref(i.pc(), stack_height);
DCHECK_NOT_NULL(c->else_label);
int control_index = static_cast<int>(control_stack.size()) - 1;
c->else_label->Bind(i.pc() + 1, kCatchAllExceptionIndex,
control_index);
c->else_label->Finish(&map_, code->start);
c->else_label = nullptr;
DCHECK_IMPLIES(!unreachable,
stack_height >= c->end_label->target_stack_height);
stack_height = c->end_label->target_stack_height;
break;
}
case kExprUnwind: {
TRACE("control @%u: Unwind\n", i.pc_offset());
Control* c = &control_stack.back();
DCHECK_EQ(*c->pc, kExprTry);
DCHECK(!exception_stack.empty());
DCHECK_EQ(exception_stack.back(), control_stack.size() - 1);
exception_stack.pop_back();
copy_unreachable();
TRACE("control @%u: Unwind\n", i.pc_offset());
if (!unreachable) c->end_label->Ref(i.pc(), stack_height);
DCHECK_NOT_NULL(c->else_label);
int control_index = static_cast<int>(control_stack.size()) - 1;
@ -3472,7 +3473,8 @@ class WasmInterpreterInternals {
}
case kExprElse:
case kExprUnwind:
case kExprCatch: {
case kExprCatch:
case kExprCatchAll: {
len = LookupTargetDelta(code, pc);
TRACE(" end => @%zu\n", pc + len);
break;

View File

@ -189,8 +189,8 @@
except1, catchstmt1, kExprCatch, except2, catchstmt2, 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, kExprElse, \
#define WASM_TRY_CATCH_ALL_T(t, trystmt, catchstmt) \
kExprTry, static_cast<byte>((t).value_type_code()), trystmt, kExprCatchAll, \
catchstmt, kExprEnd
#define WASM_TRY_DELEGATE(trystmt, depth) \
kExprTry, kVoidCode, trystmt, kExprDelegate, depth

View File

@ -11,7 +11,7 @@ load('test/mjsunit/wasm/wasm-module-builder.js');
// Create a simple Wasm module.
function create_builder(i) {
const kExprNopForTestingUnsupportedInLiftoff = 0x19;
const kExprNopForTestingUnsupportedInLiftoff = 0x16;
const builder = new WasmModuleBuilder();
builder.addFunction('main', kSig_i_i)
.addBody([

View File

@ -21,7 +21,7 @@ load("test/mjsunit/wasm/wasm-module-builder.js");
kExprCallFunction, f.index,
kExprCallFunction, f.index,
kExprLocalSet, 0,
kExprElse,
kExprCatchAll,
kExprLocalGet, 0,
kExprCallFunction, f.index,
kExprLocalSet, 0,

View File

@ -49,7 +49,7 @@ load("test/mjsunit/wasm/exceptions-utils.js");
.addBody([
kExprTry, kWasmStmt,
kExprThrow, except,
kExprElse,
kExprCatchAll,
kExprRethrow, 0,
kExprEnd,
]).exportFunc();
@ -57,7 +57,7 @@ load("test/mjsunit/wasm/exceptions-utils.js");
.addBody([
kExprTry, kWasmI32,
kExprThrow, except,
kExprElse,
kExprCatchAll,
kExprLocalGet, 0,
kExprI32Eqz,
kExprIf, kWasmStmt,

View File

@ -76,7 +76,7 @@ load("test/mjsunit/wasm/exceptions-utils.js");
.addBody([
kExprTry, kWasmStmt,
kExprUnreachable,
kExprElse,
kExprCatchAll,
kExprEnd
]).exportFunc();
builder.addFunction('unreachable_in_try_unwind', kSig_v_v)
@ -106,7 +106,7 @@ load("test/mjsunit/wasm/exceptions-utils.js");
kExprLocalGet, 0,
kExprLocalGet, 1,
kExprCallFunction, func_div.index,
kExprElse,
kExprCatchAll,
kExprI32Const, 11,
kExprEnd
]).exportFunc();
@ -141,7 +141,7 @@ load("test/mjsunit/wasm/exceptions-utils.js");
.addBody([
kExprTry, kWasmI32,
kExprCallFunction, imp,
kExprElse,
kExprCatchAll,
kExprI32Const, 11,
kExprEnd
]).exportFunc();
@ -197,7 +197,7 @@ load("test/mjsunit/wasm/exceptions-utils.js");
.addBody([
kExprTry, kWasmI32,
kExprCallFunction, imp,
kExprElse,
kExprCatchAll,
kExprI32Const, 11,
kExprEnd
]).exportFunc();
@ -1016,7 +1016,7 @@ load("test/mjsunit/wasm/exceptions-utils.js");
kExprTry, kWasmStmt,
kExprThrow, except,
kExprDelegate, 1,
kExprElse,
kExprCatchAll,
kExprEnd
]).exportFunc();
builder.addFunction('test_unwind', kSig_v_v)

View File

@ -216,6 +216,7 @@ const kWasmOpcodes = {
'Catch': 0x07,
'Throw': 0x08,
'Rethrow': 0x09,
'CatchAll': 0x19,
'Unwind': 0x0a,
'End': 0x0b,
'Br': 0x0c,

View File

@ -2872,12 +2872,17 @@ TEST_F(FunctionBodyDecoderTest, TryCatch) {
byte ex = builder.AddException(sigs.v_v());
ExpectValidates(sigs.v_v(), {WASM_TRY_OP, kExprCatch, ex, 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.
{WASM_TRY_OP, kExprCatch, ex, kExprCatchAll, kExprEnd});
ExpectFailure(sigs.v_v(),
{WASM_TRY_OP, kExprCatchAll, kExprCatch, ex, kExprEnd},
kAppendEnd, "catch after catch-all for try");
ExpectFailure(sigs.v_v(),
{WASM_TRY_OP, kExprCatchAll, kExprCatchAll, kExprEnd},
kAppendEnd, "catch-all already present for try");
ExpectFailure(sigs.v_v(), {WASM_TRY_OP, kExprEnd}, kAppendEnd,
"missing catch or catch-all in try");
ExpectFailure(sigs.v_v(), {kExprCatch, ex, kExprEnd}, kAppendEnd,
"catch does not match a try");
}
TEST_F(FunctionBodyDecoderTest, TryUnwind) {
@ -2885,16 +2890,20 @@ TEST_F(FunctionBodyDecoderTest, TryUnwind) {
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});
{WASM_TRY_OP, kExprUnwind, kExprCatch, ex, kExprEnd},
kAppendEnd, "catch after unwind for try");
ExpectFailure(sigs.v_v(), {WASM_TRY_OP, kExprCatchAll, kExprUnwind, kExprEnd},
kAppendEnd,
"catch, catch-all or unwind already present for try");
ExpectFailure(
sigs.v_v(), {WASM_TRY_OP, kExprCatch, ex, kExprUnwind, kExprEnd},
kAppendEnd, "catch, catch-all or unwind already present for try");
}
TEST_F(FunctionBodyDecoderTest, Rethrow) {
WASM_FEATURE_SCOPE(eh);
ExpectValidates(sigs.v_v(),
{WASM_TRY_OP, kExprElse, kExprRethrow, 0, kExprEnd});
{WASM_TRY_OP, kExprCatchAll, kExprRethrow, 0, kExprEnd});
ExpectFailure(sigs.v_v(),
{WASM_TRY_OP, kExprRethrow, 0, kExprCatch, kExprEnd},
kAppendEnd, "rethrow not targeting catch or catch-all");
@ -2941,7 +2950,7 @@ TEST_F(FunctionBodyDecoderTest, TryDelegate) {
kAppendEnd, "delegate does not match a try");
ExpectFailure(
sigs.v_v(),
{WASM_TRY_OP, WASM_TRY_OP, kExprElse, kExprDelegate, 1, kExprEnd},
{WASM_TRY_OP, WASM_TRY_OP, kExprCatchAll, kExprDelegate, 1, kExprEnd},
kAppendEnd, "delegate does not match a try");
}