summaryrefslogtreecommitdiff
path: root/src/ir/drop.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/ir/drop.h')
-rw-r--r--src/ir/drop.h83
1 files changed, 79 insertions, 4 deletions
diff --git a/src/ir/drop.h b/src/ir/drop.h
index 9ed5276ff..a13b83332 100644
--- a/src/ir/drop.h
+++ b/src/ir/drop.h
@@ -17,6 +17,7 @@
#ifndef wasm_ir_drop_h
#define wasm_ir_drop_h
+#include "ir/branch-utils.h"
#include "ir/effects.h"
#include "ir/iteration.h"
#include "wasm-builder.h"
@@ -32,10 +33,10 @@ namespace wasm {
//
// The caller must also pass in a last item to append to the output (which is
// typically what the original expression is replaced with).
-Expression* getDroppedChildrenAndAppend(Expression* curr,
- Module& wasm,
- const PassOptions& options,
- Expression* last) {
+inline Expression* getDroppedChildrenAndAppend(Expression* curr,
+ Module& wasm,
+ const PassOptions& options,
+ Expression* last) {
Builder builder(wasm);
std::vector<Expression*> contents;
for (auto* child : ChildIterator(curr)) {
@@ -57,6 +58,80 @@ Expression* getDroppedChildrenAndAppend(Expression* curr,
return builder.makeBlock(contents);
}
+// As the above, but only operates on children that execute unconditionally.
+// That is the case in almost all expressions, except for those with
+// conditional execution, like if, which unconditionally executes the condition
+// but then conditionally executes one of the two arms. The above function
+// simply returns all children in order, so it does this to if:
+//
+// (if
+// (condition)
+// (arm-A)
+// (arm-B)
+// )
+// =>
+// (drop
+// (condition)
+// )
+// (drop
+// (arm-A)
+// )
+// (drop
+// (arm-B)
+// )
+// (appended last item)
+//
+// This is dangerous as it executes what were conditional children in an
+// unconditional way. To avoid that issue, this function will only operate on
+// unconditional children, and keep conditional ones as they were. That means
+// it will not split up and drop the children of an if, for example. All we do
+// in that case is drop the entire if and append the last item:
+//
+// (drop
+// (if
+// (condition)
+// (arm-A)
+// (arm-B)
+// )
+// )
+// (appended last item)
+//
+// Also this function preserves other unremovable expressions like trys and
+// pops.
+inline Expression*
+getDroppedUnconditionalChildrenAndAppend(Expression* curr,
+ Module& wasm,
+ const PassOptions& options,
+ Expression* last) {
+ // We check for shallow effects here, since we may be able to remove |curr|
+ // itself but keep its children around - we don't want effects in the children
+ // to stop us from improving the code. Note that there are cases where the
+ // combined curr+children has fewer effects than curr itself, such as if curr
+ // is a block and the child branches to it, but in such cases we cannot remove
+ // curr anyhow (those cases are ruled out below), so looking at non-shallow
+ // effects would never help us (and would be slower to run).
+ ShallowEffectAnalyzer effects(options, wasm, curr);
+ // Ignore a trap, as the unreachable replacement would trap too.
+ if (last->type == Type::unreachable) {
+ effects.trap = false;
+ }
+
+ // We cannot remove
+ // 1. Expressions with unremovable side effects
+ // 2. if: 'if's contains conditional expressions
+ // 3. try: Removing a try could leave a pop without a proper parent
+ // 4. pop: Pops are struturally necessary in catch bodies
+ // 5. Branch targets: We will need the target for the branches to it to
+ // validate.
+ if (effects.hasUnremovableSideEffects() || curr->is<If>() ||
+ curr->is<Try>() || curr->is<Pop>() ||
+ BranchUtils::getDefinedName(curr).is()) {
+ Builder builder(wasm);
+ return builder.makeSequence(builder.makeDrop(curr), last);
+ }
+ return getDroppedChildrenAndAppend(curr, wasm, options, last);
+}
+
} // namespace wasm
#endif // wasm_ir_drop_h