summaryrefslogtreecommitdiff
path: root/src/passes/Inlining.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/passes/Inlining.cpp')
-rw-r--r--src/passes/Inlining.cpp41
1 files changed, 33 insertions, 8 deletions
diff --git a/src/passes/Inlining.cpp b/src/passes/Inlining.cpp
index 7df5c8f29..1732c1a2c 100644
--- a/src/passes/Inlining.cpp
+++ b/src/passes/Inlining.cpp
@@ -404,14 +404,39 @@ static Expression* doInlining(Module* module,
updater.walk(contents);
block->list.push_back(contents);
block->type = retType;
- // If the function returned a value, we just set the block containing the
- // inlined code to have that type. or, if the function was void and
- // contained void, that is fine too. a bad case is a void function in which
- // we have unreachable code, so we would be replacing a void call with an
- // unreachable.
- if (contents->type == Type::unreachable && block->type == Type::none) {
- // Make the block reachable by adding a break to it
- block->list.push_back(builder.makeBreak(block->name));
+ // The ReFinalize below will handle propagating unreachability if we need to
+ // do so, that is, if the call was reachable but now the inlined content we
+ // replaced it with was unreachable. The opposite case requires special
+ // handling: ReFinalize works under the assumption that code can become
+ // unreachable, but it does not go back from that state. But inlining can
+ // cause that:
+ //
+ // (call $A ;; an unreachable call
+ // (unreachable)
+ // )
+ // =>
+ // (block $__inlined_A_body (result i32) ;; reachable code after inlining
+ // (unreachable)
+ // )
+ //
+ // That is, if the called function wraps the input parameter in a block with a
+ // declared type, then the block is not unreachable. And then we might error
+ // if the outside expects the code to be unreachable - perhaps it only
+ // validates that way. To fix this, if the call was unreachable then we make
+ // the inlined code unreachable as well. That also maximizes DCE
+ // opportunities by propagating unreachability as much as possible.
+ //
+ // (Note that we don't need to do this for a return_call, which is always
+ // unreachable anyhow.)
+ if (call->type == Type::unreachable && !call->isReturn) {
+ // Make the replacement code unreachable. Note that we can't just add an
+ // unreachable at the end, as the block might have breaks to it (returns are
+ // transformed into those).
+ Expression* old = block;
+ if (old->type.isConcrete()) {
+ old = builder.makeDrop(old);
+ }
+ *action.callSite = builder.makeSequence(old, builder.makeUnreachable());
}
// Anything we inlined into may now have non-unique label names, fix it up.
// Note that we must do this before refinalization, as otherwise duplicate