diff options
Diffstat (limited to 'src/ir/effects.h')
-rw-r--r-- | src/ir/effects.h | 47 |
1 files changed, 21 insertions, 26 deletions
diff --git a/src/ir/effects.h b/src/ir/effects.h index 5578b3b3b..aa8a458d0 100644 --- a/src/ir/effects.h +++ b/src/ir/effects.h @@ -90,18 +90,20 @@ public: // each other, but it is not ok to remove them or reorder them with other // effects in a noticeable way. // - // Note also that we ignore runtime-dependent traps, such as hitting a - // recursion limit or running out of memory. Such traps are not part of wasm's - // official semantics, and they can occur anywhere: *any* instruction could in - // theory be implemented by a VM call (as will be the case when running in an - // interpreter), and such a call could run out of stack or memory in - // principle. To put it another way, an i32 division by zero is the program - // doing something bad that causes a trap, but the VM running out of memory is - // the VM doing something bad - and therefore the VM behaving in a way that is - // not according to the wasm semantics - and we do not model such things. Note - // that as a result we do *not* mark things like GC allocation instructions as - // having side effects, which has the nice benefit of making it possible to - // eliminate an allocation whose result is not captured. + // Note also that we ignore *optional* runtime-specific traps: we only + // consider as trapping something that will trap in *all* VMs, and *all* the + // time. For example, a single allocation might trap in a VM in a particular + // execution, if it happens to run out of memory just there, but that is not + // enough for us to mark it as having a trap effect. (Note that not marking + // each allocation as possibly trapping has the nice benefit of making it + // possible to eliminate an allocation whose result is not captured.) OTOH, we + // *do* mark a potentially infinite number of allocations as trapping, as all + // VMs would trap eventually, and the same for potentially infinite recursion, + // etc. + // * We assume that VMs will timeout eventually, so any loop that we cannot + // prove terminates is considered to trap. (Some VMs might not have + // such timeouts, but even they will error before the heat death of the + // universe, which is a kind of trap.) bool trap = false; // A trap from an instruction like a load or div/rem, which may trap on corner // cases. If we do not ignore implicit traps then these are counted as a trap. @@ -394,20 +396,13 @@ private: } void visitIf(If* curr) {} void visitLoop(Loop* curr) { - if (curr->name.is()) { - parent.breakTargets.erase(curr->name); // these were internal breaks - } - // if the loop is unreachable, then there is branching control flow: - // (1) if the body is unreachable because of a (return), uncaught (br) - // etc., then we already noted branching, so it is ok to mark it - // again (if we have *caught* (br)s, then they did not lead to the - // loop body being unreachable). (same logic applies to blocks) - // (2) if the loop is unreachable because it only has branches up to the - // loop top, but no way to get out, then it is an infinite loop, and - // we consider that a branching side effect (note how the same logic - // does not apply to blocks). - if (curr->type == Type::unreachable) { - parent.branchesOut = true; + if (curr->name.is() && parent.breakTargets.erase(curr->name) > 0) { + // Breaks to this loop exist, which we just removed as they do not have + // further effect outside of this loop. One additional thing we need to + // take into account is infinite looping, which is a noticeable side + // effect we can't normally remove - eventually the VM will time out and + // error (see more details in the comment on trapping above). + parent.implicitTrap = true; } } void visitBreak(Break* curr) { parent.breakTargets.insert(curr->name); } |