#include <atomic>
#include "ir/effects.h"
#include "ir/properties.h"
#include "ir/utils.h"
#include "pass.h"
#include "wasm-builder.h"
#include "wasm.h"
namespace wasm {
namespace {
struct GlobalInfo {
bool imported = false;
bool exported = false;
std::atomic<bool> written;
std::atomic<bool> read;
};
using GlobalInfoMap = std::map<Name, GlobalInfo>;
struct GlobalUseScanner : public WalkerPass<PostWalker<GlobalUseScanner>> {
bool isFunctionParallel() override { return true; }
GlobalUseScanner(GlobalInfoMap* infos) : infos(infos) {}
GlobalUseScanner* create() override { return new GlobalUseScanner(infos); }
void visitGlobalSet(GlobalSet* curr) { (*infos)[curr->name].written = true; }
void visitGlobalGet(GlobalGet* curr) { (*infos)[curr->name].read = true; }
private:
GlobalInfoMap* infos;
};
using NameNameMap = std::map<Name, Name>;
using NameSet = std::set<Name>;
struct GlobalUseModifier : public WalkerPass<PostWalker<GlobalUseModifier>> {
bool isFunctionParallel() override { return true; }
GlobalUseModifier(NameNameMap* copiedParentMap)
: copiedParentMap(copiedParentMap) {}
GlobalUseModifier* create() override {
return new GlobalUseModifier(copiedParentMap);
}
void visitGlobalGet(GlobalGet* curr) {
auto iter = copiedParentMap->find(curr->name);
if (iter != copiedParentMap->end()) {
curr->name = iter->second;
}
}
private:
NameNameMap* copiedParentMap;
};
struct ConstantGlobalApplier
: public WalkerPass<
LinearExecutionWalker<ConstantGlobalApplier,
UnifiedExpressionVisitor<ConstantGlobalApplier>>> {
bool isFunctionParallel() override { return true; }
ConstantGlobalApplier(NameSet* constantGlobals, bool optimize)
: constantGlobals(constantGlobals), optimize(optimize) {}
ConstantGlobalApplier* create() override {
return new ConstantGlobalApplier(constantGlobals, optimize);
}
void visitExpression(Expression* curr) {
if (auto* set = curr->dynCast<GlobalSet>()) {
if (Properties::isConstantExpression(set->value)) {
currConstantGlobals[set->name] =
getLiteralsFromConstExpression(set->value);
} else {
currConstantGlobals.erase(set->name);
}
return;
} else if (auto* get = curr->dynCast<GlobalGet>()) {
if (constantGlobals->count(get->name)) {
auto* global = getModule()->getGlobal(get->name);
assert(Properties::isConstantExpression(global->init));
replaceCurrent(ExpressionManipulator::copy(global->init, *getModule()));
replaced = true;
return;
}
auto iter = currConstantGlobals.find(get->name);
if (iter != currConstantGlobals.end()) {
Builder builder(*getModule());
replaceCurrent(builder.makeConstantExpression(iter->second));
replaced = true;
}
return;
}
EffectAnalyzer effects(getPassOptions(), getModule()->features);
effects.visit(curr);
assert(effects.globalsWritten.empty()); if (effects.calls) {
currConstantGlobals.clear();
}
}
static void doNoteNonLinear(ConstantGlobalApplier* self, Expression** currp) {
self->currConstantGlobals.clear();
}
void visitFunction(Function* curr) {
if (replaced && optimize) {
PassRunner runner(getModule(), getPassRunner()->options);
runner.setIsNested(true);
runner.addDefaultFunctionOptimizationPasses();
runner.runOnFunction(curr);
}
}
private:
NameSet* constantGlobals;
bool optimize;
bool replaced = false;
std::map<Name, Literals> currConstantGlobals;
};
struct GlobalSetRemover : public WalkerPass<PostWalker<GlobalSetRemover>> {
GlobalSetRemover(const NameSet* toRemove, bool optimize)
: toRemove(toRemove), optimize(optimize) {}
bool isFunctionParallel() override { return true; }
GlobalSetRemover* create() override {
return new GlobalSetRemover(toRemove, optimize);
}
void visitGlobalSet(GlobalSet* curr) {
if (toRemove->count(curr->name) != 0) {
replaceCurrent(Builder(*getModule()).makeDrop(curr->value));
removed = true;
}
}
void visitFunction(Function* curr) {
if (removed && optimize) {
PassRunner runner(getModule(), getPassRunner()->options);
runner.setIsNested(true);
runner.addDefaultFunctionOptimizationPasses();
runner.runOnFunction(curr);
}
}
private:
const NameSet* toRemove;
bool optimize;
bool removed = false;
};
}
struct SimplifyGlobals : public Pass {
PassRunner* runner;
Module* module;
GlobalInfoMap map;
bool optimize;
SimplifyGlobals(bool optimize = false) : optimize(optimize) {}
void run(PassRunner* runner_, Module* module_) override {
runner = runner_;
module = module_;
analyze();
removeWritesToUnreadGlobals();
preferEarlierImports();
propagateConstantsToGlobals();
propagateConstantsToCode();
}
void analyze() {
for (auto& global : module->globals) {
auto& info = map[global->name];
if (global->imported()) {
info.imported = true;
}
}
for (auto& ex : module->exports) {
if (ex->kind == ExternalKind::Global) {
map[ex->value].exported = true;
}
}
GlobalUseScanner(&map).run(runner, module);
for (auto& global : module->globals) {
auto& info = map[global->name];
if (global->mutable_ && !info.imported && !info.exported &&
!info.written) {
global->mutable_ = false;
}
}
}
void removeWritesToUnreadGlobals() {
NameSet unreadGlobals;
for (auto& global : module->globals) {
auto& info = map[global->name];
if (!info.imported && !info.exported && !info.read) {
unreadGlobals.insert(global->name);
global->mutable_ = false;
info.written = false;
}
}
GlobalSetRemover(&unreadGlobals, optimize).run(runner, module);
}
void preferEarlierImports() {
NameNameMap copiedParentMap;
for (auto& global : module->globals) {
auto child = global->name;
if (!global->mutable_ && !global->imported()) {
if (auto* get = global->init->dynCast<GlobalGet>()) {
auto parent = get->name;
if (!module->getGlobal(get->name)->mutable_) {
copiedParentMap[child] = parent;
}
}
}
}
if (!copiedParentMap.empty()) {
for (auto& global : module->globals) {
auto child = global->name;
if (copiedParentMap.count(child)) {
while (copiedParentMap.count(copiedParentMap[child])) {
copiedParentMap[child] = copiedParentMap[copiedParentMap[child]];
}
}
}
GlobalUseModifier(&copiedParentMap).run(runner, module);
}
}
void propagateConstantsToGlobals() {
std::map<Name, Literals> constantGlobals;
for (auto& global : module->globals) {
if (!global->imported()) {
if (Properties::isConstantExpression(global->init)) {
constantGlobals[global->name] =
getLiteralsFromConstExpression(global->init);
} else if (auto* get = global->init->dynCast<GlobalGet>()) {
auto iter = constantGlobals.find(get->name);
if (iter != constantGlobals.end()) {
Builder builder(*module);
global->init = builder.makeConstantExpression(iter->second);
}
}
}
}
}
void propagateConstantsToCode() {
NameSet constantGlobals;
for (auto& global : module->globals) {
if (!global->mutable_ && !global->imported() &&
Properties::isConstantExpression(global->init)) {
constantGlobals.insert(global->name);
}
}
ConstantGlobalApplier(&constantGlobals, optimize).run(runner, module);
}
};
Pass* createSimplifyGlobalsPass() { return new SimplifyGlobals(false); }
Pass* createSimplifyGlobalsOptimizingPass() {
return new SimplifyGlobals(true);
}
}