diff options
-rw-r--r-- | src/cfg/cfg-traversal.h | 18 | ||||
-rw-r--r-- | src/ir/LocalGraph.cpp | 8 | ||||
-rw-r--r-- | src/ir/local-graph.h | 8 | ||||
-rw-r--r-- | src/ir/possible-contents.cpp | 2 | ||||
-rw-r--r-- | src/passes/Heap2Local.cpp | 4 | ||||
-rw-r--r-- | src/passes/LocalSubtyping.cpp | 2 | ||||
-rw-r--r-- | src/passes/LoopInvariantCodeMotion.cpp | 2 | ||||
-rw-r--r-- | src/passes/MergeLocals.cpp | 4 | ||||
-rw-r--r-- | src/passes/OptimizeAddedConstants.cpp | 2 | ||||
-rw-r--r-- | src/passes/Precompute.cpp | 2 | ||||
-rw-r--r-- | src/passes/SSAify.cpp | 2 | ||||
-rw-r--r-- | test/gtest/cfg.cpp | 45 |
12 files changed, 78 insertions, 21 deletions
diff --git a/src/cfg/cfg-traversal.h b/src/cfg/cfg-traversal.h index 739e10e45..42f7f168a 100644 --- a/src/cfg/cfg-traversal.h +++ b/src/cfg/cfg-traversal.h @@ -295,11 +295,13 @@ struct CFGWalker : public PostWalker<SubType, VisitorType> { static void doEndCall(SubType* self, Expression** currp) { doEndThrowingInst(self, currp); - if (!self->throwingInstsStack.empty()) { - // exception not thrown. link to the continuation BB - auto* last = self->currBasicBlock; - self->link(last, self->startBasicBlock()); - } + // Create a new basic block and link to it. We do this even if there are no + // other edges leaving this call (no catch bodies in this function that we + // can reach if we throw), because we want to preserve the property that a + // basic block ends with an instruction that might branch, and the call + // might branch out of the entire function if it throws. + auto* last = self->currBasicBlock; + self->link(last, self->startBasicBlock()); } static void doStartTry(SubType* self, Expression** currp) { @@ -393,7 +395,11 @@ struct CFGWalker : public PostWalker<SubType, VisitorType> { case Expression::Id::CallId: case Expression::Id::CallIndirectId: case Expression::Id::CallRefId: { - self->pushTask(SubType::doEndCall, currp); + auto* module = self->getModule(); + if (!module || module->features.hasExceptionHandling()) { + // This call might throw, so run the code to handle that. + self->pushTask(SubType::doEndCall, currp); + } break; } case Expression::Id::TryId: { diff --git a/src/ir/LocalGraph.cpp b/src/ir/LocalGraph.cpp index 8e7d2ccc0..ab8ce16ba 100644 --- a/src/ir/LocalGraph.cpp +++ b/src/ir/LocalGraph.cpp @@ -41,9 +41,11 @@ struct Flower : public CFGWalker<Flower, Visitor<Flower>, Info> { Flower(LocalGraph::GetSetses& getSetses, LocalGraph::Locations& locations, - Function* func) + Function* func, + Module* module) : getSetses(getSetses), locations(locations) { setFunction(func); + setModule(module); // create the CFG by walking the IR CFGWalker<Flower, Visitor<Flower>, Info>::doWalkFunction(func); // flow gets across blocks @@ -227,8 +229,8 @@ struct Flower : public CFGWalker<Flower, Visitor<Flower>, Info> { // LocalGraph implementation -LocalGraph::LocalGraph(Function* func) : func(func) { - LocalGraphInternal::Flower flower(getSetses, locations, func); +LocalGraph::LocalGraph(Function* func, Module* module) : func(func) { + LocalGraphInternal::Flower flower(getSetses, locations, func, module); #ifdef LOCAL_GRAPH_DEBUG std::cout << "LocalGraph::dump\n"; diff --git a/src/ir/local-graph.h b/src/ir/local-graph.h index 111f5c073..8cc92d0fd 100644 --- a/src/ir/local-graph.h +++ b/src/ir/local-graph.h @@ -33,8 +33,12 @@ namespace wasm { struct LocalGraph { // main API - // the constructor computes getSetses, the sets affecting each get - LocalGraph(Function* func); + // The constructor computes getSetses, the sets affecting each get. + // + // If a module is passed in, it is used to find which features are needed in + // the computation (for example, if exception handling is disabled, then we + // can generate a simpler CFG, as calls cannot throw). + LocalGraph(Function* func, Module* module = nullptr); // The local.sets relevant for an index or a get. The most common case is to // have a single set; after that, to be a phi of 2 items, so we use a small diff --git a/src/ir/possible-contents.cpp b/src/ir/possible-contents.cpp index db2655e8a..3430bc872 100644 --- a/src/ir/possible-contents.cpp +++ b/src/ir/possible-contents.cpp @@ -1177,7 +1177,7 @@ struct InfoCollector assert(handledPops == totalPops); // Handle local.get/sets: each set must write to the proper gets. - LocalGraph localGraph(func); + LocalGraph localGraph(func, getModule()); for (auto& [get, setsForGet] : localGraph.getSetses) { auto index = get->index; diff --git a/src/passes/Heap2Local.cpp b/src/passes/Heap2Local.cpp index fccbc3321..7e2b02add 100644 --- a/src/passes/Heap2Local.cpp +++ b/src/passes/Heap2Local.cpp @@ -184,8 +184,8 @@ struct Heap2LocalOptimizer { Heap2LocalOptimizer(Function* func, Module* module, const PassOptions& passOptions) - : func(func), module(module), passOptions(passOptions), localGraph(func), - parents(func->body), branchTargets(func->body) { + : func(func), module(module), passOptions(passOptions), + localGraph(func, module), parents(func->body), branchTargets(func->body) { // We need to track what each set influences, to see where its value can // flow to. localGraph.computeSetInfluences(); diff --git a/src/passes/LocalSubtyping.cpp b/src/passes/LocalSubtyping.cpp index 0d41434fc..eeaaaaefa 100644 --- a/src/passes/LocalSubtyping.cpp +++ b/src/passes/LocalSubtyping.cpp @@ -60,7 +60,7 @@ struct LocalSubtyping : public WalkerPass<PostWalker<LocalSubtyping>> { // // TODO: Optimize this, as LocalGraph computes more than we need, and on // more locals than we need. - LocalGraph localGraph(func); + LocalGraph localGraph(func, getModule()); // For each local index, compute all the the sets and gets. std::vector<std::vector<LocalSet*>> setsForLocal(numLocals); diff --git a/src/passes/LoopInvariantCodeMotion.cpp b/src/passes/LoopInvariantCodeMotion.cpp index 1329c02a1..4239e39c3 100644 --- a/src/passes/LoopInvariantCodeMotion.cpp +++ b/src/passes/LoopInvariantCodeMotion.cpp @@ -49,7 +49,7 @@ struct LoopInvariantCodeMotion void doWalkFunction(Function* func) { // Compute all local dependencies first. - LocalGraph localGraphInstance(func); + LocalGraph localGraphInstance(func, getModule()); localGraph = &localGraphInstance; // Traverse the function. super::doWalkFunction(func); diff --git a/src/passes/MergeLocals.cpp b/src/passes/MergeLocals.cpp index a7f765cb4..c43ec8534 100644 --- a/src/passes/MergeLocals.cpp +++ b/src/passes/MergeLocals.cpp @@ -107,7 +107,7 @@ struct MergeLocals } // compute all dependencies auto* func = getFunction(); - LocalGraph preGraph(func); + LocalGraph preGraph(func, getModule()); preGraph.computeInfluences(); // optimize each copy std::unordered_map<LocalSet*, LocalSet*> optimizedToCopy, @@ -193,7 +193,7 @@ struct MergeLocals // if one does not work, we need to undo all its siblings (don't extend // the live range unless we are definitely removing a conflict, same // logic as before). - LocalGraph postGraph(func); + LocalGraph postGraph(func, getModule()); postGraph.computeSetInfluences(); for (auto& [copy, trivial] : optimizedToCopy) { auto& trivialInfluences = preGraph.setInfluences[trivial]; diff --git a/src/passes/OptimizeAddedConstants.cpp b/src/passes/OptimizeAddedConstants.cpp index 8b4793940..696aeaa1c 100644 --- a/src/passes/OptimizeAddedConstants.cpp +++ b/src/passes/OptimizeAddedConstants.cpp @@ -296,7 +296,7 @@ struct OptimizeAddedConstants helperIndexes.clear(); propagatable.clear(); if (propagate) { - localGraph = std::make_unique<LocalGraph>(func); + localGraph = std::make_unique<LocalGraph>(func, getModule()); localGraph->computeSetInfluences(); localGraph->computeSSAIndexes(); findPropagatable(); diff --git a/src/passes/Precompute.cpp b/src/passes/Precompute.cpp index 18139de3f..cddf8785f 100644 --- a/src/passes/Precompute.cpp +++ b/src/passes/Precompute.cpp @@ -370,7 +370,7 @@ private: // compute other sets as locals (since some of the gets they read may be // constant). // compute all dependencies - LocalGraph localGraph(func); + LocalGraph localGraph(func, getModule()); localGraph.computeInfluences(); // prepare the work list. we add things here that might change to a constant // initially, that means everything diff --git a/src/passes/SSAify.cpp b/src/passes/SSAify.cpp index 3d9b8bda6..fb952933e 100644 --- a/src/passes/SSAify.cpp +++ b/src/passes/SSAify.cpp @@ -89,7 +89,7 @@ struct SSAify : public Pass { void runOnFunction(Module* module_, Function* func_) override { module = module_; func = func_; - LocalGraph graph(func); + LocalGraph graph(func, module); graph.computeSetInfluences(); graph.computeSSAIndexes(); // create new local indexes, one for each set diff --git a/test/gtest/cfg.cpp b/test/gtest/cfg.cpp index 03e333965..7bf18ebdb 100644 --- a/test/gtest/cfg.cpp +++ b/test/gtest/cfg.cpp @@ -87,6 +87,51 @@ TEST_F(CFGTest, Print) { EXPECT_EQ(ss.str(), cfgText); } +TEST_F(CFGTest, CallBlock) { + // Verify that a call instruction ends the current basic block. Even if the + // call has no control flow edges inside the function (no catches it can + // reach), we still end a basic block with the call, so that we preserve the + // property of basic blocks ending in possibly-control-flow-transferring + // instructions. + auto moduleText = R"wasm( + (module + (func $foo + (drop + (i32.const 0) + ) + (call $bar) + (drop + (i32.const 1) + ) + ) + (func $bar) + ) + )wasm"; + + auto cfgText = R"cfg(;; preds: [], succs: [1] +0: + 0: i32.const 0 + 1: drop + 2: call $bar + +;; preds: [0], succs: [] +1: + 3: i32.const 1 + 4: drop + 5: block +)cfg"; + + Module wasm; + parseWast(wasm, moduleText); + + CFG cfg = CFG::fromFunction(wasm.getFunction("foo")); + + std::stringstream ss; + cfg.print(ss); + + EXPECT_EQ(ss.str(), cfgText); +} + TEST_F(CFGTest, LinearLiveness) { auto moduleText = R"wasm( (module |