#include <asmjs/shared-constants.h>
#include <ir/import-utils.h>
#include <ir/localize.h>
#include <ir/memory-utils.h>
#include <ir/module-utils.h>
#include <ir/table-utils.h>
#include <pass.h>
#include <shared-constants.h>
#include <wasm-builder.h>
#include <wasm-emscripten.h>
#include <wasm.h>
#define DEBUG_TYPE "post-emscripten"
namespace wasm {
namespace {
static bool isInvoke(Function* F) {
return F->imported() && F->module == ENV && F->base.startsWith("invoke_");
}
struct OptimizeCalls : public WalkerPass<PostWalker<OptimizeCalls>> {
bool isFunctionParallel() override { return true; }
Pass* create() override { return new OptimizeCalls; }
void visitCall(Call* curr) {
auto* func = getModule()->getFunction(curr->target);
if (!func->imported()) {
return;
}
if (func->module == GLOBAL_MATH) {
if (func->base == POW) {
if (auto* exponent = curr->operands[1]->dynCast<Const>()) {
if (exponent->value == Literal(double(2.0))) {
Localizer localizer(curr->operands[0], getFunction(), getModule());
Builder builder(*getModule());
replaceCurrent(builder.makeBinary(
MulFloat64,
localizer.expr,
builder.makeLocalGet(localizer.index, localizer.expr->type)));
} else if (exponent->value == Literal(double(0.5))) {
replaceCurrent(
Builder(*getModule()).makeUnary(SqrtFloat64, curr->operands[0]));
}
}
}
}
}
};
}
struct PostEmscripten : public Pass {
void run(PassRunner* runner, Module* module) override {
OptimizeCalls().run(runner, module);
optimizeExceptions(runner, module);
}
void optimizeExceptions(PassRunner* runner, Module* module) {
bool hasInvokes = false;
for (auto& imp : module->functions) {
if (isInvoke(imp.get())) {
hasInvokes = true;
}
}
if (!hasInvokes) {
return;
}
TableUtils::FlatTable flatTable(module->table);
if (!flatTable.valid) {
return;
}
struct Info
: public ModuleUtils::CallGraphPropertyAnalysis<Info>::FunctionInfo {
bool canThrow = false;
};
ModuleUtils::CallGraphPropertyAnalysis<Info> analyzer(
*module, [&](Function* func, Info& info) {
if (func->imported()) {
info.canThrow = true;
}
});
analyzer.propagateBack(
[](const Info& info) { return info.canThrow; },
[](const Info& info) { return true; },
[](Info& info, Function* reason) { info.canThrow = true; },
analyzer.IndirectCallsHaveProperty);
struct OptimizeInvokes : public WalkerPass<PostWalker<OptimizeInvokes>> {
bool isFunctionParallel() override { return true; }
Pass* create() override { return new OptimizeInvokes(map, flatTable); }
std::map<Function*, Info>& map;
TableUtils::FlatTable& flatTable;
OptimizeInvokes(std::map<Function*, Info>& map,
TableUtils::FlatTable& flatTable)
: map(map), flatTable(flatTable) {}
void visitCall(Call* curr) {
auto* target = getModule()->getFunction(curr->target);
if (isInvoke(target)) {
if (auto* index = curr->operands[0]->dynCast<Const>()) {
auto actualTarget = flatTable.names.at(index->value.geti32());
if (!map[getModule()->getFunction(actualTarget)].canThrow) {
curr->target = actualTarget;
for (Index i = 0; i < curr->operands.size() - 1; i++) {
curr->operands[i] = curr->operands[i + 1];
}
curr->operands.resize(curr->operands.size() - 1);
}
}
}
}
};
OptimizeInvokes(analyzer.map, flatTable).run(runner, module);
}
};
Pass* createPostEmscriptenPass() { return new PostEmscripten(); }
}