Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/asm2wasm.h
Original file line number Diff line number Diff line change
Expand Up @@ -660,11 +660,11 @@ void Asm2WasmBuilder::processAsm(Ref ast) {
// TODO: when not using aliasing function pointers, we could merge them by noticing that
// index 0 in each table is the null func, and each other index should only have one
// non-null func. However, that breaks down when function pointer casts are emulated.
functionTableStarts[name] = wasm.table.names.size(); // this table starts here
functionTableStarts[name] = wasm.getDefaultTable()->values.size(); // this table starts here
Ref contents = value[1];
for (unsigned k = 0; k < contents->size(); k++) {
IString curr = contents[k][1]->getIString();
wasm.table.names.push_back(curr);
wasm.getDefaultTable()->values.push_back(curr);
}
} else {
abort_on("invalid var element", pair);
Expand Down
4 changes: 2 additions & 2 deletions src/binaryen-c.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -586,7 +586,7 @@ BinaryenExpressionRef BinaryenSelect(BinaryenModuleRef module, BinaryenExpressio
}
BinaryenExpressionRef BinaryenReturn(BinaryenModuleRef module, BinaryenExpressionRef value) {
auto* ret = Builder(*((Module*)module)).makeReturn((Expression*)value);

if (tracing) {
auto id = noteExpression(ret);
std::cout << " expressions[" << id << "] = BinaryenReturn(the_module, expressions[" << expressions[value] << "]);\n";
Expand Down Expand Up @@ -730,7 +730,7 @@ void BinaryenSetFunctionTable(BinaryenModuleRef module, BinaryenFunctionRef* fun

auto* wasm = (Module*)module;
for (BinaryenIndex i = 0; i < numFuncs; i++) {
wasm->table.names.push_back(((Function*)funcs[i])->name);
wasm->getDefaultTable()->values.push_back(((Function*)funcs[i])->name);
}
}

Expand Down
10 changes: 6 additions & 4 deletions src/passes/DuplicateFunctionElimination.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -123,10 +123,12 @@ struct DuplicateFunctionElimination : public Pass {
replacerRunner.add<FunctionReplacer>(&replacements);
replacerRunner.run();
// replace in table
for (auto& name : module->table.names) {
auto iter = replacements.find(name);
if (iter != replacements.end()) {
name = iter->second;
for (auto& curr : module->tables) {
for (auto& name : curr->values) {
auto iter = replacements.find(name);
if (iter != replacements.end()) {
name = iter->second;
}
}
}
// replace in start
Expand Down
6 changes: 3 additions & 3 deletions src/passes/Print.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -576,7 +576,7 @@ struct PrintSExpression : public Visitor<PrintSExpression> {
}
void visitTable(Table *curr) {
printOpening(o, "table");
for (auto name : curr->names) {
for (auto name : curr->values) {
o << ' ';
printName(name);
}
Expand Down Expand Up @@ -647,9 +647,9 @@ struct PrintSExpression : public Visitor<PrintSExpression> {
visitGlobal(child.get());
o << maybeNewLine;
}
if (curr->table.names.size() > 0) {
for (auto& child : curr->tables) {
doIndent(o, indent);
visitTable(&curr->table);
visitTable(child.get());
o << maybeNewLine;
}
for (auto& child : curr->functions) {
Expand Down
6 changes: 4 additions & 2 deletions src/passes/RemoveUnusedFunctions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,10 @@ struct RemoveUnusedFunctions : public Pass {
root.push_back(module->getFunction(curr->value));
}
// For now, all functions that can be called indirectly are marked as roots.
for (auto& curr : module->table.names) {
root.push_back(module->getFunction(curr));
for (auto& child : module->tables) {
for (auto& curr : child->values) {
root.push_back(module->getFunction(curr));
}
}
// Compute function reachability starting from the root set.
DirectCallGraphAnalyzer analyzer(module, root);
Expand Down
6 changes: 4 additions & 2 deletions src/passes/ReorderFunctions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,10 @@ struct ReorderFunctions : public WalkerPass<PostWalker<ReorderFunctions, Visitor
for (auto& curr : module->exports) {
counts[curr->value]++;
}
for (auto& curr : module->table.names) {
counts[curr]++;
for (auto& child : module->tables) {
for (auto& curr : child->values) {
counts[curr]++;
}
}
std::sort(module->functions.begin(), module->functions.end(), [this](
const std::unique_ptr<Function>& a,
Expand Down
56 changes: 34 additions & 22 deletions src/wasm-binary.h
Original file line number Diff line number Diff line change
Expand Up @@ -480,7 +480,7 @@ class WasmBinaryWriter : public Visitor<WasmBinaryWriter, void> {
writeSignatures();
writeImports();
writeFunctionSignatures();
writeFunctionTable();
writeFunctionTables();
writeMemory();
writeGlobals();
writeExports();
Expand Down Expand Up @@ -556,7 +556,7 @@ class WasmBinaryWriter : public Visitor<WasmBinaryWriter, void> {
finishSection(start);
}

int32_t getFunctionTypeIndex(Name type) {
Index getFunctionTypeIndex(Name type) {
// TODO: optimize
for (size_t i = 0; i < wasm->functionTypes.size(); i++) {
if (wasm->functionTypes[i]->name == type) return i;
Expand Down Expand Up @@ -685,7 +685,7 @@ class WasmBinaryWriter : public Visitor<WasmBinaryWriter, void> {

void writeExports() {
if (wasm->exports.size() == 0) return;
if (debug) std::cerr << "== writeexports" << std::endl;
if (debug) std::cerr << "== writeExports" << std::endl;
auto start = startSection(BinaryConsts::Section::ExportTable);
o << U32LEB(wasm->exports.size());
for (auto& curr : wasm->exports) {
Expand Down Expand Up @@ -724,8 +724,8 @@ class WasmBinaryWriter : public Visitor<WasmBinaryWriter, void> {
assert(mappedImports.count(name));
return mappedImports[name];
}
std::map<Name, uint32_t> mappedFunctions; // name of the Function => index

std::map<Name, uint32_t> mappedFunctions; // name of the Function => entry index
uint32_t getFunctionIndex(Name name) {
if (!mappedFunctions.size()) {
// Create name => index mapping.
Expand All @@ -738,13 +738,18 @@ class WasmBinaryWriter : public Visitor<WasmBinaryWriter, void> {
return mappedFunctions[name];
}

void writeFunctionTable() {
if (wasm->table.names.size() == 0) return;
if (debug) std::cerr << "== writeFunctionTable" << std::endl;
void writeFunctionTables() {
if (wasm->tables.size() == 0) return;
if (debug) std::cerr << "== writeFunctionTables" << std::endl;
auto start = startSection(BinaryConsts::Section::FunctionTable);
o << U32LEB(wasm->table.names.size());
for (auto name : wasm->table.names) {
o << U32LEB(getFunctionIndex(name));
assert(wasm->tables.size() == 1);
// o << U32LEB(wasm->tables.size());
for (auto& curr : wasm->tables) {
if (debug) std::cerr << "write one" << std::endl;
o << U32LEB(curr->values.size());
for (auto name : curr->values) {
o << U32LEB(getFunctionIndex(name));
}
}
finishSection(start);
}
Expand Down Expand Up @@ -1256,7 +1261,7 @@ class WasmBinaryBuilder {
else if (match(BinaryConsts::Section::ExportTable)) readExports();
else if (match(BinaryConsts::Section::Globals)) readGlobals();
else if (match(BinaryConsts::Section::DataSegments)) readDataSegments();
else if (match(BinaryConsts::Section::FunctionTable)) readFunctionTable();
else if (match(BinaryConsts::Section::FunctionTable)) readFunctionTables();
else if (match(BinaryConsts::Section::Names)) readNames();
else {
std::cerr << "unfamiliar section: ";
Expand Down Expand Up @@ -1638,9 +1643,10 @@ class WasmBinaryBuilder {
}
}

for (size_t index : functionTable) {
assert(index < wasm.functions.size());
wasm.table.names.push_back(wasm.functions[index]->name);
for (auto& pair : functionTable) {
assert(pair.first < wasm.tables.size());
assert(pair.second < wasm.functions.size());
wasm.tables[pair.first]->values.push_back(wasm.functions[pair.second]->name);
}
}

Expand All @@ -1660,14 +1666,20 @@ class WasmBinaryBuilder {
}
}

std::vector<size_t> functionTable;
std::vector<std::pair<size_t, size_t>> functionTable;

void readFunctionTable() {
if (debug) std::cerr << "== readFunctionTable" << std::endl;
auto num = getU32LEB();
for (size_t i = 0; i < num; i++) {
auto index = getU32LEB();
functionTable.push_back(index);
void readFunctionTables() {
if (debug) std::cerr << "== readFunctionTables" << std::endl;
size_t numTables = 1; // getU32LEB()
for (size_t i = 0; i < numTables; i++) {
if (debug) std::cerr << "read one" << std::endl;
auto curr = new Table;
auto size = getU32LEB();
for (size_t j = 0; j < size; j++) {
auto index = getU32LEB();
functionTable.push_back(std::make_pair<>(i, index));
}
wasm.addTable(curr);
}
}

Expand Down
5 changes: 3 additions & 2 deletions src/wasm-interpreter.h
Original file line number Diff line number Diff line change
Expand Up @@ -666,9 +666,10 @@ class ModuleInstance {
LiteralList arguments;
Flow flow = generateArguments(curr->operands, arguments);
if (flow.breaking()) return flow;
Table *table = instance.wasm.getDefaultTable();
size_t index = target.value.geti32();
if (index >= instance.wasm.table.names.size()) trap("callIndirect: overflow");
Name name = instance.wasm.table.names[index];
if (index >= table->values.size()) trap("callIndirect: overflow");
Name name = table->values[index];
Function *func = instance.wasm.getFunction(name);
if (func->type.is() && func->type != curr->fullType) trap("callIndirect: bad type");
if (func->params.size() != arguments.size()) trap("callIndirect: bad # of arguments");
Expand Down
96 changes: 55 additions & 41 deletions src/wasm-linker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -117,22 +117,20 @@ void Linker::layout() {

// Pre-assign the function indexes
for (auto& pair : out.indirectIndexes) {
if (functionIndexes.count(pair.first) != 0 ||
functionNames.count(pair.second) != 0) {
Fatal() << "Function " << pair.first << " already has an index " <<
functionIndexes[pair.first] << " while setting index " << pair.second;
Index tableIndex = Table::kDefault;
Table *table = out.getIndirectTable(tableIndex);
if (functionIndexes.count(pair.second) != 0) {
Fatal() << "Function " << pair.second << " already has an index " <<
functionIndexes[pair.second].first << " while setting index " << pair.first;
}
if (debug) {
std::cerr << "pre-assigned function index: " << pair.first << ": "
<< pair.second << '\n';
std::cerr << "pre-assigned function index: " << pair.second << ": "
<< pair.first << '\n';
}
functionIndexes[pair.first] = pair.second;
functionNames[pair.second] = pair.first;
}

// Emit the pre-assigned function names in sorted order
for (const auto& P : functionNames) {
out.wasm.table.names.push_back(P.second);
assert(table->values.size() == pair.first);
table->values.push_back(pair.second);
auto indexes = std::make_pair(tableIndex, pair.first);
functionIndexes[pair.second] = indexes;
}

for (auto& relocation : out.relocations) {
Expand Down Expand Up @@ -170,6 +168,15 @@ void Linker::layout() {
}
}
}

// Create the actual tables in the underlying module. This is delayed because
// table references may be out of order, and the underlying object is a vector.
Index counter = 0;
for (auto& pair : out.tables) {
if (pair.first != counter++) Fatal() << "Tables are nonconsecutive!" << '\n';
out.wasm.addTable(pair.second);
}

if (!!startFunction) {
if (out.symbolInfo.implementedFunctions.count(startFunction) == 0) {
Fatal() << "Unknown start function: `" << startFunction << "`\n";
Expand Down Expand Up @@ -206,9 +213,11 @@ void Linker::layout() {
}

// ensure an explicit function type for indirect call targets
for (auto& name : out.wasm.table.names) {
auto* func = out.wasm.getFunction(name);
func->type = ensureFunctionType(getSig(func), &out.wasm)->name;
for (auto& table : out.wasm.tables) {
for (auto& name : table->values) {
auto* func = out.wasm.getFunction(name);
func->type = ensureFunctionType(getSig(func), &out.wasm)->name;
}
}
}

Expand Down Expand Up @@ -371,14 +380,17 @@ void Linker::emscriptenGlue(std::ostream& o) {

Index Linker::getFunctionIndex(Name name) {
if (!functionIndexes.count(name)) {
functionIndexes[name] = out.wasm.table.names.size();
out.wasm.table.names.push_back(name);
Index tableIndex = Table::kDefault;
Table *table = out.getIndirectTable(tableIndex);
functionIndexes[name] = std::make_pair(tableIndex, table->values.size());
table->values.push_back(name);
if (debug) {
std::cerr << "function index: " << name << ": "
<< functionIndexes[name] << '\n';
<< functionIndexes[name].first << " "
<< functionIndexes[name].second << '\n';
}
}
return functionIndexes[name];
return functionIndexes[name].second;
}

bool hasI64ResultOrParam(FunctionType* ft) {
Expand All @@ -390,7 +402,7 @@ bool hasI64ResultOrParam(FunctionType* ft) {
}

void Linker::makeDummyFunction() {
assert(out.wasm.table.names.empty());
assert(out.wasm.tables.empty());
bool create = false;
// Check if there are address-taken functions
for (auto& relocation : out.relocations) {
Expand All @@ -410,27 +422,29 @@ void Linker::makeDummyFunction() {
void Linker::makeDynCallThunks() {
std::unordered_set<std::string> sigs;
wasm::Builder wasmBuilder(out.wasm);
for (const auto& indirectFunc : out.wasm.table.names) {
// Skip generating thunks for the dummy function
if (indirectFunc == dummyFunction) continue;
std::string sig(getSig(out.wasm.getFunction(indirectFunc)));
auto* funcType = ensureFunctionType(sig, &out.wasm);
if (hasI64ResultOrParam(funcType)) continue; // Can't export i64s on the web.
if (!sigs.insert(sig).second) continue; // Sig is already in the set
std::vector<NameType> params;
params.emplace_back("fptr", i32); // function pointer param
int p = 0;
for (const auto& ty : funcType->params) params.emplace_back(std::to_string(p++), ty);
Function* f = wasmBuilder.makeFunction(std::string("dynCall_") + sig, std::move(params), funcType->result, {});
Expression* fptr = wasmBuilder.makeGetLocal(0, i32);
std::vector<Expression*> args;
for (unsigned i = 0; i < funcType->params.size(); ++i) {
args.push_back(wasmBuilder.makeGetLocal(i + 1, funcType->params[i]));
for (const auto& table : out.wasm.tables) {
for (const auto& indirectFunc : table->values) {
// Skip generating thunks for the dummy function
if (indirectFunc == dummyFunction) continue;
std::string sig(getSig(out.wasm.getFunction(indirectFunc)));
auto* funcType = ensureFunctionType(sig, &out.wasm);
if (hasI64ResultOrParam(funcType)) continue; // Can't export i64s on the web.
if (!sigs.insert(sig).second) continue; // Sig is already in the set
std::vector<NameType> params;
params.emplace_back("fptr", i32); // function pointer param
int p = 0;
for (const auto& ty : funcType->params) params.emplace_back(std::to_string(p++), ty);
Function* f = wasmBuilder.makeFunction(std::string("dynCall_") + sig, std::move(params), funcType->result, {});
Expression* fptr = wasmBuilder.makeGetLocal(0, i32);
std::vector<Expression*> args;
for (unsigned i = 0; i < funcType->params.size(); ++i) {
args.push_back(wasmBuilder.makeGetLocal(i + 1, funcType->params[i]));
}
Expression* call = wasmBuilder.makeCallIndirect(funcType, fptr, args);
f->body = call;
out.wasm.addFunction(f);
exportFunction(f->name, true);
}
Expression* call = wasmBuilder.makeCallIndirect(funcType, fptr, args);
f->body = call;
out.wasm.addFunction(f);
exportFunction(f->name, true);
}
}

Expand Down
Loading