Commit 53cddab8 authored by Thibaud Michaud's avatar Thibaud Michaud Committed by Commit Bot

[wasm] Allow polymorphic stack in the interpreter's side table

Quoting from the spec, the expected behavior for validating unreachable
code is that:

A polymorphic stack cannot underflow, but instead generates
Unknown types as needed.

(https://webassembly.github.io/spec/core/appendix/algorithm.html)

This CL changes the representation of the stack height in the
interpreter's side table builder from unsigned to signed to prevent
underflow, and makes some DCHECKs depend on code reachability.

R=clemensb@chromium.org

Bug: chromium:1017061
Change-Id: I4c999859019d6cefb76c1366ba0e98f199f7a0be
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1876813
Commit-Queue: Thibaud Michaud <thibaudm@chromium.org>
Reviewed-by: 's avatarClemens Backes <clemensb@chromium.org>
Cr-Commit-Position: refs/heads/master@{#64546}
parent bfefb6ab
...@@ -640,7 +640,7 @@ const char* OpcodeName(uint32_t val) { ...@@ -640,7 +640,7 @@ const char* OpcodeName(uint32_t val) {
return WasmOpcodes::OpcodeName(static_cast<WasmOpcode>(val)); return WasmOpcodes::OpcodeName(static_cast<WasmOpcode>(val));
} }
constexpr uint32_t kCatchInArity = 1; constexpr int32_t kCatchInArity = 1;
} // namespace } // namespace
...@@ -665,7 +665,7 @@ struct InterpreterCode { ...@@ -665,7 +665,7 @@ struct InterpreterCode {
class SideTable : public ZoneObject { class SideTable : public ZoneObject {
public: public:
ControlTransferMap map_; ControlTransferMap map_;
uint32_t max_stack_height_ = 0; int32_t max_stack_height_ = 0;
SideTable(Zone* zone, const WasmModule* module, InterpreterCode* code) SideTable(Zone* zone, const WasmModule* module, InterpreterCode* code)
: map_(zone) { : map_(zone) {
...@@ -674,7 +674,7 @@ class SideTable : public ZoneObject { ...@@ -674,7 +674,7 @@ class SideTable : public ZoneObject {
// Represents a control flow label. // Represents a control flow label.
class CLabel : public ZoneObject { class CLabel : public ZoneObject {
explicit CLabel(Zone* zone, uint32_t target_stack_height, uint32_t arity) explicit CLabel(Zone* zone, int32_t target_stack_height, uint32_t arity)
: target_stack_height(target_stack_height), : target_stack_height(target_stack_height),
arity(arity), arity(arity),
refs(zone) {} refs(zone) {}
...@@ -682,15 +682,15 @@ class SideTable : public ZoneObject { ...@@ -682,15 +682,15 @@ class SideTable : public ZoneObject {
public: public:
struct Ref { struct Ref {
const byte* from_pc; const byte* from_pc;
const uint32_t stack_height; const int32_t stack_height;
}; };
const byte* target = nullptr; const byte* target = nullptr;
uint32_t target_stack_height; int32_t target_stack_height;
// Arity when branching to this label. // Arity when branching to this label.
const uint32_t arity; const uint32_t arity;
ZoneVector<Ref> refs; ZoneVector<Ref> refs;
static CLabel* New(Zone* zone, uint32_t stack_height, uint32_t arity) { static CLabel* New(Zone* zone, int32_t stack_height, uint32_t arity) {
return new (zone) CLabel(zone, stack_height, arity); return new (zone) CLabel(zone, stack_height, arity);
} }
...@@ -701,7 +701,7 @@ class SideTable : public ZoneObject { ...@@ -701,7 +701,7 @@ class SideTable : public ZoneObject {
} }
// Reference this label from the given location. // Reference this label from the given location.
void Ref(const byte* from_pc, uint32_t stack_height) { void Ref(const byte* from_pc, int32_t stack_height) {
// Target being bound before a reference means this is a loop. // Target being bound before a reference means this is a loop.
DCHECK_IMPLIES(target, *target == kExprLoop); DCHECK_IMPLIES(target, *target == kExprLoop);
refs.push_back({from_pc, stack_height}); refs.push_back({from_pc, stack_height});
...@@ -763,7 +763,7 @@ class SideTable : public ZoneObject { ...@@ -763,7 +763,7 @@ class SideTable : public ZoneObject {
// control transfers are treated just like other branches in the resulting // control transfers are treated just like other branches in the resulting
// map. This stack contains indices into the above control stack. // map. This stack contains indices into the above control stack.
ZoneVector<size_t> exception_stack(zone); ZoneVector<size_t> exception_stack(zone);
uint32_t stack_height = 0; int32_t stack_height = 0;
uint32_t func_arity = uint32_t func_arity =
static_cast<uint32_t>(code->function->sig->return_count()); static_cast<uint32_t>(code->function->sig->return_count());
CLabel* func_label = CLabel* func_label =
...@@ -779,7 +779,7 @@ class SideTable : public ZoneObject { ...@@ -779,7 +779,7 @@ class SideTable : public ZoneObject {
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();
uint32_t exceptional_stack_height = 0; int32_t exceptional_stack_height = 0;
if (WasmOpcodes::IsPrefixOpcode(opcode)) opcode = i.prefixed_opcode(); if (WasmOpcodes::IsPrefixOpcode(opcode)) opcode = i.prefixed_opcode();
bool unreachable = control_stack.back().unreachable; bool unreachable = control_stack.back().unreachable;
if (unreachable) { if (unreachable) {
...@@ -861,7 +861,8 @@ class SideTable : public ZoneObject { ...@@ -861,7 +861,8 @@ class SideTable : public ZoneObject {
c->else_label->Finish(&map_, code->orig_start); c->else_label->Finish(&map_, code->orig_start);
stack_height = c->else_label->target_stack_height; stack_height = c->else_label->target_stack_height;
c->else_label = nullptr; c->else_label = nullptr;
DCHECK_GE(stack_height, c->end_label->target_stack_height); DCHECK_IMPLIES(!unreachable,
stack_height >= c->end_label->target_stack_height);
break; break;
} }
case kExprTry: { case kExprTry: {
...@@ -895,7 +896,8 @@ class SideTable : public ZoneObject { ...@@ -895,7 +896,8 @@ class SideTable : public ZoneObject {
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);
c->else_label = nullptr; c->else_label = nullptr;
DCHECK_GE(stack_height, c->end_label->target_stack_height); DCHECK_IMPLIES(!unreachable,
stack_height >= c->end_label->target_stack_height);
stack_height = c->end_label->target_stack_height + kCatchInArity; stack_height = c->end_label->target_stack_height + kCatchInArity;
break; break;
} }
...@@ -906,7 +908,7 @@ class SideTable : public ZoneObject { ...@@ -906,7 +908,7 @@ class SideTable : public ZoneObject {
DCHECK_EQ(0, imm.index.exception->sig->return_count()); DCHECK_EQ(0, imm.index.exception->sig->return_count());
size_t params = imm.index.exception->sig->parameter_count(); size_t params = imm.index.exception->sig->parameter_count();
// Taken branches pop the exception and push the encoded values. // Taken branches pop the exception and push the encoded values.
uint32_t height = stack_height - 1 + static_cast<uint32_t>(params); int32_t height = stack_height - 1 + static_cast<int32_t>(params);
TRACE("control @%u: BrOnExn[depth=%u]\n", i.pc_offset(), depth); TRACE("control @%u: BrOnExn[depth=%u]\n", i.pc_offset(), depth);
Control* c = &control_stack[control_stack.size() - depth - 1]; Control* c = &control_stack[control_stack.size() - depth - 1];
if (!unreachable) c->end_label->Ref(i.pc(), height); if (!unreachable) c->end_label->Ref(i.pc(), height);
...@@ -922,7 +924,8 @@ class SideTable : public ZoneObject { ...@@ -922,7 +924,8 @@ class SideTable : public ZoneObject {
c->end_label->Bind(i.pc() + 1); c->end_label->Bind(i.pc() + 1);
} }
c->Finish(&map_, code->orig_start); c->Finish(&map_, code->orig_start);
DCHECK_GE(stack_height, c->end_label->target_stack_height); DCHECK_IMPLIES(!unreachable,
stack_height >= c->end_label->target_stack_height);
stack_height = c->end_label->target_stack_height + c->exit_arity; stack_height = c->end_label->target_stack_height + c->exit_arity;
control_stack.pop_back(); control_stack.pop_back();
break; break;
......
...@@ -401,6 +401,45 @@ load("test/mjsunit/wasm/wasm-module-builder.js"); ...@@ -401,6 +401,45 @@ load("test/mjsunit/wasm/wasm-module-builder.js");
assertEquals(instance.exports.main(), [1, 2]); assertEquals(instance.exports.main(), [1, 2]);
})(); })();
(function MultiUnreachablePolymorphicTest() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
let sig_v_i = builder.addType(kSig_v_i);
let sig_i_i = builder.addType(kSig_i_i);
builder.addFunction("block", kSig_v_v)
.addBody([
kExprReturn,
kExprBlock, sig_v_i,
kExprDrop,
kExprEnd
])
.exportAs("block");
builder.addFunction("if_else", kSig_v_v)
.addBody([
kExprReturn,
kExprIf, sig_v_i,
kExprDrop,
kExprElse,
kExprDrop,
kExprEnd
])
.exportAs("if_else");
builder.addFunction("loop", kSig_v_v)
.addBody([
kExprReturn,
kExprLoop, sig_i_i,
kExprEnd,
kExprDrop
])
.exportAs("loop");
// TODO(thibaudm): Create eh + mv mjsunit test and add try/catch case.
let instance = builder.instantiate();
instance.exports.block();
instance.exports.if_else();
instance.exports.loop();
})();
(function MultiWasmToJSReturnTest() { (function MultiWasmToJSReturnTest() {
print(arguments.callee.name); print(arguments.callee.name);
let builder = new WasmModuleBuilder(); let builder = new WasmModuleBuilder();
......
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