Skip to content

Commit 1e91e1b

Browse files
authored
Use a single table in wasm2js (WebAssembly#2005)
This replaces the multiple asm.js tables (of power-of-2 size) with a single simple table. Also supports importing the table.
1 parent a803c11 commit 1e91e1b

61 files changed

Lines changed: 214 additions & 128 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

src/ir/table-utils.h

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/*
2+
* Copyright 2019 WebAssembly Community Group participants
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#ifndef wasm_ir_table_h
18+
#define wasm_ir_table_h
19+
20+
#include "wasm.h"
21+
#include "wasm-traversal.h"
22+
23+
namespace wasm {
24+
25+
struct FlatTable {
26+
std::vector<Name> names;
27+
bool valid;
28+
29+
FlatTable(Table& table) {
30+
valid = true;
31+
for (auto& segment : table.segments) {
32+
auto offset = segment.offset;
33+
if (!offset->is<Const>()) {
34+
// TODO: handle some non-constant segments
35+
valid = false;
36+
return;
37+
}
38+
Index start = offset->cast<Const>()->value.geti32();
39+
Index end = start + segment.data.size();
40+
if (end > names.size()) {
41+
names.resize(end);
42+
}
43+
for (Index i = 0; i < segment.data.size(); i++) {
44+
names[start + i] = segment.data[i];
45+
}
46+
}
47+
}
48+
};
49+
50+
} // namespace wasm
51+
52+
#endif // wasm_ir_table_h

src/passes/Directize.cpp

Lines changed: 2 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -27,37 +27,13 @@
2727
#include "wasm-builder.h"
2828
#include "wasm-traversal.h"
2929
#include "asm_v_wasm.h"
30-
#include <ir/utils.h>
30+
#include "ir/table-utils.h"
31+
#include "ir/utils.h"
3132

3233
namespace wasm {
3334

3435
namespace {
3536

36-
struct FlatTable {
37-
std::vector<Name> names;
38-
bool valid;
39-
40-
FlatTable(Table& table) {
41-
valid = true;
42-
for (auto& segment : table.segments) {
43-
auto offset = segment.offset;
44-
if (!offset->is<Const>()) {
45-
// TODO: handle some non-constant segments
46-
valid = false;
47-
return;
48-
}
49-
Index start = offset->cast<Const>()->value.geti32();
50-
Index end = start + segment.data.size();
51-
if (end > names.size()) {
52-
names.resize(end);
53-
}
54-
for (Index i = 0; i < segment.data.size(); i++) {
55-
names[start + i] = segment.data[i];
56-
}
57-
}
58-
}
59-
};
60-
6137
struct FunctionDirectizer : public WalkerPass<PostWalker<FunctionDirectizer>> {
6238
bool isFunctionParallel() override { return true; }
6339

src/wasm2js.h

Lines changed: 39 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
#include "ir/load-utils.h"
3939
#include "ir/module-utils.h"
4040
#include "ir/names.h"
41+
#include "ir/table-utils.h"
4142
#include "ir/utils.h"
4243
#include "passes/passes.h"
4344
#include "support/base64.h"
@@ -267,15 +268,14 @@ class Wasm2JSBuilder {
267268
std::unordered_map<const char*, IString> mangledNames[(int) NameScope::Max];
268269
std::unordered_set<IString> allMangledNames;
269270

270-
// All our function tables have the same size TODO: optimize?
271271
size_t tableSize;
272272

273273
bool almostASM = false;
274274

275275
void addBasics(Ref ast);
276276
void addFunctionImport(Ref ast, Function* import);
277277
void addGlobalImport(Ref ast, Global* import);
278-
void addTables(Ref ast, Module* wasm);
278+
void addTable(Ref ast, Module* wasm);
279279
void addExports(Ref ast, Module* wasm);
280280
void addGlobal(Ref ast, Global* global);
281281
void setNeedsAlmostASM(const char *reason);
@@ -387,7 +387,7 @@ Ref Wasm2JSBuilder::processWasm(Module* wasm, Name funcName) {
387387
asmFunc[3]->push_back(ValueBuilder::makeName("// EMSCRIPTEN_END_FUNCS"));
388388
}
389389

390-
addTables(asmFunc[3], wasm);
390+
addTable(asmFunc[3], wasm);
391391
// memory XXX
392392
addExports(asmFunc[3], wasm);
393393
return ret;
@@ -499,38 +499,43 @@ void Wasm2JSBuilder::addGlobalImport(Ref ast, Global* import) {
499499
);
500500
}
501501

502-
void Wasm2JSBuilder::addTables(Ref ast, Module* wasm) {
503-
std::map<std::string, std::vector<IString>> tables; // asm.js tables, sig => contents of table
504-
for (Table::Segment& seg : wasm->table.segments) {
505-
for (size_t i = 0; i < seg.data.size(); i++) {
506-
Name name = seg.data[i];
507-
auto func = wasm->getFunction(name);
508-
std::string sig = getSig(func);
509-
auto& table = tables[sig];
510-
if (table.size() == 0) {
511-
// fill it with the first of its type seen. we have to fill with something; and for asm2wasm output, the first is the null anyhow
512-
table.resize(tableSize);
513-
for (size_t j = 0; j < tableSize; j++) {
514-
table[j] = fromName(name, NameScope::Top);
515-
}
516-
} else {
517-
table[i + constOffset(seg)] = fromName(name, NameScope::Top);
518-
}
519-
}
520-
}
521-
for (auto& pair : tables) {
522-
auto& sig = pair.first;
523-
auto& table = pair.second;
524-
std::string stable = std::string("FUNCTION_TABLE_") + sig;
525-
IString asmName = IString(stable.c_str(), false);
526-
// add to asm module
502+
void Wasm2JSBuilder::addTable(Ref ast, Module* wasm) {
503+
// Emit a simple flat table as a JS array literal. Otherwise,
504+
// emit assignments separately for each index.
505+
FlatTable flat(wasm->table);
506+
assert(flat.valid); // TODO: non-flat tables
507+
if (!wasm->table.imported()) {
527508
Ref theVar = ValueBuilder::makeVar();
528509
ast->push_back(theVar);
529510
Ref theArray = ValueBuilder::makeArray();
530-
ValueBuilder::appendToVar(theVar, asmName, theArray);
531-
for (auto& name : table) {
511+
ValueBuilder::appendToVar(theVar, FUNCTION_TABLE, theArray);
512+
Name null("null");
513+
for (auto& name : flat.names) {
514+
if (name.is()) {
515+
name = fromName(name, NameScope::Top);
516+
} else {
517+
name = null;
518+
}
532519
ValueBuilder::appendToArray(theArray, ValueBuilder::makeName(name));
533520
}
521+
} else {
522+
// TODO: optimize for size
523+
for (auto& segment : wasm->table.segments) {
524+
auto offset = segment.offset;
525+
Index start = offset->cast<Const>()->value.geti32();
526+
for (Index i = 0; i < segment.data.size(); i++) {
527+
ast->push_back(ValueBuilder::makeStatement(
528+
ValueBuilder::makeBinary(
529+
ValueBuilder::makeSub(
530+
ValueBuilder::makeName(FUNCTION_TABLE),
531+
ValueBuilder::makeInt(start + i)
532+
),
533+
SET,
534+
ValueBuilder::makeName(fromName(segment.data[i], NameScope::Top))
535+
)
536+
));
537+
}
538+
}
534539
}
535540
}
536541

@@ -1058,11 +1063,8 @@ Ref Wasm2JSBuilder::processFunctionBody(Module* m, Function* func, IString resul
10581063
Ref visitCallIndirect(CallIndirect* curr) {
10591064
// TODO: the codegen here is a pessimization of what the ideal codegen
10601065
// looks like. Eventually if necessary this should be tightened up in the
1061-
// case that the argument expression don't have any side effects.
1066+
// case that the argument expression doesn't have any side effects.
10621067
assert(isStatement(curr));
1063-
std::string stable = std::string("FUNCTION_TABLE_") +
1064-
getSig(module->getFunctionType(curr->fullType));
1065-
IString table = IString(stable.c_str(), false);
10661068
Ref ret = ValueBuilder::makeBlock();
10671069
ScopedTemp idx(i32, parent, func);
10681070
return makeStatementizedCall(
@@ -1071,8 +1073,8 @@ Ref Wasm2JSBuilder::processFunctionBody(Module* m, Function* func, IString resul
10711073
[&]() {
10721074
flattenAppend(ret, visitAndAssign(curr->target, idx));
10731075
return ValueBuilder::makeCall(ValueBuilder::makeSub(
1074-
ValueBuilder::makeName(table),
1075-
ValueBuilder::makeBinary(idx.getAstName(), AND, ValueBuilder::makeInt(parent->getTableSize()-1))
1076+
ValueBuilder::makeName(FUNCTION_TABLE),
1077+
idx.getAstName()
10761078
));
10771079
},
10781080
result,
@@ -2031,7 +2033,7 @@ void Wasm2JSGlue::emitPre() {
20312033
}
20322034

20332035
void Wasm2JSGlue::emitPreEmscripten() {
2034-
out << "function instantiate(asmLibraryArg, wasmMemory, wasmTable) {\n\n";
2036+
out << "function instantiate(asmLibraryArg, wasmMemory, FUNCTION_TABLE) {\n\n";
20352037
}
20362038

20372039
void Wasm2JSGlue::emitPreES6() {

test/binaryen.js/emit_asmjs.js.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ function asmFunc(global, env, buffer) {
2727
return $0 | 0;
2828
}
2929

30+
var FUNCTION_TABLE = [];
3031
return {
3132
main: main
3233
};

test/wasm2js.asserts.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ function asmFunc0(global, env, buffer) {
7373
return i64toi32_i32$HIGH_BITS | 0;
7474
}
7575

76+
var FUNCTION_TABLE = [];
7677
return {
7778
empty: $0,
7879
add: $1,

test/wasm2js.traps.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ function asmFunc0(global, env, buffer) {
7373
return i64toi32_i32$HIGH_BITS | 0;
7474
}
7575

76+
var FUNCTION_TABLE = [];
7677
return {
7778
empty: $0,
7879
add: $1,

test/wasm2js/address.2asm.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ function asmFunc(global, env, buffer) {
4747
HEAP32[(i + 4294967295 | 0) >> 2] | 0;
4848
}
4949

50+
var FUNCTION_TABLE = [];
5051
return {
5152
good: $0,
5253
bad: $1

test/wasm2js/base64.2asm.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ function asmFunc(global, env, buffer) {
2222
var nan = global.NaN;
2323
var infinity = global.Infinity;
2424
var i64toi32_i32$HIGH_BITS = 0;
25+
var FUNCTION_TABLE = [];
2526
return {
2627

2728
};

test/wasm2js/block.2asm.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,7 @@ function asmFunc(global, env, buffer) {
177177
return 32 | 0;
178178
}
179179

180+
var FUNCTION_TABLE = [];
180181
return {
181182
empty: $1,
182183
singular: $2,

test/wasm2js/br.2asm.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -601,7 +601,7 @@ function asmFunc(global, env, buffer) {
601601
return i64toi32_i32$4 | 0;
602602
}
603603

604-
var FUNCTION_TABLE_iiii = [f];
604+
var FUNCTION_TABLE = [f];
605605
return {
606606
type_i32: $1,
607607
type_i64: $2,
@@ -750,6 +750,7 @@ function asmFunc(global, env, buffer) {
750750

751751
}
752752

753+
var FUNCTION_TABLE = [];
753754
return {
754755

755756
};

0 commit comments

Comments
 (0)