diff --git a/src/cfg/rpo.h b/src/cfg/rpo.h new file mode 100644 index 00000000000..f99f5543ac3 --- /dev/null +++ b/src/cfg/rpo.h @@ -0,0 +1,82 @@ +/* + * Copyright 2026 WebAssembly Community Group participants + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// +// Utilities for reverse-postorder queue management. +// + +#ifndef cfg_rpo_h +#define cfg_rpo_h + +#include + +#include "wasm.h" + +namespace wasm { + +// +// Given a CFG in reverse postorder (e.g. from cfg-traversal), implement a +// priority queue working in reverse postorder. BasicBlock indexes indicate the +// block's position in RPO, and by processing the ones with lower indexes first, +// we can ensure that we fully process loops and diamonds before proceeding +// onward to flow data elsewhere in the CFG. This avoids the wasted work problem +// where we have, say, an If, and process one arm, then look at the rest of a +// massive function, then process the other If arm, and the entire massive +// function must be recomputed. +// +// The BasicBlock of the CFG must contain two fields: +// +// bool inQueue; // whether already in the queue +// Index index; // basic block index +// +template struct RPOQueue { + CFG& cfg; + + RPOQueue(CFG& cfg) : cfg(cfg) { + // Initialize the block indexes and queue booleans. + auto& basicBlocks = cfg.basicBlocks; + for (Index i = 0; i < basicBlocks.size(); ++i) { + auto& contents = basicBlocks[i]->contents; + contents.inQueue = false; + contents.index = i; + } + } + + std::priority_queue, std::greater> queue; + + void push(CFG::BasicBlock* block) { + // Push if not already in the queue. + if (!block->contents.inQueue) { + block->contents.inQueue = true; + queue.push(block->contents.index); + } + } + + CFG::BasicBlock* pop() { + // Pop the smallest element (next in RPO), which is at the top. + auto* block = cfg.basicBlocks[queue.top()].get(); + queue.pop(); + block->contents.inQueue = false; + return block; + } + + bool empty() const { return queue.empty(); } +}; + +} // namespace wasm + +#endif // cfg_rpo_h + diff --git a/src/passes/ConstraintAnalysis.cpp b/src/passes/ConstraintAnalysis.cpp index 9e275665e9e..091e46b3657 100644 --- a/src/passes/ConstraintAnalysis.cpp +++ b/src/passes/ConstraintAnalysis.cpp @@ -69,6 +69,7 @@ #include #include "cfg/cfg-traversal.h" +#include "cfg/rpo.h" #include "ir/constraint.h" #include "ir/drop.h" #include "ir/eh-utils.h" @@ -96,6 +97,10 @@ namespace { // Information in a basic block. struct Info { + // For RPOQueue + bool inQueue; + Index index; + // All relevant operations: local gets and sets and uses of them. std::vector actions; @@ -337,7 +342,7 @@ struct ConstraintAnalysis } // Starting from the entry, keep going while we find something new. - UniqueDeferredQueue work; + RPOQueue work(*this); work.push(entry); while (!work.empty()) {