diff options
author | Alon Zakai <azakai@google.com> | 2023-07-26 14:11:51 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-07-26 21:11:51 +0000 |
commit | afcbad033912e9a9201903525aba06f10d65c584 (patch) | |
tree | d10762001576fe4e9d109fe728311fddfbeb8f48 /src | |
parent | c015c9fee1e0af4d3a1da2ff435b020ff107abd8 (diff) | |
download | binaryen-afcbad033912e9a9201903525aba06f10d65c584.tar.gz binaryen-afcbad033912e9a9201903525aba06f10d65c584.tar.bz2 binaryen-afcbad033912e9a9201903525aba06f10d65c584.zip |
CFGWalker: Allow users to ignore branches outside the function [NFC] (#5838)
A pass that just operates on locals, for example, does not care about branches outside of
the function. That means that when we see a call, then even if EH is enabled we don't need
to create a new basic block right after it (unless the call is inside a try-catch - then it might
branch to the catch, of course).
This makes CFG-using passes 9% faster compared to before this PR. This plus #5827 offset
the slowdown from #5823 and overall give an improvement compared to before.
Diffstat (limited to 'src')
-rw-r--r-- | src/cfg/cfg-traversal.h | 33 | ||||
-rw-r--r-- | src/ir/LocalGraph.cpp | 4 | ||||
-rw-r--r-- | src/passes/CoalesceLocals.cpp | 4 | ||||
-rw-r--r-- | src/passes/RedundantSetElimination.cpp | 4 |
4 files changed, 38 insertions, 7 deletions
diff --git a/src/cfg/cfg-traversal.h b/src/cfg/cfg-traversal.h index 42f7f168a..4ae075049 100644 --- a/src/cfg/cfg-traversal.h +++ b/src/cfg/cfg-traversal.h @@ -293,15 +293,34 @@ struct CFGWalker : public PostWalker<SubType, VisitorType> { } } + // We can optionally ignore branches to outside of the function. Such a branch + // does not link two basic blocks (since the target is outside of the + // function), but it can cause us to end the current basic block and link to a + // new one, just in order to preserve the property that blocks do not have + // instructions in the middle that can transfer control flow somewhere. That + // property is useful to have in general, but if a user of this code just does + // not care about what happens when we leave the current function (say, if it + // only reads locals, which are gone anyhow if we leave) then it can flip this + // option to avoid creating new blocks just for such branches. + // + // The main situation where this matters is calls, which can throw if EH is + // enabled. With this set to ignore, we don't create new basic blocks just + // because of that, which can save a significant amount of overhead (~10%). + bool ignoreBranchesOutsideOfFunc = false; + static void doEndCall(SubType* self, Expression** currp) { doEndThrowingInst(self, currp); - // 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()); + if (!self->throwingInstsStack.empty() || + !self->ignoreBranchesOutsideOfFunc) { + // |doEndThrowingInst| added a link from the current block to a catch, so + // we must end the current block and start another. Or, we are not + // ignoring branches to outside of the function, so even without a branch + // to a catch we want to start a new basic block here, to preserve the + // property that control flow transfers (both within the function or to + // the outside) can only happen at the end of basic blocks. + auto* last = self->currBasicBlock; + self->link(last, self->startBasicBlock()); + } } static void doStartTry(SubType* self, Expression** currp) { diff --git a/src/ir/LocalGraph.cpp b/src/ir/LocalGraph.cpp index ab8ce16ba..af3a71d1f 100644 --- a/src/ir/LocalGraph.cpp +++ b/src/ir/LocalGraph.cpp @@ -54,6 +54,10 @@ struct Flower : public CFGWalker<Flower, Visitor<Flower>, Info> { BasicBlock* makeBasicBlock() { return new BasicBlock(); } + // Branches outside of the function can be ignored, as we only look at locals + // which vanish when we leave. + bool ignoreBranchesOutsideOfFunc = true; + // cfg traversal work static void doVisitLocalGet(Flower* self, Expression** currp) { diff --git a/src/passes/CoalesceLocals.cpp b/src/passes/CoalesceLocals.cpp index ce36b3c69..eb3babeb0 100644 --- a/src/passes/CoalesceLocals.cpp +++ b/src/passes/CoalesceLocals.cpp @@ -55,6 +55,10 @@ struct CoalesceLocals return std::make_unique<CoalesceLocals>(); } + // Branches outside of the function can be ignored, as we only look at locals + // which vanish when we leave. + bool ignoreBranchesOutsideOfFunc = true; + // main entry point void doWalkFunction(Function* func); diff --git a/src/passes/RedundantSetElimination.cpp b/src/passes/RedundantSetElimination.cpp index 4f82d5181..d54863639 100644 --- a/src/passes/RedundantSetElimination.cpp +++ b/src/passes/RedundantSetElimination.cpp @@ -68,6 +68,10 @@ struct RedundantSetElimination return std::make_unique<RedundantSetElimination>(); } + // Branches outside of the function can be ignored, as we only look at locals + // which vanish when we leave. + bool ignoreBranchesOutsideOfFunc = true; + Index numLocals; // In rare cases we make a change to a type that requires a refinalize. |