Commit ff2cf7da authored by Thibaud Michaud's avatar Thibaud Michaud Committed by V8 LUCI CQ

[wasm][eh] Remove unwind instruction

Relevant links:
https://github.com/WebAssembly/exception-handling/issues/153
https://github.com/WebAssembly/exception-handling/pull/156

R=clemensb@chromium.org

Bug: v8:8091
Change-Id: I0deeb9665c6648e643d0aa4f310b7676e1c2fa32
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2959624
Commit-Queue: Thibaud Michaud <thibaudm@chromium.org>
Reviewed-by: 's avatarClemens Backes <clemensb@chromium.org>
Cr-Commit-Position: refs/heads/master@{#75135}
parent 659424f8
......@@ -1226,8 +1226,7 @@ class LiftoffCompiler {
}
void CatchAll(FullDecoder* decoder, Control* block) {
DCHECK(block->is_try_catchall() || block->is_try_catch() ||
block->is_try_unwind());
DCHECK(block->is_try_catchall() || block->is_try_catch());
DCHECK_EQ(decoder->control_at(0), block);
// The catch block is unreachable if no possible throws in the try block
......@@ -1333,7 +1332,7 @@ class LiftoffCompiler {
}
void FinishTry(FullDecoder* decoder, Control* c) {
DCHECK(c->is_try_catch() || c->is_try_catchall() || c->is_try_unwind());
DCHECK(c->is_try_catch() || c->is_try_catchall());
if (!c->end_merge.reached) {
if (c->try_info->catch_reached) {
// Drop the implicit exception ref.
......@@ -1357,8 +1356,7 @@ class LiftoffCompiler {
if (c->is_onearmed_if()) {
// Special handling for one-armed ifs.
FinishOneArmedIf(decoder, c);
} else if (c->is_try_catch() || c->is_try_catchall() ||
c->is_try_unwind()) {
} else if (c->is_try_catch() || c->is_try_catchall()) {
FinishTry(decoder, c);
} else if (c->end_merge.reached) {
// There is a merge already. Merge our state into that, then continue with
......@@ -3128,8 +3126,7 @@ class LiftoffCompiler {
? next_control->stack_depth + __ num_locals() +
next_control->num_exceptions
: __ cache_state()->stack_height();
bool exception = control->is_try_catch() || control->is_try_catchall() ||
control->is_try_unwind();
bool exception = control->is_try_catch() || control->is_try_catchall();
for (; index < end_index; ++index) {
auto& slot = stack_state[index];
auto& value = values[index];
......
......@@ -843,7 +843,6 @@ enum ControlKind : uint8_t {
kControlTry,
kControlTryCatch,
kControlTryCatchAll,
kControlTryUnwind
};
enum Reachability : uint8_t {
......@@ -905,10 +904,8 @@ 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() ||
is_try_unwind();
return is_incomplete_try() || is_try_catch() || is_try_catchall();
}
Merge<Value>* br_merge() {
......@@ -1629,7 +1626,6 @@ class WasmDecoder : public Decoder {
case kExprDrop:
case kExprSelect:
case kExprCatchAll:
case kExprUnwind:
return 1;
case kExprSelectWithType: {
SelectTypeImmediate<validate> imm(WasmFeatures::All(), decoder, pc + 1,
......@@ -1972,7 +1968,6 @@ class WasmDecoder : public Decoder {
case kExprCatch:
case kExprCatchAll:
case kExprDelegate:
case kExprUnwind:
case kExprRethrow:
case kExprNop:
case kExprNopForTestingUnsupportedInLiftoff:
......@@ -2328,7 +2323,6 @@ class WasmFullDecoder : public WasmDecoder<validate> {
case kControlIfElse:
case kControlTryCatch:
case kControlTryCatchAll:
case kControlTryUnwind:
case kControlLet: // TODO(7748): Implement
break;
}
......@@ -2452,10 +2446,6 @@ 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;
}
FallThrough();
c->kind = kControlTryCatch;
// TODO(jkummerow): Consider moving the stack manipulation after the
......@@ -2492,8 +2482,7 @@ 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() ||
target->is_try_unwind()) {
if (target->is_try_catch() || target->is_try_catchall()) {
this->DecodeError(
"cannot delegate inside the catch handler of the target");
return 0;
......@@ -2518,10 +2507,6 @@ class WasmFullDecoder : public WasmDecoder<validate> {
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;
}
FallThrough();
c->kind = kControlTryCatchAll;
c->reachability = control_at(1)->innerReachability();
......@@ -2532,29 +2517,6 @@ class WasmFullDecoder : public WasmDecoder<validate> {
return 1;
}
DECODE(Unwind) {
CHECK_PROTOTYPE_OPCODE(eh);
DCHECK(!control_.empty());
Control* c = &control_.back();
if (!VALIDATE(c->is_try())) {
this->DecodeError("unwind does not match a try");
return 0;
}
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;
}
FallThrough();
c->kind = kControlTryUnwind;
c->reachability = control_at(1)->innerReachability();
current_catch_ = c->previous_catch; // Pop try scope.
CALL_INTERFACE_IF_OK_AND_PARENT_REACHABLE(CatchAll, c);
stack_end_ = stack_ + c->stack_depth;
current_code_reachable_and_ok_ = this->ok() && c->reachable();
return 1;
}
DECODE(BrOnNull) {
CHECK_PROTOTYPE_OPCODE(typed_funcref);
BranchDepthImmediate<validate> imm(this, this->pc_ + 1);
......@@ -2738,11 +2700,6 @@ class WasmFullDecoder : public WasmDecoder<validate> {
if (c->is_onearmed_if()) {
if (!VALIDATE(TypeCheckOneArmedIf(c))) return 0;
}
if (c->is_try_unwind()) {
// Unwind implicitly rethrows at the end.
CALL_INTERFACE_IF_OK_AND_REACHABLE(Rethrow, c);
EndControl();
}
if (c->is_let()) {
CALL_INTERFACE_IF_OK_AND_REACHABLE(DeallocateLocals, c->locals_count);
......@@ -3344,7 +3301,6 @@ class WasmFullDecoder : public WasmDecoder<validate> {
DECODE_IMPL(Catch);
DECODE_IMPL(Delegate);
DECODE_IMPL(CatchAll);
DECODE_IMPL(Unwind);
DECODE_IMPL(BrOnNull);
DECODE_IMPL(BrOnNonNull);
DECODE_IMPL(Let);
......
......@@ -191,7 +191,7 @@ bool PrintRawWasmCode(AccountingAllocator* allocator, const FunctionBody& body,
}
if (line_numbers) line_numbers->push_back(i.position());
if (opcode == kExprElse || opcode == kExprCatch ||
opcode == kExprCatchAll || opcode == kExprUnwind) {
opcode == kExprCatchAll) {
control_depth--;
}
......@@ -241,7 +241,6 @@ bool PrintRawWasmCode(AccountingAllocator* allocator, const FunctionBody& body,
case kExprElse:
case kExprCatch:
case kExprCatchAll:
case kExprUnwind:
os << " @" << i.pc_offset();
control_depth++;
break;
......
......@@ -721,8 +721,7 @@ class WasmGraphBuildingInterface {
}
void Rethrow(FullDecoder* decoder, Control* block) {
DCHECK(block->is_try_catchall() || block->is_try_catch() ||
block->is_try_unwind());
DCHECK(block->is_try_catchall() || block->is_try_catch());
TFNode* exception = block->try_info->exception;
DCHECK_NOT_NULL(exception);
CheckForException(decoder, builder_->Rethrow(exception));
......@@ -808,8 +807,7 @@ class WasmGraphBuildingInterface {
}
void CatchAll(FullDecoder* decoder, Control* block) {
DCHECK(block->is_try_catchall() || block->is_try_catch() ||
block->is_try_unwind());
DCHECK(block->is_try_catchall() || block->is_try_catch());
DCHECK_EQ(decoder->control_at(0), block);
// The catch block is unreachable if no possible throws in the try block
......
......@@ -197,7 +197,6 @@ constexpr const char* WasmOpcodes::OpcodeName(WasmOpcode opcode) {
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")
......
......@@ -42,7 +42,6 @@ bool V8_EXPORT_PRIVATE IsJSCompatibleSignature(const FunctionSig* sig,
V(Catch, 0x07, _ /* eh_prototype */) \
V(Throw, 0x08, _ /* eh_prototype */) \
V(Rethrow, 0x09, _ /* eh_prototype */) \
V(Unwind, 0x0a, _ /* eh_prototype */) \
V(End, 0x0b, _) \
V(Br, 0x0c, _) \
V(BrIf, 0x0d, _) \
......
......@@ -211,43 +211,11 @@ WASM_EXEC_TEST(TryDelegate) {
}
}
WASM_EXEC_TEST(TryUnwind) {
TestSignatures sigs;
EXPERIMENTAL_FLAG_SCOPE(eh);
WasmRunner<uint32_t, uint32_t> r(execution_tier);
byte except = r.builder().AddException(sigs.v_v());
constexpr uint32_t kResult0 = 23;
constexpr uint32_t kResult1 = 42;
// Build the main test function.
BUILD(r, WASM_TRY_CATCH_T(
kWasmI32,
WASM_TRY_UNWIND_T(
kWasmI32,
WASM_TRY_DELEGATE_T(
kWasmI32,
WASM_STMTS(WASM_I32V(kResult1),
WASM_IF(WASM_I32_EQZ(WASM_LOCAL_GET(0)),
WASM_THROW(except))),
0),
kExprNop),
WASM_I32V(kResult0), except));
if (execution_tier != TestExecutionTier::kInterpreter) {
// Need to call through JS to allow for creation of stack traces.
r.CheckCallViaJS(kResult0, 0);
r.CheckCallViaJS(kResult1, 1);
} else {
CHECK_EQ(kResult0, r.CallInterpreter(0));
CHECK_EQ(kResult1, r.CallInterpreter(1));
}
}
WASM_EXEC_TEST(TestCatchlessTry) {
TestSignatures sigs;
EXPERIMENTAL_FLAG_SCOPE(eh);
WasmRunner<uint32_t> r(execution_tier);
uint32_t except = r.builder().AddException(sigs.v_i());
byte except = r.builder().AddException(sigs.v_i());
BUILD(r,
WASM_TRY_CATCH_T(
kWasmI32,
......@@ -706,17 +674,16 @@ TEST(Regress1186795) {
EXPERIMENTAL_FLAG_SCOPE(eh);
WasmRunner<uint32_t> r(TestExecutionTier::kInterpreter);
byte except = r.builder().AddException(sigs.v_i());
BUILD(r, WASM_TRY_CATCH_T(
kWasmI32,
WASM_STMTS(
WASM_I32V(0), WASM_I32V(0), WASM_I32V(0), WASM_I32V(0),
WASM_I32V(0), WASM_I32V(0), WASM_I32V(0),
WASM_TRY_UNWIND_T(
kWasmI32, WASM_STMTS(WASM_I32V(0), WASM_THROW(except)),
WASM_I32V(0)),
WASM_DROP, WASM_DROP, WASM_DROP, WASM_DROP, WASM_DROP,
WASM_DROP, WASM_DROP),
WASM_NOP, except));
BUILD(r,
WASM_TRY_CATCH_T(
kWasmI32,
WASM_STMTS(WASM_I32V(0), WASM_I32V(0), WASM_I32V(0), WASM_I32V(0),
WASM_I32V(0), WASM_I32V(0), WASM_I32V(0),
WASM_TRY_T(kWasmI32,
WASM_STMTS(WASM_I32V(0), WASM_THROW(except))),
WASM_DROP, WASM_DROP, WASM_DROP, WASM_DROP, WASM_DROP,
WASM_DROP, WASM_DROP),
WASM_NOP, except));
CHECK_EQ(0, r.CallInterpreter());
}
......
......@@ -690,9 +690,6 @@ class SideTable : public ZoneObject {
// Track whether this block was already left, i.e. all further
// instructions are unreachable.
bool unreachable = false;
// Whether this is a try...unwind...end block. Needed to handle the
// implicit rethrow when we reach the end of the block.
bool unwind = false;
Control(const byte* pc, CLabel* end_label, CLabel* else_label,
uint32_t exit_arity)
......@@ -865,27 +862,6 @@ class SideTable : public ZoneObject {
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();
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;
c->unwind = true;
DCHECK_IMPLIES(!unreachable,
stack_height >= c->end_label->target_stack_height);
stack_height = c->end_label->target_stack_height;
break;
}
case kExprTry: {
BlockTypeImmediate<Decoder::kNoValidation> imm(
WasmFeatures::All(), &i, i.pc() + 1, module);
......@@ -968,13 +944,6 @@ class SideTable : public ZoneObject {
stack_height = c->else_label->target_stack_height;
rethrow = !unreachable;
}
} else if (c->unwind) {
DCHECK_EQ(*c->pc, kExprTry);
rethrow_map_.emplace(i.pc() - i.start(),
static_cast<int>(control_stack.size()) - 1);
if (!exception_stack.empty()) {
rethrow = !unreachable;
}
}
c->end_label->Bind(i.pc() + 1);
}
......@@ -3415,7 +3384,6 @@ class WasmInterpreterInternals {
break;
}
case kExprElse:
case kExprUnwind:
case kExprCatch:
case kExprCatchAll: {
len = LookupTargetDelta(code, pc);
......@@ -3512,19 +3480,6 @@ class WasmInterpreterInternals {
break;
}
case kExprEnd: {
if (code->side_table->rethrow_map_.count(pc)) {
// Implicit rethrow after unwind.
HandleScope scope(isolate_);
DCHECK(!frames_.back().caught_exception_stack.is_null());
int index = code->side_table->rethrow_map_[pc];
Handle<Object> exception = handle(
frames_.back().caught_exception_stack->get(index), isolate_);
DCHECK(!exception->IsTheHole());
CommitPc(pc); // Needed for local unwinding.
if (!DoRethrowException(exception)) return;
ReloadFromFrameOnException(&decoder, &code, &pc, &limit);
continue; // Do not bump pc.
}
break;
}
case kExprI32Const: {
......
......@@ -199,9 +199,6 @@
#define WASM_TRY_DELEGATE_T(t, trystmt, depth) \
kExprTry, static_cast<byte>((t).value_type_code()), trystmt, kExprDelegate, \
depth
#define WASM_TRY_UNWIND_T(t, trystmt, unwindstmt) \
kExprTry, static_cast<byte>((t).value_type_code()), trystmt, kExprUnwind, \
unwindstmt, kExprEnd
#define WASM_SELECT(tval, fval, cond) tval, fval, cond, kExprSelect
#define WASM_SELECT_I(tval, fval, cond) \
......
......@@ -215,7 +215,6 @@ class WasmGenerator {
// Allow one more target than there are enclosing try blocks, for delegating
// to the caller.
uint8_t delegate_target = data->get<uint8_t>() % (try_blocks_.size() + 1);
bool is_unwind = num_catch == 0 && !has_catch_all && !is_delegate;
Vector<const ValueType> return_type_vec = return_type.kind() == kVoid
? Vector<ValueType>{}
......@@ -249,10 +248,6 @@ class WasmGenerator {
builder_->EmitWithU32V(kExprDelegate, delegate_depth);
}
catch_blocks_.pop_back();
if (is_unwind) {
builder_->Emit(kExprUnwind);
Generate(return_type, data);
}
}
template <ValueKind T>
......
......@@ -79,17 +79,9 @@ d8.file.execute("test/mjsunit/wasm/exceptions-utils.js");
kExprCatchAll,
kExprEnd
]).exportFunc();
builder.addFunction('unreachable_in_try_unwind', kSig_v_v)
.addBody([
kExprTry, kWasmVoid,
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() {
......@@ -110,21 +102,10 @@ d8.file.execute("test/mjsunit/wasm/exceptions-utils.js");
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();
let instance = builder.instantiate();
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() {
......@@ -145,14 +126,6 @@ d8.file.execute("test/mjsunit/wasm/exceptions-utils.js");
kExprI32Const, 11,
kExprEnd
]).exportFunc();
builder.addFunction('call_import_unwind', kSig_i_v)
.addBody([
kExprTry, kWasmI32,
kExprCallFunction, imp,
kExprUnwind,
kExprI32Const, 11,
kExprEnd
]).exportFunc();
let exception = undefined;
let instance;
function js_import() {
......@@ -175,18 +148,6 @@ d8.file.execute("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() {
......@@ -201,21 +162,12 @@ d8.file.execute("test/mjsunit/wasm/exceptions-utils.js");
kExprI32Const, 11,
kExprEnd
]).exportFunc();
builder.addFunction('call_import_unwind', kSig_i_v)
.addBody([
kExprTry, kWasmI32,
kExprCallFunction, imp,
kExprUnwind,
kExprI32Const, 11,
kExprEnd
]).exportFunc();
function throw_exc() {
throw new WebAssembly.RuntimeError('My user text');
}
let instance = builder.instantiate({imp: {ort: throw_exc}});
assertEquals(11, instance.exports.call_import());
assertThrows(instance.exports.call_import_unwind, WebAssembly.RuntimeError, "My user text");
})();
(function TestExnWithWasmProtoNotCaught() {
......@@ -1041,18 +993,8 @@ d8.file.execute("test/mjsunit/wasm/exceptions-utils.js");
kExprCatchAll,
kExprEnd
]).exportFunc();
builder.addFunction('test_unwind', kSig_v_v)
.addBody([
kExprTry, kWasmVoid,
kExprTry, kWasmVoid,
kExprThrow, except,
kExprDelegate, 1,
kExprUnwind,
kExprEnd
]).exportFunc();
instance = builder.instantiate();
assertTraps(WebAssembly.RuntimeError, () => instance.exports.test());
assertTraps(WebAssembly.RuntimeError, () => instance.exports.test_unwind());
})();
(function TestThrowBeforeUnreachable() {
......
......@@ -228,7 +228,6 @@ const kWasmOpcodes = {
'Throw': 0x08,
'Rethrow': 0x09,
'CatchAll': 0x19,
'Unwind': 0x0a,
'End': 0x0b,
'Br': 0x0c,
'BrIf': 0x0d,
......
......@@ -2967,21 +2967,6 @@ TEST_F(FunctionBodyDecoderTest, TryCatch) {
"catch does not match a 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},
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(),
......@@ -2993,9 +2978,6 @@ TEST_F(FunctionBodyDecoderTest, Rethrow) {
"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) {
......@@ -3021,11 +3003,6 @@ TEST_F(FunctionBodyDecoderTest, TryDelegate) {
WASM_TRY_DELEGATE(WASM_STMTS(kExprThrow, ex), 0), kExprEnd},
kAppendEnd,
"cannot delegate inside the catch handler of the target");
ExpectFailure(sigs.v_v(),
{WASM_TRY_OP, kExprUnwind,
WASM_TRY_DELEGATE(WASM_STMTS(kExprThrow, ex), 0), kExprEnd},
kAppendEnd,
"cannot delegate inside the catch handler of the target");
ExpectFailure(
sigs.v_v(),
{WASM_BLOCK(WASM_TRY_OP, WASM_TRY_DELEGATE(WASM_STMTS(kExprThrow, ex), 3),
......
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