From 952286421a19fb8358d645f49d455b75bfbd1d19 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 3 Sep 2024 16:09:08 -0700 Subject: [PATCH] [NFC] Convert HeapStoreOptimization to use a CFG (#6896) This does not use the CFG yet, so there is no benefit (and likely some small slowdown). The next PR will actually use it to fix a correctness bug. This PR only sets up the CFG and converts the pass to operate on it, without changing any behavior or tests. Followup to #6882 --- src/passes/HeapStoreOptimization.cpp | 54 +++++++++++++++++++++++++--- 1 file changed, 49 insertions(+), 5 deletions(-) diff --git a/src/passes/HeapStoreOptimization.cpp b/src/passes/HeapStoreOptimization.cpp index 566290c3643..f227dc53631 100644 --- a/src/passes/HeapStoreOptimization.cpp +++ b/src/passes/HeapStoreOptimization.cpp @@ -20,6 +20,7 @@ // TODO: Add dead store elimination / load forwarding here. // +#include "cfg/cfg-traversal.h" #include "ir/effects.h" #include "pass.h" #include "wasm-builder.h" @@ -27,8 +28,17 @@ namespace wasm { +namespace { + +// In each basic block we will store the relevant heap store operations and +// other actions that matter to our analysis. +struct Info { + std::vector actions; +}; + struct HeapStoreOptimization - : public WalkerPass> { + : public WalkerPass< + CFGWalker, Info>> { bool isFunctionParallel() override { return true; } // Locals are not modified here. @@ -38,7 +48,39 @@ struct HeapStoreOptimization return std::make_unique(); } - void visitStructSet(StructSet* curr) { + // Branches outside of the function can be ignored, as we only look at local + // state in the function. (This may need to change if we do more general dead + // store elimination.) + bool ignoreBranchesOutsideOfFunc = true; + + // Store struct.sets and blocks, as we can find patterns among those. + void addAction() { + if (currBasicBlock) { + currBasicBlock->contents.actions.push_back(getCurrentPointer()); + } + } + void visitStructSet(StructSet* curr) { addAction(); } + void visitBlock(Block* curr) { addAction(); } + + void visitFunction(Function* curr) { + // Now that the walk is complete and we have a CFG, find things to optimize. + for (auto& block : basicBlocks) { + for (auto** currp : block->contents.actions) { + auto* curr = *currp; + if (auto* set = curr->dynCast()) { + optimizeStructSet(set, currp); + } else if (auto* block = curr->dynCast()) { + optimizeBlock(block); + } else { + WASM_UNREACHABLE("bad action"); + } + } + } + } + + // Optimize a struct.set. Receives also a pointer to where it is referred to, + // so we can replace it (which we do if we optimize). + void optimizeStructSet(StructSet* curr, Expression** currp) { // If our reference is a tee of a struct.new, we may be able to fold the // stored value into the new itself: // @@ -52,7 +94,7 @@ struct HeapStoreOptimization // Success, so we do not need the struct.set any more, and the tee // can just be a set instead of us. tee->makeSet(); - replaceCurrent(tee); + *currp = tee; } } } @@ -69,9 +111,9 @@ struct HeapStoreOptimization // We also handle other struct.sets immediately after this one. If the // instruction following the new is not a struct.set we push the new down if // possible. - void visitBlock(Block* curr) { optimizeHeapStores(curr->list); } + void optimizeBlock(Block* curr) { + auto& list = curr->list; - void optimizeHeapStores(ExpressionList& list) { for (Index i = 0; i < list.size(); i++) { auto* localSet = list[i]->dynCast(); if (!localSet) { @@ -234,6 +276,8 @@ struct HeapStoreOptimization } }; +} // anonymous namespace + Pass* createHeapStoreOptimizationPass() { return new HeapStoreOptimization(); } } // namespace wasm