#include "abi/js.h"
#include "asm_v_wasm.h"
#include "ir/element-utils.h"
#include "ir/import-utils.h"
#include "pass.h"
#include "support/debug.h"
#include "support/insert_ordered.h"
#include "wasm-builder.h"
#define DEBUG_TYPE "generate-dyncalls"
namespace wasm {
struct GenerateDynCalls : public WalkerPass<PostWalker<GenerateDynCalls>> {
GenerateDynCalls(bool onlyI64) : onlyI64(onlyI64) {}
void doWalkModule(Module* wasm) {
PostWalker<GenerateDynCalls>::doWalkModule(wasm);
for (auto& type : invokeTypes) {
generateDynCallThunk(type);
}
}
void visitTable(Table* table) {
Module* wasm = getModule();
auto& segments = wasm->elementSegments;
auto it = std::find_if(segments.begin(),
segments.end(),
[&](std::unique_ptr<ElementSegment>& segment) {
return segment->table == table->name;
});
if (it != segments.end()) {
std::vector<Name> tableSegmentData;
ElementUtils::iterElementSegmentFunctionNames(
it->get(), [&](Name name, Index) {
generateDynCallThunk(wasm->getFunction(name)->type);
});
}
}
void visitFunction(Function* func) {
if (func->imported() && func->module == ENV &&
func->base.startsWith("invoke_")) {
Signature sig = func->type.getSignature();
std::vector<Type> newParams(sig.params.begin() + 1, sig.params.end());
invokeTypes.insert(Signature(Type(newParams), sig.results));
}
}
void generateDynCallThunk(HeapType funcType);
bool onlyI64;
InsertOrderedSet<HeapType> invokeTypes;
};
static bool hasI64(Signature sig) {
for (auto t : sig.results) {
if (t.getID() == Type::i64) {
return true;
}
}
for (auto t : sig.params) {
if (t.getID() == Type::i64) {
return true;
}
}
return false;
}
static void exportFunction(Module& wasm, Name name, bool must_export) {
if (!wasm.getFunctionOrNull(name)) {
assert(!must_export);
return;
}
if (wasm.getExportOrNull(name)) {
return; }
auto exp = new Export;
exp->name = exp->value = name;
exp->kind = ExternalKind::Function;
wasm.addExport(exp);
}
void GenerateDynCalls::generateDynCallThunk(HeapType funcType) {
Signature sig = funcType.getSignature();
if (sig.results.isTuple()) {
Fatal() << "GenerateDynCalls: Cannot operate on multiple return values:"
<< sig.results;
}
if (onlyI64 && !hasI64(sig)) {
return;
}
Module* wasm = getModule();
Builder builder(*wasm);
Name name = std::string("dynCall_") + getSig(sig.results, sig.params);
if (wasm->getFunctionOrNull(name) || wasm->getExportOrNull(name)) {
return; }
std::vector<NameType> namedParams;
std::vector<Type> params;
namedParams.emplace_back("fptr", Type::i32); params.push_back(Type::i32);
int p = 0;
for (const auto& param : sig.params) {
namedParams.emplace_back(std::to_string(p++), param);
params.push_back(param);
}
auto f = builder.makeFunction(
name, std::move(namedParams), Signature(Type(params), sig.results), {});
Expression* fptr = builder.makeLocalGet(0, Type::i32);
std::vector<Expression*> args;
Index i = 0;
for (const auto& param : sig.params) {
args.push_back(builder.makeLocalGet(++i, param));
}
if (wasm->tables.empty()) {
auto* table = wasm->addTable(Builder::makeTable(Name::fromInt(0)));
table->module = ENV;
table->base = "__indirect_function_table";
}
f->body =
builder.makeCallIndirect(wasm->tables[0]->name, fptr, args, funcType);
wasm->addFunction(std::move(f));
exportFunction(*wasm, name, true);
}
Pass* createGenerateDynCallsPass() { return new GenerateDynCalls(false); }
Pass* createGenerateI64DynCallsPass() { return new GenerateDynCalls(true); }
}