Commit 4423c9cc authored by Clemens Hammacher's avatar Clemens Hammacher Committed by Commit Bot

[wasm] [interpreter] Ignore stack effects after unreachable

During computation of the side table, ignore stack effects of
instructions following any unconditional jump in the same block
(|unreachable|, |br|, |br_table| or |return| jump out of the block).
Without this fix, the current stack height might underflow, or we compute an
unnecessarily large max_stack_height_. Note that those instruction will
never get executed anyway.
Hence, we don't need to store any side table information for such
unreachable code.

R=rossberg@chromium.org
BUG=chromium:716936, chromium:715990

Change-Id: I282f7f18ba1b972a112210e692f6cd05cf32308c
Reviewed-on: https://chromium-review.googlesource.com/493266Reviewed-by: 's avatarAndreas Rossberg <rossberg@chromium.org>
Commit-Queue: Clemens Hammacher <clemensh@chromium.org>
Cr-Commit-Position: refs/heads/master@{#45059}
parent cacd618e
...@@ -744,6 +744,9 @@ class SideTable : public ZoneObject { ...@@ -744,6 +744,9 @@ class SideTable : public ZoneObject {
// Arity (number of values on the stack) when exiting this control // Arity (number of values on the stack) when exiting this control
// structure via |end|. // structure via |end|.
uint32_t exit_arity; uint32_t exit_arity;
// Track whether this block was already left, i.e. all further
// instructions are unreachable.
bool unreachable = false;
Control(const byte* pc, CLabel* end_label, CLabel* else_label, Control(const byte* pc, CLabel* end_label, CLabel* else_label,
uint32_t exit_arity) uint32_t exit_arity)
...@@ -772,19 +775,32 @@ class SideTable : public ZoneObject { ...@@ -772,19 +775,32 @@ class SideTable : public ZoneObject {
CLabel* func_label = CLabel* func_label =
CLabel::New(&control_transfer_zone, stack_height, func_arity); CLabel::New(&control_transfer_zone, stack_height, func_arity);
control_stack.emplace_back(code->orig_start, func_label, func_arity); control_stack.emplace_back(code->orig_start, func_label, func_arity);
auto control_parent = [&]() -> Control& {
DCHECK_LE(2, control_stack.size());
return control_stack[control_stack.size() - 2];
};
auto copy_unreachable = [&] {
control_stack.back().unreachable = control_parent().unreachable;
};
for (BytecodeIterator i(code->orig_start, code->orig_end, &code->locals); for (BytecodeIterator i(code->orig_start, code->orig_end, &code->locals);
i.has_next(); i.next()) { i.has_next(); i.next()) {
WasmOpcode opcode = i.current(); WasmOpcode opcode = i.current();
auto stack_effect = bool unreachable = control_stack.back().unreachable;
StackEffect(module, code->function->sig, i.pc(), i.end()); if (unreachable) {
TRACE("@%u: control %s (sp %d - %d + %d)\n", i.pc_offset(), TRACE("@%u: %s (is unreachable)\n", i.pc_offset(),
WasmOpcodes::OpcodeName(opcode), stack_height, stack_effect.first, WasmOpcodes::OpcodeName(opcode));
stack_effect.second); } else {
DCHECK_GE(stack_height, stack_effect.first); auto stack_effect =
DCHECK_GE(kMaxUInt32, static_cast<uint64_t>(stack_height) - StackEffect(module, code->function->sig, i.pc(), i.end());
stack_effect.first + stack_effect.second); TRACE("@%u: %s (sp %d - %d + %d)\n", i.pc_offset(),
stack_height = stack_height - stack_effect.first + stack_effect.second; WasmOpcodes::OpcodeName(opcode), stack_height, stack_effect.first,
if (stack_height > max_stack_height_) max_stack_height_ = stack_height; stack_effect.second);
DCHECK_GE(stack_height, stack_effect.first);
DCHECK_GE(kMaxUInt32, static_cast<uint64_t>(stack_height) -
stack_effect.first + stack_effect.second);
stack_height = stack_height - stack_effect.first + stack_effect.second;
if (stack_height > max_stack_height_) max_stack_height_ = stack_height;
}
switch (opcode) { switch (opcode) {
case kExprBlock: case kExprBlock:
case kExprLoop: { case kExprLoop: {
...@@ -795,6 +811,7 @@ class SideTable : public ZoneObject { ...@@ -795,6 +811,7 @@ class SideTable : public ZoneObject {
CLabel* label = CLabel::New(&control_transfer_zone, stack_height, CLabel* label = CLabel::New(&control_transfer_zone, stack_height,
is_loop ? 0 : operand.arity); is_loop ? 0 : operand.arity);
control_stack.emplace_back(i.pc(), label, operand.arity); control_stack.emplace_back(i.pc(), label, operand.arity);
copy_unreachable();
if (is_loop) label->Bind(i.pc()); if (is_loop) label->Bind(i.pc());
break; break;
} }
...@@ -807,13 +824,17 @@ class SideTable : public ZoneObject { ...@@ -807,13 +824,17 @@ class SideTable : public ZoneObject {
CLabel::New(&control_transfer_zone, stack_height, 0); CLabel::New(&control_transfer_zone, stack_height, 0);
control_stack.emplace_back(i.pc(), end_label, else_label, control_stack.emplace_back(i.pc(), end_label, else_label,
operand.arity); operand.arity);
else_label->Ref(i.pc(), stack_height); copy_unreachable();
if (!unreachable) else_label->Ref(i.pc(), stack_height);
break; break;
} }
case kExprElse: { case kExprElse: {
Control* c = &control_stack.back(); Control* c = &control_stack.back();
copy_unreachable();
TRACE("control @%u: Else\n", i.pc_offset()); TRACE("control @%u: Else\n", i.pc_offset());
c->end_label->Ref(i.pc(), stack_height); if (!control_parent().unreachable) {
c->end_label->Ref(i.pc(), stack_height);
}
DCHECK_NOT_NULL(c->else_label); DCHECK_NOT_NULL(c->else_label);
c->else_label->Bind(i.pc() + 1); c->else_label->Bind(i.pc() + 1);
c->else_label->Finish(&map_, code->orig_start); c->else_label->Finish(&map_, code->orig_start);
...@@ -841,14 +862,14 @@ class SideTable : public ZoneObject { ...@@ -841,14 +862,14 @@ class SideTable : public ZoneObject {
BreakDepthOperand<false> operand(&i, i.pc()); BreakDepthOperand<false> operand(&i, i.pc());
TRACE("control @%u: Br[depth=%u]\n", i.pc_offset(), operand.depth); TRACE("control @%u: Br[depth=%u]\n", i.pc_offset(), operand.depth);
Control* c = &control_stack[control_stack.size() - operand.depth - 1]; Control* c = &control_stack[control_stack.size() - operand.depth - 1];
c->end_label->Ref(i.pc(), stack_height); if (!unreachable) c->end_label->Ref(i.pc(), stack_height);
break; break;
} }
case kExprBrIf: { case kExprBrIf: {
BreakDepthOperand<false> operand(&i, i.pc()); BreakDepthOperand<false> operand(&i, i.pc());
TRACE("control @%u: BrIf[depth=%u]\n", i.pc_offset(), operand.depth); TRACE("control @%u: BrIf[depth=%u]\n", i.pc_offset(), operand.depth);
Control* c = &control_stack[control_stack.size() - operand.depth - 1]; Control* c = &control_stack[control_stack.size() - operand.depth - 1];
c->end_label->Ref(i.pc(), stack_height); if (!unreachable) c->end_label->Ref(i.pc(), stack_height);
break; break;
} }
case kExprBrTable: { case kExprBrTable: {
...@@ -856,17 +877,21 @@ class SideTable : public ZoneObject { ...@@ -856,17 +877,21 @@ class SideTable : public ZoneObject {
BranchTableIterator<false> iterator(&i, operand); BranchTableIterator<false> iterator(&i, operand);
TRACE("control @%u: BrTable[count=%u]\n", i.pc_offset(), TRACE("control @%u: BrTable[count=%u]\n", i.pc_offset(),
operand.table_count); operand.table_count);
while (iterator.has_next()) { if (!unreachable) {
uint32_t j = iterator.cur_index(); while (iterator.has_next()) {
uint32_t target = iterator.next(); uint32_t j = iterator.cur_index();
Control* c = &control_stack[control_stack.size() - target - 1]; uint32_t target = iterator.next();
c->end_label->Ref(i.pc() + j, stack_height); Control* c = &control_stack[control_stack.size() - target - 1];
c->end_label->Ref(i.pc() + j, stack_height);
}
} }
break; break;
} }
default: { default:
break; break;
} }
if (WasmOpcodes::IsUnconditionalJump(opcode)) {
control_stack.back().unreachable = true;
} }
} }
DCHECK_EQ(0, control_stack.size()); DCHECK_EQ(0, control_stack.size());
......
...@@ -286,6 +286,7 @@ bool WasmOpcodes::IsPrefixOpcode(WasmOpcode opcode) { ...@@ -286,6 +286,7 @@ bool WasmOpcodes::IsPrefixOpcode(WasmOpcode opcode) {
return false; return false;
} }
} }
bool WasmOpcodes::IsControlOpcode(WasmOpcode opcode) { bool WasmOpcodes::IsControlOpcode(WasmOpcode opcode) {
switch (opcode) { switch (opcode) {
#define CHECK_OPCODE(name, opcode, _) \ #define CHECK_OPCODE(name, opcode, _) \
...@@ -298,6 +299,18 @@ bool WasmOpcodes::IsControlOpcode(WasmOpcode opcode) { ...@@ -298,6 +299,18 @@ bool WasmOpcodes::IsControlOpcode(WasmOpcode opcode) {
} }
} }
bool WasmOpcodes::IsUnconditionalJump(WasmOpcode opcode) {
switch (opcode) {
case kExprUnreachable:
case kExprBr:
case kExprBrTable:
case kExprReturn:
return true;
default:
return false;
}
}
std::ostream& operator<<(std::ostream& os, const FunctionSig& sig) { std::ostream& operator<<(std::ostream& os, const FunctionSig& sig) {
if (sig.return_count() == 0) os << "v"; if (sig.return_count() == 0) os << "v";
for (auto ret : sig.returns()) { for (auto ret : sig.returns()) {
......
...@@ -598,6 +598,9 @@ class V8_EXPORT_PRIVATE WasmOpcodes { ...@@ -598,6 +598,9 @@ class V8_EXPORT_PRIVATE WasmOpcodes {
static FunctionSig* AtomicSignature(WasmOpcode opcode); static FunctionSig* AtomicSignature(WasmOpcode opcode);
static bool IsPrefixOpcode(WasmOpcode opcode); static bool IsPrefixOpcode(WasmOpcode opcode);
static bool IsControlOpcode(WasmOpcode opcode); static bool IsControlOpcode(WasmOpcode opcode);
// Check whether the given opcode always jumps, i.e. all instructions after
// this one in the current block are dead. Returns false for |end|.
static bool IsUnconditionalJump(WasmOpcode opcode);
static int TrapReasonToMessageId(TrapReason reason); static int TrapReasonToMessageId(TrapReason reason);
static const char* TrapReasonMessage(TrapReason reason); static const char* TrapReasonMessage(TrapReason reason);
......
...@@ -3007,3 +3007,46 @@ WASM_EXEC_TEST(LoopsWithValues) { ...@@ -3007,3 +3007,46 @@ WASM_EXEC_TEST(LoopsWithValues) {
BUILD(r, WASM_LOOP_I(WASM_LOOP_I(WASM_ONE), WASM_ONE, kExprI32Add)); BUILD(r, WASM_LOOP_I(WASM_LOOP_I(WASM_ONE), WASM_ONE, kExprI32Add));
CHECK_EQ(2, r.Call()); CHECK_EQ(2, r.Call());
} }
WASM_EXEC_TEST(InvalidStackAfterUnreachable) {
WasmRunner<int32_t> r(execution_mode);
BUILD(r, kExprUnreachable, kExprI32Add);
CHECK_TRAP32(r.Call());
}
WASM_EXEC_TEST(InvalidStackAfterBr) {
WasmRunner<int32_t> r(execution_mode);
BUILD(r, WASM_BRV(0, WASM_I32V_1(27)), kExprI32Add);
CHECK_EQ(27, r.Call());
}
WASM_EXEC_TEST(InvalidStackAfterReturn) {
WasmRunner<int32_t> r(execution_mode);
BUILD(r, WASM_RETURN1(WASM_I32V_1(17)), kExprI32Add);
CHECK_EQ(17, r.Call());
}
WASM_EXEC_TEST(BranchOverUnreachableCode) {
WasmRunner<int32_t> r(execution_mode);
BUILD(r,
// Start a block which breaks in the middle (hence unreachable code
// afterwards) and continue execution after this block.
WASM_BLOCK_I(WASM_BRV(0, WASM_I32V_1(17)), kExprI32Add),
// Add one to the 17 returned from the block.
WASM_ONE, kExprI32Add);
CHECK_EQ(18, r.Call());
}
WASM_EXEC_TEST(BlockInsideUnreachable) {
WasmRunner<int32_t> r(execution_mode);
BUILD(r, WASM_RETURN1(WASM_I32V_1(17)), WASM_BLOCK(WASM_BR(0)));
CHECK_EQ(17, r.Call());
}
WASM_EXEC_TEST(IfInsideUnreachable) {
WasmRunner<int32_t> r(execution_mode);
BUILD(
r, WASM_RETURN1(WASM_I32V_1(17)),
WASM_IF_ELSE_I(WASM_ONE, WASM_BRV(0, WASM_ONE), WASM_RETURN1(WASM_ONE)));
CHECK_EQ(17, r.Call());
}
...@@ -489,14 +489,41 @@ TEST_F(ControlTransferTest, BiggerSpDiffs) { ...@@ -489,14 +489,41 @@ TEST_F(ControlTransferTest, BiggerSpDiffs) {
0, // @7 0, // @7
kExprI32Const, // @8 kExprI32Const, // @8
0, // @9 0, // @9
kExprBr, // @10 kExprI32Const, // @10
0, // @11 0, // @11
kExprBr, // @12 kExprBrIf, // @12
1, // @13 0, // @13
kExprEnd, // @14 kExprBr, // @14
kExprEnd // @15 1, // @15
kExprEnd, // @16
kExprEnd // @17
}; };
CheckTransfers(code, {{10, 5, 2, 0}, {12, 4, 3, 1}}); CheckTransfers(code, {{12, 5, 2, 0}, {14, 4, 3, 1}});
}
TEST_F(ControlTransferTest, NoInfoForUnreachableCode) {
byte code[] = {
kExprBlock, // @0
kLocalVoid, // @1
kExprBr, // @2
0, // @3
kExprBr, // @4 -- no control transfer entry!
1, // @5
kExprEnd, // @6
kExprBlock, // @7
kLocalVoid, // @8
kExprUnreachable, // @9
kExprI32Const, // @10
0, // @11
kExprIf, // @12 -- no control transfer entry!
kLocalVoid, // @13
kExprBr, // @14 -- no control transfer entry!
0, // @15
kExprElse, // @16 -- no control transfer entry!
kExprEnd, // @17
kExprEnd // @18
};
CheckTransfers(code, {{2, 5, 0, 0}});
} }
} // namespace wasm } // namespace wasm
......
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