Open sunfishcode opened 8 years ago
Thanks, interesting problem. Not sure what to do here. Maybe the two main options are
What do you suggest? Is there a standard compiler behavior for stuff like this?
An option for controlling imprecise optimizations sounds reasonable to me. I can see uses for optimizing arbitrary wasm without changing its behavior in any way; you could do things like run the spec tests with that optimization run on them, and they should all continue to pass. I can also see uses for developers who know they don't care about observing every last store in the event of a trap.
Ok, what's a conventional name for such a thing? --fast-math
perhaps? Or should it be more specific like --assume-math-cannot-trap
?
--fast-math
typically implies a lot of other things, like relaxed precision, so that doesn't seem quite right. I like --assume-math-cannot-trap
. Or how about --assume-no-math-traps
?
+1 to not changing the behavior in the default case and having a compiler option.
AFAIU, C undefined behavior would allow this reordering within the same control equivalence class; i.e. whenever undefined behavior becomes inevitable, that entire execution path becomes invalid and the compiler is free to do what it likes. So for producers coming from C, this optimization is OK (provided it doesn't lift across non-terminating calls). But from a pure WASM perspective this optimization is unsound.
Why even have the option? Is it useful?
@jfbastien The option would prevent disabling some code reduction optimizations, so it sounds useful. Or do you think it would be rare in practice to see such potentially-trapping operations be movable?
Right, I would just respect the compiler's output and not even try doing more. No need for the option, just don't do it.
I guess you're thinking of LLVM-generated code? But another use case is for a compiler to let Binaryen do optimization for it. It could then say, optimize under this assumption, or not.
On more thought, I'd like to generalize this to not just math. For example, it is nice if the optimizer can assume that loads do not trap unless told otherwise.
How about --assume-no-implicit-traps
? (Implicit, since (unreachable)
should still trap even in this case?)
One thing to watch out for is moving trapping operations themselves, since they may have a control condition that guards that they don't trap. (e.g. x != 0? 3 / x : 5)
Given the following input:
binaryen-shell with --simplify-locals produces this:
The
i32.div_s
was moved past the first store. This is an observable change in behavior, because if $y is 0, the divide traps, and in the original program the first store doesn't happen. In the transformed code, the store does happen.