[wasm] optimized switch implementation in asm.js to wasm builder
This change implements switch as a balanced if/else tree or break table or hybrid. A lot of asm.js modules are expected to extensively use switch alongside function tables that can benefit from a better implementation. BUG=v8:4203 TEST=mjsunit/asm-wasm R=titzer@chromium.org,bradnelson@chromium.org,ahaas@chromium.org LOG=N Review URL: https://codereview.chromium.org/1838973002 Cr-Commit-Position: refs/heads/master@{#35455}
This commit is contained in:
parent
6df04b296b
commit
1d37d4216b
2
BUILD.gn
2
BUILD.gn
@ -1412,6 +1412,8 @@ source_set("v8_base") {
|
||||
"src/version.h",
|
||||
"src/vm-state-inl.h",
|
||||
"src/vm-state.h",
|
||||
"src/wasm/switch-logic.h",
|
||||
"src/wasm/switch-logic.cc",
|
||||
"src/wasm/asm-wasm-builder.cc",
|
||||
"src/wasm/asm-wasm-builder.h",
|
||||
"src/wasm/ast-decoder.cc",
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include <math.h>
|
||||
|
||||
#include "src/wasm/asm-wasm-builder.h"
|
||||
#include "src/wasm/switch-logic.h"
|
||||
#include "src/wasm/wasm-macro-gen.h"
|
||||
#include "src/wasm/wasm-opcodes.h"
|
||||
|
||||
@ -99,6 +100,11 @@ class AsmWasmBuilderImpl : public AstVisitor {
|
||||
void VisitStatements(ZoneList<Statement*>* stmts) {
|
||||
for (int i = 0; i < stmts->length(); ++i) {
|
||||
Statement* stmt = stmts->at(i);
|
||||
ExpressionStatement* e = stmt->AsExpressionStatement();
|
||||
if (e != nullptr && e->expression()->IsUndefinedLiteral()) {
|
||||
block_size_--;
|
||||
continue;
|
||||
}
|
||||
RECURSE(Visit(stmt));
|
||||
if (stmt->IsJump()) break;
|
||||
}
|
||||
@ -235,53 +241,124 @@ class AsmWasmBuilderImpl : public AstVisitor {
|
||||
|
||||
void VisitWithStatement(WithStatement* stmt) { UNREACHABLE(); }
|
||||
|
||||
void SetLocalTo(uint16_t index, int value) {
|
||||
current_function_builder_->Emit(kExprSetLocal);
|
||||
AddLeb128(index, true);
|
||||
// TODO(bradnelson): variable size
|
||||
void GenerateCaseComparisonCode(int value, WasmOpcode op,
|
||||
VariableProxy* tag) {
|
||||
current_function_builder_->Emit(kExprIfElse);
|
||||
current_function_builder_->Emit(op);
|
||||
VisitVariableProxy(tag);
|
||||
byte code[] = {WASM_I32V(value)};
|
||||
current_function_builder_->EmitCode(code, sizeof(code));
|
||||
block_size_++;
|
||||
}
|
||||
|
||||
void CompileCase(CaseClause* clause, uint16_t fall_through,
|
||||
VariableProxy* tag) {
|
||||
Literal* label = clause->label()->AsLiteral();
|
||||
DCHECK_NOT_NULL(label);
|
||||
block_size_++;
|
||||
current_function_builder_->Emit(kExprIf);
|
||||
current_function_builder_->Emit(kExprI32Ior);
|
||||
current_function_builder_->Emit(kExprI32Eq);
|
||||
VisitVariableProxy(tag);
|
||||
VisitLiteral(label);
|
||||
current_function_builder_->Emit(kExprGetLocal);
|
||||
AddLeb128(fall_through, true);
|
||||
BlockVisitor visitor(this, nullptr, kExprBlock, false, 0);
|
||||
SetLocalTo(fall_through, 1);
|
||||
ZoneList<Statement*>* stmts = clause->statements();
|
||||
block_size_ += stmts->length();
|
||||
RECURSE(VisitStatements(stmts));
|
||||
void HandleCase(CaseNode* node,
|
||||
const ZoneMap<int, unsigned int>& case_to_block,
|
||||
VariableProxy* tag, int default_block) {
|
||||
if (node->left != nullptr) {
|
||||
GenerateCaseComparisonCode(node->begin, kExprI32LtS, tag);
|
||||
HandleCase(node->left, case_to_block, tag, default_block);
|
||||
}
|
||||
if (node->right != nullptr) {
|
||||
GenerateCaseComparisonCode(node->end, kExprI32GtS, tag);
|
||||
HandleCase(node->right, case_to_block, tag, default_block);
|
||||
}
|
||||
if (node->begin == node->end) {
|
||||
current_function_builder_->Emit(kExprIf);
|
||||
current_function_builder_->Emit(kExprI32Eq);
|
||||
VisitVariableProxy(tag);
|
||||
byte code[] = {WASM_I32V(node->begin)};
|
||||
current_function_builder_->EmitCode(code, sizeof(code));
|
||||
DCHECK(case_to_block.find(node->begin) != case_to_block.end());
|
||||
current_function_builder_->EmitWithVarInt(kExprBr,
|
||||
case_to_block.at(node->begin));
|
||||
current_function_builder_->Emit(kExprNop);
|
||||
} else {
|
||||
current_function_builder_->Emit(kExprBrTable);
|
||||
std::vector<uint8_t> count =
|
||||
UnsignedLEB128From(node->end - node->begin + 1);
|
||||
current_function_builder_->EmitCode(&count[0],
|
||||
static_cast<uint32_t>(count.size()));
|
||||
for (int v = node->begin; v <= node->end; v++) {
|
||||
if (case_to_block.find(v) != case_to_block.end()) {
|
||||
byte break_code[] = {BR_TARGET(case_to_block.at(v))};
|
||||
current_function_builder_->EmitCode(break_code, sizeof(break_code));
|
||||
} else {
|
||||
byte break_code[] = {BR_TARGET(default_block)};
|
||||
current_function_builder_->EmitCode(break_code, sizeof(break_code));
|
||||
}
|
||||
if (v == kMaxInt) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
byte break_code[] = {BR_TARGET(default_block)};
|
||||
current_function_builder_->EmitCode(break_code, sizeof(break_code));
|
||||
// TODO(aseemgarg): remove the if once sub 0 is fixed
|
||||
if (node->begin != 0) {
|
||||
current_function_builder_->Emit(kExprI32Sub);
|
||||
VisitVariableProxy(tag);
|
||||
byte code[] = {WASM_I32V(node->begin)};
|
||||
current_function_builder_->EmitCode(code, sizeof(code));
|
||||
} else {
|
||||
VisitVariableProxy(tag);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VisitSwitchStatement(SwitchStatement* stmt) {
|
||||
VariableProxy* tag = stmt->tag()->AsVariableProxy();
|
||||
DCHECK_NOT_NULL(tag);
|
||||
BlockVisitor visitor(this, stmt->AsBreakableStatement(), kExprBlock, false,
|
||||
0);
|
||||
uint16_t fall_through = current_function_builder_->AddLocal(kAstI32);
|
||||
SetLocalTo(fall_through, 0);
|
||||
|
||||
ZoneList<CaseClause*>* clauses = stmt->cases();
|
||||
for (int i = 0; i < clauses->length(); ++i) {
|
||||
int case_count = clauses->length();
|
||||
if (case_count == 0) {
|
||||
block_size_--;
|
||||
return;
|
||||
}
|
||||
BlockVisitor visitor(this, stmt->AsBreakableStatement(), kExprBlock, false,
|
||||
1);
|
||||
ZoneVector<BlockVisitor*> blocks(zone_);
|
||||
ZoneVector<int32_t> cases(zone_);
|
||||
ZoneMap<int, unsigned int> case_to_block(zone_);
|
||||
bool has_default = false;
|
||||
for (int i = case_count - 1; i >= 0; i--) {
|
||||
CaseClause* clause = clauses->at(i);
|
||||
blocks.push_back(new BlockVisitor(this, nullptr, kExprBlock, false,
|
||||
clause->statements()->length() + 1));
|
||||
if (!clause->is_default()) {
|
||||
CompileCase(clause, fall_through, tag);
|
||||
Literal* label = clause->label()->AsLiteral();
|
||||
Handle<Object> value = label->value();
|
||||
DCHECK(value->IsNumber() &&
|
||||
label->bounds().upper->Is(cache_.kAsmSigned));
|
||||
int32_t label_value;
|
||||
if (!value->ToInt32(&label_value)) {
|
||||
UNREACHABLE();
|
||||
}
|
||||
case_to_block[label_value] = i;
|
||||
cases.push_back(label_value);
|
||||
} else {
|
||||
ZoneList<Statement*>* stmts = clause->statements();
|
||||
block_size_ += stmts->length();
|
||||
RECURSE(VisitStatements(stmts));
|
||||
DCHECK_EQ(i, case_count - 1);
|
||||
has_default = true;
|
||||
}
|
||||
}
|
||||
if (!has_default || case_count > 1) {
|
||||
int default_block = has_default ? case_count - 1 : case_count;
|
||||
BlockVisitor switch_logic_block(this, nullptr, kExprBlock, false, 1);
|
||||
CaseNode* root = OrderCases(&cases, zone_);
|
||||
HandleCase(root, case_to_block, tag, default_block);
|
||||
if (root->left != nullptr || root->right != nullptr ||
|
||||
root->begin == root->end) {
|
||||
block_size_++;
|
||||
current_function_builder_->EmitWithVarInt(kExprBr, default_block);
|
||||
current_function_builder_->Emit(kExprNop);
|
||||
}
|
||||
} else {
|
||||
block_size_ = clauses->at(0)->statements()->length();
|
||||
}
|
||||
for (int i = 0; i < case_count; i++) {
|
||||
CaseClause* clause = clauses->at(i);
|
||||
RECURSE(VisitStatements(clause->statements()));
|
||||
BlockVisitor* v = blocks.at(case_count - i - 1);
|
||||
blocks.pop_back();
|
||||
delete v;
|
||||
}
|
||||
}
|
||||
|
||||
void VisitCaseClause(CaseClause* clause) { UNREACHABLE(); }
|
||||
|
63
src/wasm/switch-logic.cc
Normal file
63
src/wasm/switch-logic.cc
Normal file
@ -0,0 +1,63 @@
|
||||
// Copyright 2016 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "src/wasm/switch-logic.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
namespace wasm {
|
||||
|
||||
namespace {
|
||||
CaseNode* CreateBst(ZoneVector<CaseNode*>* nodes, size_t begin, size_t end) {
|
||||
if (end < begin) {
|
||||
return nullptr;
|
||||
} else if (end == begin) {
|
||||
return nodes->at(begin);
|
||||
} else {
|
||||
size_t root_index = (begin + end) / 2;
|
||||
CaseNode* root = nodes->at(root_index);
|
||||
if (root_index != 0) {
|
||||
root->left = CreateBst(nodes, begin, root_index - 1);
|
||||
}
|
||||
root->right = CreateBst(nodes, root_index + 1, end);
|
||||
return root;
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
CaseNode* OrderCases(ZoneVector<int>* cases, Zone* zone) {
|
||||
const int max_distance = 2;
|
||||
const int min_size = 4;
|
||||
if (cases->empty()) {
|
||||
return nullptr;
|
||||
}
|
||||
std::sort(cases->begin(), cases->end());
|
||||
ZoneVector<size_t> table_breaks(zone);
|
||||
for (size_t i = 1; i < cases->size(); i++) {
|
||||
if (cases->at(i) - cases->at(i - 1) > max_distance) {
|
||||
table_breaks.push_back(i);
|
||||
}
|
||||
}
|
||||
table_breaks.push_back(cases->size());
|
||||
ZoneVector<CaseNode*> nodes(zone);
|
||||
size_t curr_pos = 0;
|
||||
for (size_t i = 0; i < table_breaks.size(); i++) {
|
||||
size_t break_pos = table_breaks[i];
|
||||
if (break_pos - curr_pos >= min_size) {
|
||||
int begin = cases->at(curr_pos);
|
||||
int end = cases->at(break_pos - 1);
|
||||
nodes.push_back(new (zone) CaseNode(begin, end));
|
||||
curr_pos = break_pos;
|
||||
} else {
|
||||
for (; curr_pos < break_pos; curr_pos++) {
|
||||
nodes.push_back(new (zone)
|
||||
CaseNode(cases->at(curr_pos), cases->at(curr_pos)));
|
||||
}
|
||||
}
|
||||
}
|
||||
return CreateBst(&nodes, 0, nodes.size() - 1);
|
||||
}
|
||||
} // namespace wasm
|
||||
} // namespace internal
|
||||
} // namespace v8
|
31
src/wasm/switch-logic.h
Normal file
31
src/wasm/switch-logic.h
Normal file
@ -0,0 +1,31 @@
|
||||
// Copyright 2016 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef V8_WASM_SWITCH_LOGIC_H
|
||||
#define V8_WASM_SWITCH_LOGIC_H
|
||||
|
||||
#include "src/zone-containers.h"
|
||||
#include "src/zone.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
namespace wasm {
|
||||
|
||||
struct CaseNode : public ZoneObject {
|
||||
const int begin;
|
||||
const int end;
|
||||
CaseNode* left;
|
||||
CaseNode* right;
|
||||
CaseNode(int begin, int end) : begin(begin), end(end) {
|
||||
left = nullptr;
|
||||
right = nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
CaseNode* OrderCases(ZoneVector<int>* cases, Zone* zone);
|
||||
} // namespace wasm
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
||||
#endif
|
@ -274,6 +274,7 @@
|
||||
'wasm/asm-wasm-literals': [PASS, ['arch in [arm, arm64, mips, mipsel, mips64, mips64el] or ignition == True', SKIP]],
|
||||
'wasm/asm-wasm-copy': [PASS, ['arch in [arm, arm64, mips, mipsel, mips64, mips64el]', SKIP]],
|
||||
'wasm/asm-wasm-deopt': [PASS, ['arch in [arm, arm64, mips, mipsel, mips64, mips64el]', SKIP]],
|
||||
'wasm/asm-wasm-switch': [PASS, ['arch in [arm, arm64, mips, mipsel, mips64, mips64el]', SKIP]],
|
||||
|
||||
# TODO(branelson): Figure out why ignition + asm->wasm fails embenchen.
|
||||
'wasm/embenchen/*': [PASS, ['arch == arm64', SKIP], ['ignition == True', SKIP]],
|
||||
|
442
test/mjsunit/wasm/asm-wasm-switch.js
Normal file
442
test/mjsunit/wasm/asm-wasm-switch.js
Normal file
@ -0,0 +1,442 @@
|
||||
// Copyright 2016 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// Flags: --expose-wasm
|
||||
|
||||
(function TestSwitch() {
|
||||
function asmModule() {
|
||||
"use asm"
|
||||
|
||||
function caller() {
|
||||
var ret = 0;
|
||||
var x = 7;
|
||||
switch (x) {
|
||||
case 1: return 0;
|
||||
case 7: {
|
||||
ret = 12;
|
||||
break;
|
||||
}
|
||||
default: return 0;
|
||||
}
|
||||
switch (x) {
|
||||
case 1: return 0;
|
||||
case 8: return 0;
|
||||
default: ret = (ret + 11)|0;
|
||||
}
|
||||
return ret|0;
|
||||
}
|
||||
|
||||
return {caller:caller};
|
||||
}
|
||||
var wasm = Wasm.instantiateModuleFromAsm(asmModule.toString());
|
||||
assertEquals(23, wasm.caller());
|
||||
})();
|
||||
|
||||
(function TestSwitchFallthrough() {
|
||||
function asmModule() {
|
||||
"use asm"
|
||||
|
||||
function caller() {
|
||||
var x = 17;
|
||||
var ret = 0;
|
||||
switch (x) {
|
||||
case 17:
|
||||
case 14: ret = 39;
|
||||
case 1: ret = (ret + 3)|0;
|
||||
case 4: break;
|
||||
default: ret = (ret + 1)|0;
|
||||
}
|
||||
return ret|0;
|
||||
}
|
||||
|
||||
return {caller:caller};
|
||||
}
|
||||
var wasm = Wasm.instantiateModuleFromAsm(asmModule.toString());
|
||||
assertEquals(42, wasm.caller());
|
||||
})();
|
||||
|
||||
(function TestNestedSwitch() {
|
||||
function asmModule() {
|
||||
"use asm"
|
||||
|
||||
function caller() {
|
||||
var x = 3;
|
||||
var y = -13;
|
||||
switch (x) {
|
||||
case 1: return 0;
|
||||
case 3: {
|
||||
switch (y) {
|
||||
case 2: return 0;
|
||||
case -13: return 43;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
default: return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
return {caller:caller};
|
||||
}
|
||||
var wasm = Wasm.instantiateModuleFromAsm(asmModule.toString());
|
||||
assertEquals(43, wasm.caller());
|
||||
})();
|
||||
|
||||
(function TestSwitchWithDefaultOnly() {
|
||||
function asmModule() {
|
||||
"use asm";
|
||||
function main(x) {
|
||||
x = x|0;
|
||||
switch(x|0) {
|
||||
default: return -10;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
return {
|
||||
main: main,
|
||||
};
|
||||
}
|
||||
var wasm = Wasm.instantiateModuleFromAsm(asmModule.toString());
|
||||
assertEquals(-10, wasm.main(2));
|
||||
assertEquals(-10, wasm.main(54));
|
||||
})();
|
||||
|
||||
(function TestEmptySwitch() {
|
||||
function asmModule() {
|
||||
"use asm";
|
||||
function main(x) {
|
||||
x = x|0;
|
||||
switch(x|0) {
|
||||
}
|
||||
return 73;
|
||||
}
|
||||
return {
|
||||
main: main,
|
||||
};
|
||||
}
|
||||
var wasm = Wasm.instantiateModuleFromAsm(asmModule.toString());
|
||||
assertEquals(73, wasm.main(7));
|
||||
})();
|
||||
|
||||
(function TestSwitchWithBrTable() {
|
||||
function asmModule() {
|
||||
"use asm";
|
||||
function main(x) {
|
||||
x = x|0;
|
||||
switch(x|0) {
|
||||
case 14: return 23;
|
||||
case 12: return 25;
|
||||
case 15: return 29;
|
||||
case 19: return 34;
|
||||
case 18: return 17;
|
||||
case 16: return 16;
|
||||
default: return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
return {
|
||||
main: main,
|
||||
};
|
||||
}
|
||||
var wasm = Wasm.instantiateModuleFromAsm(asmModule.toString());
|
||||
assertEquals(25, wasm.main(12));
|
||||
assertEquals(23, wasm.main(14));
|
||||
assertEquals(29, wasm.main(15));
|
||||
assertEquals(16, wasm.main(16));
|
||||
assertEquals(17, wasm.main(18));
|
||||
assertEquals(34, wasm.main(19));
|
||||
assertEquals(-1, wasm.main(-1));
|
||||
})();
|
||||
|
||||
(function TestSwitchWithBalancedTree() {
|
||||
function asmModule() {
|
||||
"use asm";
|
||||
function main(x) {
|
||||
x = x|0;
|
||||
switch(x|0) {
|
||||
case 5: return 52;
|
||||
case 1: return 11;
|
||||
case 6: return 63;
|
||||
case 9: return 19;
|
||||
case -4: return -4;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
return {
|
||||
main: main,
|
||||
};
|
||||
}
|
||||
var wasm = Wasm.instantiateModuleFromAsm(asmModule.toString());
|
||||
assertEquals(-4, wasm.main(-4));
|
||||
assertEquals(11, wasm.main(1));
|
||||
assertEquals(52, wasm.main(5));
|
||||
assertEquals(63, wasm.main(6));
|
||||
assertEquals(19, wasm.main(9));
|
||||
assertEquals(0, wasm.main(11));
|
||||
})();
|
||||
|
||||
(function TestSwitchHybrid() {
|
||||
function asmModule() {
|
||||
"use asm";
|
||||
function main(x) {
|
||||
x = x|0;
|
||||
switch(x|0) {
|
||||
case 1: return -4;
|
||||
case 2: return 23;
|
||||
case 3: return 32;
|
||||
case 4: return 14;
|
||||
case 7: return 17;
|
||||
case 10: return 10;
|
||||
case 11: return 121;
|
||||
case 12: return 112;
|
||||
case 13: return 31;
|
||||
case 16: return 16;
|
||||
default: return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
return {
|
||||
main: main,
|
||||
};
|
||||
}
|
||||
var wasm = Wasm.instantiateModuleFromAsm(asmModule.toString());
|
||||
assertEquals(-4, wasm.main(1));
|
||||
assertEquals(23, wasm.main(2));
|
||||
assertEquals(32, wasm.main(3));
|
||||
assertEquals(14, wasm.main(4));
|
||||
assertEquals(17, wasm.main(7));
|
||||
assertEquals(10, wasm.main(10));
|
||||
assertEquals(121, wasm.main(11));
|
||||
assertEquals(112, wasm.main(12));
|
||||
assertEquals(31, wasm.main(13));
|
||||
assertEquals(16, wasm.main(16));
|
||||
assertEquals(-1, wasm.main(20));
|
||||
})();
|
||||
|
||||
(function TestSwitchFallthroughWithBrTable() {
|
||||
function asmModule() {
|
||||
"use asm";
|
||||
function main(x) {
|
||||
x = x|0;
|
||||
var ret = 0;
|
||||
switch(x|0) {
|
||||
case 1: {
|
||||
ret = 21;
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
ret = 12;
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
ret = 43;
|
||||
}
|
||||
case 4: {
|
||||
ret = 54;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
ret = 10;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ret|0;
|
||||
}
|
||||
return {
|
||||
main: main,
|
||||
};
|
||||
}
|
||||
var wasm = Wasm.instantiateModuleFromAsm(asmModule.toString());
|
||||
assertEquals(12, wasm.main(2));
|
||||
assertEquals(10, wasm.main(10));
|
||||
assertEquals(54, wasm.main(3));
|
||||
})();
|
||||
|
||||
(function TestSwitchFallthroughHybrid() {
|
||||
function asmModule() {
|
||||
"use asm";
|
||||
function main(x) {
|
||||
x = x|0;
|
||||
var ret = 0;
|
||||
switch(x|0) {
|
||||
case 1: {
|
||||
ret = 1;
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
ret = 2;
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
ret = 3;
|
||||
break;
|
||||
}
|
||||
case 4: {
|
||||
ret = 4;
|
||||
}
|
||||
case 7: {
|
||||
ret = 7;
|
||||
break;
|
||||
}
|
||||
case 10: {
|
||||
ret = 10;
|
||||
}
|
||||
case 16: {
|
||||
ret = 16;
|
||||
break;
|
||||
}
|
||||
case 17: {
|
||||
ret = 17;
|
||||
break;
|
||||
}
|
||||
case 18: {
|
||||
ret = 18;
|
||||
break;
|
||||
}
|
||||
case 19: {
|
||||
ret = 19;
|
||||
}
|
||||
default: {
|
||||
ret = -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ret|0;
|
||||
}
|
||||
return {
|
||||
main: main,
|
||||
};
|
||||
}
|
||||
var wasm = Wasm.instantiateModuleFromAsm(asmModule.toString());
|
||||
assertEquals(7, wasm.main(4));
|
||||
assertEquals(16, wasm.main(10));
|
||||
assertEquals(-1, wasm.main(19));
|
||||
assertEquals(-1, wasm.main(23));
|
||||
})();
|
||||
|
||||
(function TestSwitchHybridWithNoDefault() {
|
||||
function asmModule() {
|
||||
"use asm";
|
||||
function main(x) {
|
||||
x = x|0;
|
||||
var ret = 19;
|
||||
switch(x|0) {
|
||||
case 1: {
|
||||
ret = 1;
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
ret = 2;
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
ret = 3;
|
||||
break;
|
||||
}
|
||||
case 4: {
|
||||
ret = 4;
|
||||
break;
|
||||
}
|
||||
case 7: {
|
||||
ret = 7;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ret|0;
|
||||
}
|
||||
return {
|
||||
main: main,
|
||||
};
|
||||
}
|
||||
var wasm = Wasm.instantiateModuleFromAsm(asmModule.toString());
|
||||
assertEquals(2, wasm.main(2));
|
||||
assertEquals(7, wasm.main(7));
|
||||
assertEquals(19, wasm.main(-1));
|
||||
})();
|
||||
|
||||
(function TestLargeSwitch() {
|
||||
function LargeSwitchGenerator(begin, end, gap, handle_case) {
|
||||
var str = "function asmModule() {\
|
||||
\"use asm\";\
|
||||
function main(x) {\
|
||||
x = x|0;\
|
||||
switch(x|0) {";
|
||||
for (var i = begin; i <= end; i = i + gap) {
|
||||
str = str.concat("case ", i.toString(), ": ", handle_case(i));
|
||||
}
|
||||
str = str.concat("default: return -1;\
|
||||
}\
|
||||
return -2;\
|
||||
}\
|
||||
return {main: main}; }");
|
||||
|
||||
var wasm = Wasm.instantiateModuleFromAsm(str);
|
||||
return wasm;
|
||||
}
|
||||
|
||||
var handle_case = function(k) {
|
||||
return "return ".concat(k, ";");
|
||||
}
|
||||
var wasm = LargeSwitchGenerator(0, 513, 1, handle_case);
|
||||
for (var i = 0; i <= 513; i++) {
|
||||
assertEquals(i, wasm.main(i));
|
||||
}
|
||||
assertEquals(-1, wasm.main(-1));
|
||||
|
||||
wasm = LargeSwitchGenerator(0, 1024, 3, handle_case);
|
||||
for (var i = 0; i <= 1024; i = i + 3) {
|
||||
assertEquals(i, wasm.main(i));
|
||||
}
|
||||
assertEquals(-1, wasm.main(-1));
|
||||
|
||||
wasm = LargeSwitchGenerator(-2147483648, -2147483000, 1, handle_case);
|
||||
for (var i = -2147483648; i <= -2147483000; i++) {
|
||||
assertEquals(i, wasm.main(i));
|
||||
}
|
||||
assertEquals(-1, wasm.main(-1));
|
||||
assertEquals(-1, wasm.main(214748647));
|
||||
|
||||
wasm = LargeSwitchGenerator(-2147483648, -2147483000, 3, handle_case);
|
||||
for (var i = -2147483648; i <= -2147483000; i = i + 3) {
|
||||
assertEquals(i, wasm.main(i));
|
||||
}
|
||||
assertEquals(-1, wasm.main(-1));
|
||||
assertEquals(-1, wasm.main(214748647));
|
||||
|
||||
wasm = LargeSwitchGenerator(2147483000, 2147483647, 1, handle_case);
|
||||
for (var i = 2147483000; i <= 2147483647; i++) {
|
||||
assertEquals(i, wasm.main(i));
|
||||
}
|
||||
assertEquals(-1, wasm.main(-1));
|
||||
assertEquals(-1, wasm.main(-214748647));
|
||||
|
||||
wasm = LargeSwitchGenerator(2147483000, 2147483647, 4, handle_case);
|
||||
for (var i = 2147483000; i <= 2147483647; i = i + 4) {
|
||||
assertEquals(i, wasm.main(i));
|
||||
}
|
||||
assertEquals(-1, wasm.main(-1));
|
||||
assertEquals(-1, wasm.main(-214748647));
|
||||
|
||||
handle_case = function(k) {
|
||||
if (k != 7) return "return ".concat(k, ";");
|
||||
else return "break;";
|
||||
}
|
||||
wasm = LargeSwitchGenerator(0, 1499, 7, handle_case);
|
||||
for (var i = 0; i <= 1499; i = i + 7) {
|
||||
if (i == 7) assertEquals(-2, wasm.main(i));
|
||||
else assertEquals(i, wasm.main(i));
|
||||
}
|
||||
assertEquals(-1, wasm.main(-1));
|
||||
|
||||
handle_case = function(k) {
|
||||
if (k != 56) return "break;";
|
||||
else return "return 23;";
|
||||
}
|
||||
wasm = LargeSwitchGenerator(0, 638, 2, handle_case);
|
||||
for (var i = 0; i <= 638; i = i + 2) {
|
||||
if (i == 56) assertEquals(23, wasm.main(i));
|
||||
else assertEquals(-2, wasm.main(i));
|
||||
}
|
||||
assertEquals(-1, wasm.main(-1));
|
||||
})();
|
@ -769,82 +769,6 @@ function TestConditional() {
|
||||
assertWasm(41, TestConditional);
|
||||
|
||||
|
||||
function TestSwitch() {
|
||||
"use asm"
|
||||
|
||||
function caller() {
|
||||
var ret = 0;
|
||||
var x = 7;
|
||||
switch (x) {
|
||||
case 1: return 0;
|
||||
case 7: {
|
||||
ret = 12;
|
||||
break;
|
||||
}
|
||||
default: return 0;
|
||||
}
|
||||
switch (x) {
|
||||
case 1: return 0;
|
||||
case 8: return 0;
|
||||
default: ret = (ret + 11)|0;
|
||||
}
|
||||
return ret|0;
|
||||
}
|
||||
|
||||
return {caller:caller};
|
||||
}
|
||||
|
||||
assertWasm(23, TestSwitch);
|
||||
|
||||
|
||||
function TestSwitchFallthrough() {
|
||||
"use asm"
|
||||
|
||||
function caller() {
|
||||
var x = 17;
|
||||
var ret = 0;
|
||||
switch (x) {
|
||||
case 17:
|
||||
case 14: ret = 39;
|
||||
case 1: ret = (ret + 3)|0;
|
||||
case 4: break;
|
||||
default: ret = (ret + 1)|0;
|
||||
}
|
||||
return ret|0;
|
||||
}
|
||||
|
||||
return {caller:caller};
|
||||
}
|
||||
|
||||
assertWasm(42, TestSwitchFallthrough);
|
||||
|
||||
|
||||
function TestNestedSwitch() {
|
||||
"use asm"
|
||||
|
||||
function caller() {
|
||||
var x = 3;
|
||||
var y = -13;
|
||||
switch (x) {
|
||||
case 1: return 0;
|
||||
case 3: {
|
||||
switch (y) {
|
||||
case 2: return 0;
|
||||
case -13: return 43;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
default: return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
return {caller:caller};
|
||||
}
|
||||
|
||||
assertWasm(43, TestNestedSwitch);
|
||||
|
||||
|
||||
(function () {
|
||||
function TestInitFunctionWithNoGlobals() {
|
||||
"use asm";
|
||||
|
@ -120,6 +120,7 @@
|
||||
'wasm/encoder-unittest.cc',
|
||||
'wasm/loop-assignment-analysis-unittest.cc',
|
||||
'wasm/module-decoder-unittest.cc',
|
||||
'wasm/switch-logic-unittest.cc',
|
||||
'wasm/wasm-macro-gen-unittest.cc',
|
||||
],
|
||||
'conditions': [
|
||||
|
89
test/unittests/wasm/switch-logic-unittest.cc
Normal file
89
test/unittests/wasm/switch-logic-unittest.cc
Normal file
@ -0,0 +1,89 @@
|
||||
// Copyright 2016 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "src/wasm/switch-logic.h"
|
||||
#include "test/unittests/test-utils.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
namespace wasm {
|
||||
class SwitchLogicTest : public TestWithZone {};
|
||||
|
||||
void CheckNodeValues(CaseNode* node, int begin, int end) {
|
||||
CHECK_EQ(node->begin, begin);
|
||||
CHECK_EQ(node->end, end);
|
||||
}
|
||||
|
||||
TEST_F(SwitchLogicTest, Single_Table_Test) {
|
||||
ZoneVector<int> values(zone());
|
||||
values.push_back(14);
|
||||
values.push_back(12);
|
||||
values.push_back(15);
|
||||
values.push_back(19);
|
||||
values.push_back(18);
|
||||
values.push_back(16);
|
||||
CaseNode* root = OrderCases(&values, zone());
|
||||
CHECK_NULL(root->left);
|
||||
CHECK_NULL(root->right);
|
||||
CheckNodeValues(root, 12, 19);
|
||||
}
|
||||
|
||||
TEST_F(SwitchLogicTest, Balanced_Tree_Test) {
|
||||
ZoneVector<int> values(zone());
|
||||
values.push_back(5);
|
||||
values.push_back(1);
|
||||
values.push_back(6);
|
||||
values.push_back(9);
|
||||
values.push_back(-4);
|
||||
CaseNode* root = OrderCases(&values, zone());
|
||||
CheckNodeValues(root, 5, 5);
|
||||
CheckNodeValues(root->left, -4, -4);
|
||||
CHECK_NULL(root->left->left);
|
||||
CheckNodeValues(root->left->right, 1, 1);
|
||||
CHECK_NULL(root->left->right->left);
|
||||
CHECK_NULL(root->left->right->right);
|
||||
CheckNodeValues(root->right, 6, 6);
|
||||
CHECK_NULL(root->right->left);
|
||||
CheckNodeValues(root->right->right, 9, 9);
|
||||
CHECK_NULL(root->right->right->left);
|
||||
CHECK_NULL(root->right->right->right);
|
||||
}
|
||||
|
||||
TEST_F(SwitchLogicTest, Hybrid_Test) {
|
||||
ZoneVector<int> values(zone());
|
||||
values.push_back(1);
|
||||
values.push_back(2);
|
||||
values.push_back(3);
|
||||
values.push_back(4);
|
||||
values.push_back(7);
|
||||
values.push_back(10);
|
||||
values.push_back(11);
|
||||
values.push_back(12);
|
||||
values.push_back(13);
|
||||
values.push_back(16);
|
||||
CaseNode* root = OrderCases(&values, zone());
|
||||
CheckNodeValues(root, 7, 7);
|
||||
CheckNodeValues(root->left, 1, 4);
|
||||
CheckNodeValues(root->right, 10, 13);
|
||||
CheckNodeValues(root->right->right, 16, 16);
|
||||
}
|
||||
|
||||
TEST_F(SwitchLogicTest, Single_Case) {
|
||||
ZoneVector<int> values(zone());
|
||||
values.push_back(3);
|
||||
CaseNode* root = OrderCases(&values, zone());
|
||||
CheckNodeValues(root, 3, 3);
|
||||
CHECK_NULL(root->left);
|
||||
CHECK_NULL(root->right);
|
||||
}
|
||||
|
||||
TEST_F(SwitchLogicTest, Empty_Case) {
|
||||
ZoneVector<int> values(zone());
|
||||
CaseNode* root = OrderCases(&values, zone());
|
||||
CHECK_NULL(root);
|
||||
}
|
||||
|
||||
} // namespace wasm
|
||||
} // namespace internal
|
||||
} // namespace v8
|
@ -1116,6 +1116,8 @@
|
||||
'../../src/version.h',
|
||||
'../../src/vm-state-inl.h',
|
||||
'../../src/vm-state.h',
|
||||
'../../src/wasm/switch-logic.h',
|
||||
'../../src/wasm/switch-logic.cc',
|
||||
'../../src/wasm/asm-wasm-builder.cc',
|
||||
'../../src/wasm/asm-wasm-builder.h',
|
||||
'../../src/wasm/ast-decoder.cc',
|
||||
|
Loading…
Reference in New Issue
Block a user