diff --git a/src/cfg/liveness-traversal.h b/src/cfg/liveness-traversal.h index 49e8532220c..7bae2cbaf48 100644 --- a/src/cfg/liveness-traversal.h +++ b/src/cfg/liveness-traversal.h @@ -23,6 +23,7 @@ #include "cfg-traversal.h" #include "ir/utils.h" +#include "rpo.h" #include "support/sorted_vector.h" #include "support/sparse_square_matrix.h" #include "wasm-builder.h" @@ -82,6 +83,10 @@ struct LivenessAction { // information about liveness in a basic block struct Liveness { + // For POQueue + bool inQueue; + Index index; + SetOfLocals start, end; // live locals at the start and end std::vector actions; // actions occurring in this block @@ -220,12 +225,12 @@ struct LivenessWalker : public CFGWalker { void flowLiveness() { // keep working while stuff is flowing - std::unordered_set queue; + POQueue> queue(*this); for (auto& curr : CFGWalker::basicBlocks) { if (!liveBlocks.contains(curr.get())) { continue; // ignore dead blocks } - queue.insert(curr.get()); + queue.push(curr.get()); // do the first scan through the block, starting with nothing live at the // end, and updating the liveness at the start scanLivenessThroughActions(curr->contents.actions, curr->contents.start); @@ -233,10 +238,8 @@ struct LivenessWalker : public CFGWalker { // at every point in time, we assume we already noted interferences between // things already known alive at the end, and scanned back through the block // using that - while (queue.size() > 0) { - auto iter = queue.begin(); - auto* curr = *iter; - queue.erase(iter); + while (!queue.empty()) { + auto* curr = queue.pop(); SetOfLocals live; if (!mergeStartsAndCheckChange(curr->out, curr->contents.end, live)) { continue; @@ -252,7 +255,7 @@ struct LivenessWalker : public CFGWalker { assert(curr->contents.start.size() < live.size()); curr->contents.start = live; for (auto* in : curr->in) { - queue.insert(in); + queue.push(in); } } } diff --git a/src/cfg/rpo.h b/src/cfg/rpo.h index f99f5543ac3..b1a6cf3d7cf 100644 --- a/src/cfg/rpo.h +++ b/src/cfg/rpo.h @@ -42,7 +42,7 @@ namespace wasm { // bool inQueue; // whether already in the queue // Index index; // basic block index // -template struct RPOQueue { +template> struct RPOQueue { CFG& cfg; RPOQueue(CFG& cfg) : cfg(cfg) { @@ -55,9 +55,9 @@ template struct RPOQueue { } } - std::priority_queue, std::greater> queue; + std::priority_queue, Compare> queue; - void push(CFG::BasicBlock* block) { + void push(typename CFG::BasicBlock* block) { // Push if not already in the queue. if (!block->contents.inQueue) { block->contents.inQueue = true; @@ -65,8 +65,8 @@ template struct RPOQueue { } } - CFG::BasicBlock* pop() { - // Pop the smallest element (next in RPO), which is at the top. + typename CFG::BasicBlock* pop() { + // Pop the top element. auto* block = cfg.basicBlocks[queue.top()].get(); queue.pop(); block->contents.inQueue = false; @@ -76,6 +76,13 @@ template struct RPOQueue { bool empty() const { return queue.empty(); } }; +// A queue that works in postorder (the reverse of RPO), which is useful when +// flowing information backwards through the CFG (such as in liveness analysis). +// By processing blocks with higher RPO indexes first, we process successors +// before predecessors, and fully process loops and diamonds before flowing data +// backwards to earlier blocks in the CFG. +template using POQueue = RPOQueue>; + } // namespace wasm #endif // cfg_rpo_h