#ifndef liveness_traversal_h
#define liveness_traversal_h
#include "cfg-traversal.h"
#include "ir/utils.h"
#include "support/sorted_vector.h"
#include "support/sparse_square_matrix.h"
#include "wasm-builder.h"
#include "wasm-traversal.h"
#include "wasm.h"
namespace wasm {
using SetOfLocals = SortedVector;
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;
sparse_square_matrix<uint8_t> copies;
std::vector<Index> totalCopies;
static void doVisitLocalGet(SubType* self, Expression** currp) {
auto* curr = (*currp)->cast<LocalGet>();
if (!self->currBasicBlock) {
Builder builder(*self->getModule());
auto* rep = builder.replaceWithIdenticalType(curr);
if (rep->is<LocalGet>()) {
rep = builder.makeBlock({builder.makeUnreachable()}, curr->type);
}
*currp = rep;
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()) {
auto originalType = curr->type;
if (originalType != curr->value->type) {
*currp =
Builder(*self->getModule()).makeBlock({curr->value}, originalType);
} else {
*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;
}
void doWalkFunction(Function* func) {
numLocals = func->getNumLocals();
copies.recreate(numLocals);
totalCopies.clear();
totalCopies.resize(numLocals);
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) {
if (j > i) {
std::swap(i, j);
}
copies.set(i, j, std::min(copies.get(i, j), uint8_t(254)) + 1);
totalCopies[i]++;
totalCopies[j]++;
}
uint8_t getCopies(Index i, Index j) {
if (j > i) {
std::swap(i, j);
}
return copies.get(i, j);
}
};
}
#endif