#ifndef liveness_traversal_h
#define liveness_traversal_h
#include "cfg-traversal.h"
#include "ir/utils.h"
#include "support/sorted_vector.h"
#include "wasm-builder.h"
#include "wasm-traversal.h"
#include "wasm.h"
namespace wasm {
typedef SortedVector SetOfLocals;
struct LivenessAction {
enum What { Get = 0, Set = 1, Other = 2 };
What what;
Index index; Expression** origin; bool effective;
LivenessAction(What what, Index index, Expression** origin)
: what(what), index(index), origin(origin), effective(false) {
assert(what != Other);
if (what == Get) {
assert((*origin)->is<LocalGet>());
}
if (what == Set) {
assert((*origin)->is<LocalSet>());
}
}
LivenessAction(Expression** origin) : what(Other), origin(origin) {}
bool isGet() { return what == Get; }
bool isSet() { return what == Set; }
bool isOther() { return what == Other; }
void removeCopy() {
auto* set = (*origin)->cast<LocalSet>();
if (set->isTee()) {
*origin = set->value->cast<LocalGet>();
} else {
ExpressionManipulator::nop(set);
}
what = Other;
}
};
struct Liveness {
SetOfLocals start, end; std::vector<LivenessAction> actions;
#if LIVENESS_DEBUG
void dump(Function* func) {
if (actions.empty())
return;
std::cout << " actions:\n";
for (auto& action : actions) {
std::cout << " "
<< (action.isGet() ? "get" : (action.isSet() ? "set" : "other"))
<< " " << func->getLocalName(action.index) << "\n";
}
}
#endif };
template<typename SubType, typename VisitorType>
struct LivenessWalker : public CFGWalker<SubType, VisitorType, Liveness> {
typedef
typename CFGWalker<SubType, VisitorType, Liveness>::BasicBlock BasicBlock;
Index numLocals;
std::unordered_set<BasicBlock*> liveBlocks;
std::vector<uint8_t> copies;
std::vector<Index> totalCopies;
static void doVisitLocalGet(SubType* self, Expression** currp) {
auto* curr = (*currp)->cast<LocalGet>();
if (!self->currBasicBlock) {
*currp = Builder(*self->getModule()).replaceWithIdenticalType(curr);
return;
}
self->currBasicBlock->contents.actions.emplace_back(
LivenessAction::Get, curr->index, currp);
}
static void doVisitLocalSet(SubType* self, Expression** currp) {
auto* curr = (*currp)->cast<LocalSet>();
if (!self->currBasicBlock) {
if (curr->isTee()) {
*currp = curr->value;
} else {
*currp = Builder(*self->getModule()).makeDrop(curr->value);
}
return;
}
self->currBasicBlock->contents.actions.emplace_back(
LivenessAction::Set, curr->index, currp);
if (auto* get = self->getCopy(curr)) {
self->addCopy(curr->index, get->index);
self->addCopy(curr->index, get->index);
}
}
LocalGet* getCopy(LocalSet* set) {
if (auto* get = set->value->dynCast<LocalGet>()) {
return get;
}
if (auto* iff = set->value->dynCast<If>()) {
if (auto* get = iff->ifTrue->dynCast<LocalGet>()) {
return get;
}
if (iff->ifFalse) {
if (auto* get = iff->ifFalse->dynCast<LocalGet>()) {
return get;
}
}
}
return nullptr;
}
bool canRun(Function* func) {
Index numLocals = func->getNumLocals();
if (uint64_t(numLocals) * uint64_t(numLocals) <=
std::numeric_limits<Index>::max()) {
return true;
}
std::cerr << "warning: too many locals (" << numLocals
<< ") to run liveness analysis in " << this->getFunction()->name
<< '\n';
return false;
}
void doWalkFunction(Function* func) {
numLocals = func->getNumLocals();
assert(canRun(func));
copies.resize(numLocals * numLocals);
std::fill(copies.begin(), copies.end(), 0);
totalCopies.resize(numLocals);
std::fill(totalCopies.begin(), totalCopies.end(), 0);
CFGWalker<SubType, VisitorType, Liveness>::doWalkFunction(func);
liveBlocks = CFGWalker<SubType, VisitorType, Liveness>::findLiveBlocks();
CFGWalker<SubType, VisitorType, Liveness>::unlinkDeadBlocks(liveBlocks);
flowLiveness();
}
void flowLiveness() {
std::unordered_set<BasicBlock*> queue;
for (auto& curr : CFGWalker<SubType, VisitorType, Liveness>::basicBlocks) {
if (liveBlocks.count(curr.get()) == 0) {
continue; }
queue.insert(curr.get());
scanLivenessThroughActions(curr->contents.actions, curr->contents.start);
}
while (queue.size() > 0) {
auto iter = queue.begin();
auto* curr = *iter;
queue.erase(iter);
SetOfLocals live;
if (!mergeStartsAndCheckChange(curr->out, curr->contents.end, live)) {
continue;
}
assert(curr->contents.end.size() < live.size());
curr->contents.end = live;
scanLivenessThroughActions(curr->contents.actions, live);
if (curr->contents.start == live) {
continue;
}
assert(curr->contents.start.size() < live.size());
curr->contents.start = live;
for (auto* in : curr->in) {
queue.insert(in);
}
}
}
bool mergeStartsAndCheckChange(std::vector<BasicBlock*>& blocks,
SetOfLocals& old,
SetOfLocals& ret) {
if (blocks.size() == 0) {
return false;
}
ret = blocks[0]->contents.start;
if (blocks.size() > 1) {
for (Index i = 1; i < blocks.size(); i++) {
ret = ret.merge(blocks[i]->contents.start);
}
}
return old != ret;
}
void scanLivenessThroughActions(std::vector<LivenessAction>& actions,
SetOfLocals& live) {
for (int i = int(actions.size()) - 1; i >= 0; i--) {
auto& action = actions[i];
if (action.isGet()) {
live.insert(action.index);
} else if (action.isSet()) {
live.erase(action.index);
}
}
}
void addCopy(Index i, Index j) {
auto k = std::min(i, j) * numLocals + std::max(i, j);
copies[k] = std::min(copies[k], uint8_t(254)) + 1;
totalCopies[i]++;
totalCopies[j]++;
}
uint8_t getCopies(Index i, Index j) {
return copies[std::min(i, j) * numLocals + std::max(i, j)];
}
};
}
#endif