summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/cfg/cfg-traversal.h18
-rw-r--r--src/ir/LocalGraph.cpp8
-rw-r--r--src/ir/local-graph.h8
-rw-r--r--src/ir/possible-contents.cpp2
-rw-r--r--src/passes/Heap2Local.cpp4
-rw-r--r--src/passes/LocalSubtyping.cpp2
-rw-r--r--src/passes/LoopInvariantCodeMotion.cpp2
-rw-r--r--src/passes/MergeLocals.cpp4
-rw-r--r--src/passes/OptimizeAddedConstants.cpp2
-rw-r--r--src/passes/Precompute.cpp2
-rw-r--r--src/passes/SSAify.cpp2
-rw-r--r--test/gtest/cfg.cpp45
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