Commit bcfa4003 authored by titzer's avatar titzer Committed by Commit bot

[wasm] Remove TableSwitch and replace with br_table.

R=rossberg@chromium.org,binji@chromium.org,bradnelson@chromium.org
BUG=

Review URL: https://codereview.chromium.org/1764723002

Cr-Commit-Position: refs/heads/master@{#34511}
parent 38e2699c
......@@ -195,26 +195,14 @@ class WasmDecoder : public Decoder {
return false;
}
bool Validate(const byte* pc, TableSwitchOperand& operand,
bool Validate(const byte* pc, BranchTableOperand& operand,
size_t block_depth) {
if (operand.table_count == 0) {
error(pc, "tableswitch with 0 entries");
return false;
}
// Verify table.
for (uint32_t i = 0; i < operand.table_count; i++) {
for (uint32_t i = 0; i < operand.table_count + 1; i++) {
uint16_t target = operand.read_entry(this, i);
if (target >= 0x8000) {
size_t depth = target - 0x8000;
if (depth > block_depth) {
error(operand.table + i * 2, "improper branch in tableswitch");
return false;
}
} else {
if (target >= operand.case_count) {
error(operand.table + i * 2, "invalid case target in tableswitch");
return false;
}
if (target >= block_depth) {
error(operand.table + i * 2, "improper branch in br_table");
return false;
}
}
return true;
......@@ -280,9 +268,8 @@ class WasmDecoder : public Decoder {
case kExprReturn: {
return static_cast<int>(function_env_->sig->return_count());
}
case kExprTableSwitch: {
TableSwitchOperand operand(this, pc);
return 1 + operand.case_count;
case kExprBrTable: {
return 1;
}
#define DECLARE_OPCODE_CASE(name, opcode, sig) \
......@@ -344,8 +331,8 @@ class WasmDecoder : public Decoder {
LocalIndexOperand operand(this, pc);
return 1 + operand.length;
}
case kExprTableSwitch: {
TableSwitchOperand operand(this, pc);
case kExprBrTable: {
BranchTableOperand operand(this, pc);
return 1 + operand.length;
}
case kExprI8Const:
......@@ -656,10 +643,10 @@ class LR_WasmDecoder : public WasmDecoder {
len = 1 + operand.length;
break;
}
case kExprTableSwitch: {
TableSwitchOperand operand(this, pc_);
case kExprBrTable: {
BranchTableOperand operand(this, pc_);
if (Validate(pc_, operand, blocks_.size())) {
Shift(kAstEnd, 1 + operand.case_count);
Shift(kAstEnd, 1);
}
len = 1 + operand.length;
break;
......@@ -1044,68 +1031,37 @@ class LR_WasmDecoder : public WasmDecoder {
}
break;
}
case kExprTableSwitch: {
case kExprBrTable: {
if (p->index == 1) {
// Switch key finished.
TypeCheckLast(p, kAstI32);
if (failed()) break;
TableSwitchOperand operand(this, p->pc());
BranchTableOperand operand(this, p->pc());
DCHECK(Validate(p->pc(), operand, blocks_.size()));
// Build the switch only if it has more than just a default target.
bool build_switch = operand.table_count > 1;
// Build a switch only if it has more than just a default target.
bool build_switch = operand.table_count > 0;
TFNode* sw = nullptr;
if (build_switch)
sw = BUILD(Switch, operand.table_count, p->last()->node);
// Allocate environments for each case.
SsaEnv** case_envs = zone_->NewArray<SsaEnv*>(operand.case_count);
for (uint32_t i = 0; i < operand.case_count; i++) {
case_envs[i] = UnreachableEnv();
if (build_switch) {
sw = BUILD(Switch, operand.table_count + 1, p->last()->node);
}
ifs_.push_back({nullptr, nullptr, case_envs});
SsaEnv* break_env = ssa_env_;
PushBlock(break_env);
SsaEnv* copy = Steal(break_env);
ssa_env_ = copy;
// Build the environments for each case based on the table.
for (uint32_t i = 0; i < operand.table_count; i++) {
// Process the targets of the break table.
SsaEnv* prev = ssa_env_;
SsaEnv* copy = Steal(prev);
for (uint32_t i = 0; i < operand.table_count + 1; i++) {
uint16_t target = operand.read_entry(this, i);
SsaEnv* env = copy;
if (build_switch) {
env = Split(env);
env->control = (i == operand.table_count - 1)
? BUILD(IfDefault, sw)
: BUILD(IfValue, i, sw);
}
if (target >= 0x8000) {
// Targets an outer block.
int depth = target - 0x8000;
SsaEnv* tenv = blocks_[blocks_.size() - depth - 1].ssa_env;
Goto(env, tenv);
} else {
// Targets a case.
Goto(env, case_envs[target]);
ssa_env_ = env = Split(env);
env->control = i == operand.table_count ? BUILD(IfDefault, sw)
: BUILD(IfValue, i, sw);
}
SsaEnv* tenv = blocks_[blocks_.size() - target - 1].ssa_env;
Goto(env, tenv);
}
}
if (p->done()) {
// Last case. Fall through to the end.
Block* block = &blocks_.back();
if (p->index > 1) ReduceBreakToExprBlock(p, block);
SsaEnv* next = block->ssa_env;
blocks_.pop_back();
ifs_.pop_back();
SetEnv("switch:end", next);
} else {
// Interior case. Maybe fall through to the next case.
SsaEnv* next = ifs_.back().case_envs[p->index - 1];
if (p->index > 1 && ssa_env_->go()) Goto(ssa_env_, next);
SetEnv("switch:case", next);
ssa_env_ = prev;
}
break;
}
......
......@@ -142,24 +142,23 @@ struct ImportIndexOperand {
}
};
struct TableSwitchOperand {
uint32_t case_count;
struct BranchTableOperand {
uint32_t table_count;
const byte* table;
int length;
inline TableSwitchOperand(Decoder* decoder, const byte* pc) {
case_count = decoder->checked_read_u16(pc, 1, "expected #cases");
table_count = decoder->checked_read_u16(pc, 3, "expected #entries");
length = 4 + table_count * 2;
inline BranchTableOperand(Decoder* decoder, const byte* pc) {
table_count = decoder->checked_read_u16(pc, 1, "expected #entries");
length = 2 + table_count * 2 + 2;
if (decoder->check(pc, 5, table_count * 2, "expected <table entries>")) {
table = pc + 5;
if (decoder->check(pc, 3, table_count * 2 + 2,
"expected <table entries>")) {
table = pc + 3;
} else {
table = nullptr;
}
}
inline uint16_t read_entry(Decoder* decoder, int i) {
DCHECK(i >= 0 && static_cast<uint32_t>(i) < table_count);
DCHECK(i >= 0 && static_cast<uint32_t>(i) <= table_count);
return table ? decoder->read_u16(table + i * sizeof(uint16_t)) : 0;
}
};
......
......@@ -33,14 +33,8 @@
#define WASM_RETURN(...) kExprReturn, __VA_ARGS__
#define WASM_UNREACHABLE kExprUnreachable
#define WASM_TABLESWITCH_OP(case_count, table_count, ...) \
kExprTableSwitch, static_cast<byte>(case_count), \
static_cast<byte>(case_count >> 8), static_cast<byte>(table_count), \
static_cast<byte>(table_count >> 8), __VA_ARGS__
#define WASM_TABLESWITCH_BODY0(key) key
#define WASM_TABLESWITCH_BODY(key, ...) key, __VA_ARGS__
#define WASM_BR_TABLE(key, count, ...) \
kExprBrTable, U16_LE(count), __VA_ARGS__, key
#define WASM_CASE(x) static_cast<byte>(x), static_cast<byte>(x >> 8)
#define WASM_CASE_BR(x) static_cast<byte>(x), static_cast<byte>(0x80 | (x) >> 8)
......@@ -407,6 +401,7 @@ inline void CheckI64v(int64_t value, int length) {
#define SIG_INDEX(v) U16_LE(v)
#define FUNC_INDEX(v) U16_LE(v)
#define NAME_OFFSET(v) U32_LE(v)
#define BR_TARGET(v) U16_LE(v)
#define MASK_7 ((1 << 7) - 1)
#define MASK_14 ((1 << 14) - 1)
......
......@@ -80,7 +80,7 @@ std::ostream& operator<<(std::ostream& os, const FunctionSig& function);
V(Select, 0x05, _) \
V(Br, 0x06, _) \
V(BrIf, 0x07, _) \
V(TableSwitch, 0x08, _) \
V(BrTable, 0x08, _) \
V(Return, 0x14, _) \
V(Unreachable, 0x15, _)
......
This diff is collapsed.
......@@ -1671,120 +1671,81 @@ TEST_F(AstDecoderTest, ExprBrIf_Unify) {
}
}
TEST_F(AstDecoderTest, TableSwitch0) {
static byte code[] = {kExprTableSwitch, 0, 0, 0, 0};
TEST_F(AstDecoderTest, BrTable0) {
static byte code[] = {kExprBrTable, 0, 0};
EXPECT_FAILURE(&env_v_v, code);
}
TEST_F(AstDecoderTest, TableSwitch0b) {
static byte code[] = {kExprTableSwitch, 0, 0, 0, 0, kExprI8Const, 11};
TEST_F(AstDecoderTest, BrTable0b) {
static byte code[] = {kExprBrTable, 0, 0, kExprI8Const, 11};
EXPECT_FAILURE(&env_v_v, code);
EXPECT_FAILURE(&env_i_i, code);
}
TEST_F(AstDecoderTest, TableSwitch0c) {
static byte code[] = {
WASM_BLOCK(1, WASM_TABLESWITCH_OP(0, 1, WASM_CASE_BR(0)), WASM_I8(67))};
EXPECT_VERIFIES(&env_v_v, code);
TEST_F(AstDecoderTest, BrTable0c) {
static byte code[] = {kExprBrTable, 0, 1, 0, 0, kExprI8Const, 11};
EXPECT_FAILURE(&env_v_v, code);
EXPECT_FAILURE(&env_i_i, code);
}
TEST_F(AstDecoderTest, TableSwitch0d) {
TEST_F(AstDecoderTest, BrTable1a) {
static byte code[] = {
WASM_BLOCK(1, WASM_TABLESWITCH_OP(0, 2, WASM_CASE_BR(0), WASM_CASE_BR(1)),
WASM_I8(67))};
WASM_BLOCK(1, WASM_BR_TABLE(WASM_I8(67), 0, BR_TARGET(0)))};
EXPECT_VERIFIES(&env_v_v, code);
}
TEST_F(AstDecoderTest, TableSwitch1) {
static byte code[] = {WASM_TABLESWITCH_OP(1, 1, WASM_CASE(0)),
WASM_TABLESWITCH_BODY(WASM_I8(0), WASM_I8(9))};
EXPECT_VERIFIES(&env_i_i, code);
TEST_F(AstDecoderTest, BrTable1b) {
static byte code[] = {
WASM_BLOCK(1, WASM_BR_TABLE(WASM_ZERO, 0, BR_TARGET(0)))};
EXPECT_VERIFIES(&env_v_v, code);
EXPECT_FAILURE(&env_i_i, code);
EXPECT_FAILURE(&env_f_ff, code);
EXPECT_FAILURE(&env_d_dd, code);
}
TEST_F(AstDecoderTest, TableSwitch_off_end) {
static byte code[] = {WASM_TABLESWITCH_OP(1, 1, WASM_CASE(0)),
WASM_TABLESWITCH_BODY(WASM_I8(0), WASM_I8(9))};
for (size_t len = arraysize(code) - 1; len > 0; len--) {
Verify(kError, &env_v_v, code, code + len);
}
}
TEST_F(AstDecoderTest, TableSwitch2) {
TEST_F(AstDecoderTest, BrTable2a) {
static byte code[] = {
WASM_TABLESWITCH_OP(2, 2, WASM_CASE(0), WASM_CASE(1)),
WASM_TABLESWITCH_BODY(WASM_I8(3), WASM_I8(10), WASM_I8(11))};
EXPECT_VERIFIES(&env_i_i, code);
WASM_BLOCK(1, WASM_BR_TABLE(WASM_I8(67), 1, BR_TARGET(0), BR_TARGET(0)))};
EXPECT_VERIFIES(&env_v_v, code);
EXPECT_FAILURE(&env_f_ff, code);
EXPECT_FAILURE(&env_d_dd, code);
}
TEST_F(AstDecoderTest, TableSwitch1b) {
EXPECT_VERIFIES_INLINE(&env_i_i, WASM_TABLESWITCH_OP(1, 1, WASM_CASE(0)),
WASM_TABLESWITCH_BODY(WASM_GET_LOCAL(0), WASM_ZERO));
EXPECT_VERIFIES_INLINE(&env_f_ff, WASM_TABLESWITCH_OP(1, 1, WASM_CASE(0)),
WASM_TABLESWITCH_BODY(WASM_ZERO, WASM_F32(0.0)));
EXPECT_VERIFIES_INLINE(&env_d_dd, WASM_TABLESWITCH_OP(1, 1, WASM_CASE(0)),
WASM_TABLESWITCH_BODY(WASM_ZERO, WASM_F64(0.0)));
TEST_F(AstDecoderTest, BrTable2b) {
static byte code[] = {WASM_BLOCK(
1, WASM_BLOCK(
1, WASM_BR_TABLE(WASM_I8(67), 1, BR_TARGET(0), BR_TARGET(1))))};
EXPECT_VERIFIES(&env_v_v, code);
}
TEST_F(AstDecoderTest, TableSwitch_br1) {
for (int depth = 0; depth < 2; depth++) {
byte code[] = {WASM_BLOCK(1, WASM_TABLESWITCH_OP(0, 1, WASM_CASE_BR(depth)),
WASM_GET_LOCAL(0))};
EXPECT_VERIFIES(&env_v_i, code);
EXPECT_FAILURE(&env_i_i, code);
TEST_F(AstDecoderTest, BrTable_off_end) {
static byte code[] = {
WASM_BLOCK(1, WASM_BR_TABLE(WASM_GET_LOCAL(0), 0, BR_TARGET(0)))};
for (size_t len = 1; len < sizeof(code); len++) {
Verify(kError, &env_i_i, code, code + len);
}
}
TEST_F(AstDecoderTest, TableSwitch_invalid_br) {
for (int depth = 1; depth < 4; depth++) {
EXPECT_FAILURE_INLINE(&env_v_i,
WASM_TABLESWITCH_OP(0, 1, WASM_CASE_BR(depth)),
WASM_GET_LOCAL(0));
EXPECT_FAILURE_INLINE(
&env_v_i,
WASM_TABLESWITCH_OP(0, 2, WASM_CASE_BR(depth), WASM_CASE_BR(depth)),
WASM_GET_LOCAL(0));
TEST_F(AstDecoderTest, BrTable_invalid_br1) {
for (int depth = 0; depth < 4; depth++) {
byte code[] = {
WASM_BLOCK(1, WASM_BR_TABLE(WASM_GET_LOCAL(0), 0, BR_TARGET(depth)))};
if (depth == 0) {
EXPECT_VERIFIES(&env_v_i, code);
} else {
EXPECT_FAILURE(&env_v_i, code);
}
}
}
TEST_F(AstDecoderTest, TableSwitch_invalid_case_ref) {
EXPECT_FAILURE_INLINE(&env_i_i, WASM_TABLESWITCH_OP(0, 1, WASM_CASE(0)),
WASM_GET_LOCAL(0));
EXPECT_FAILURE_INLINE(&env_i_i, WASM_TABLESWITCH_OP(1, 1, WASM_CASE(1)),
WASM_TABLESWITCH_BODY(WASM_GET_LOCAL(0), WASM_ZERO));
}
TEST_F(AstDecoderTest, TableSwitch1_br) {
EXPECT_VERIFIES_INLINE(
&env_i_i, WASM_TABLESWITCH_OP(1, 1, WASM_CASE(0)),
WASM_TABLESWITCH_BODY(WASM_GET_LOCAL(0), WASM_BRV(0, WASM_ZERO)));
}
TEST_F(AstDecoderTest, TableSwitch2_br) {
EXPECT_VERIFIES_INLINE(
&env_i_i, WASM_TABLESWITCH_OP(2, 2, WASM_CASE(0), WASM_CASE(1)),
WASM_TABLESWITCH_BODY(WASM_GET_LOCAL(0), WASM_BRV(0, WASM_I8(0)),
WASM_BRV(0, WASM_I8(1))));
EXPECT_FAILURE_INLINE(
&env_f_ff, WASM_TABLESWITCH_OP(2, 2, WASM_CASE(0), WASM_CASE(1)),
WASM_TABLESWITCH_BODY(WASM_ZERO, WASM_BRV(0, WASM_I8(3)),
WASM_BRV(0, WASM_I8(4))));
}
TEST_F(AstDecoderTest, TableSwitch2x2) {
EXPECT_VERIFIES_INLINE(
&env_i_i, WASM_TABLESWITCH_OP(2, 4, WASM_CASE(0), WASM_CASE(1),
WASM_CASE(0), WASM_CASE(1)),
WASM_TABLESWITCH_BODY(WASM_GET_LOCAL(0), WASM_BRV(0, WASM_I8(3)),
WASM_BRV(0, WASM_I8(4))));
TEST_F(AstDecoderTest, BrTable_invalid_br2) {
for (int depth = 0; depth < 4; depth++) {
byte code[] = {
WASM_LOOP(1, WASM_BR_TABLE(WASM_GET_LOCAL(0), 0, BR_TARGET(depth)))};
if (depth <= 1) {
EXPECT_VERIFIES(&env_v_i, code);
} else {
EXPECT_FAILURE(&env_v_i, code);
}
}
}
TEST_F(AstDecoderTest, ExprBreakNesting1) {
......
......@@ -92,17 +92,8 @@ TEST_F(WasmMacroGenTest, MacroStatements) {
EXPECT_SIZE(3, WASM_CONTINUE(0));
}
TEST_F(WasmMacroGenTest, TableSwitch) {
EXPECT_SIZE(2, WASM_CASE(9));
EXPECT_SIZE(2, WASM_CASE_BR(11));
EXPECT_SIZE(7, WASM_TABLESWITCH_OP(0, 1, WASM_CASE(7)));
EXPECT_SIZE(9, WASM_TABLESWITCH_OP(0, 2, WASM_CASE(7), WASM_CASE(8)));
EXPECT_SIZE(4, WASM_TABLESWITCH_BODY(WASM_I8(88), WASM_I8(77)));
EXPECT_SIZE(
6, WASM_TABLESWITCH_BODY(WASM_I8(33), WASM_I8(44), WASM_GET_LOCAL(0)));
TEST_F(WasmMacroGenTest, BrTable) {
EXPECT_SIZE(7, WASM_BR_TABLE(WASM_ZERO, 1, BR_TARGET(1)));
}
......
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