Optimization should not replace strictEqual with notEqual
cardillan opened this issue · comments
There's a problem in the ImproveConditionalJumps optimizer: it assumes that notEqual
is the opposite operation to strictEqual
, but that isn't true: notEqual
coerces types, and won't therefore distinguish between null
and 0
.
It can lead to compiled code that doesn't work as intended. This code snippet:
if @unit.dead === 0
print("alive")
end
is meant to print "alive" it the @unit
variable is not null
and its @dead
property is equal to 0
(in other words, only if the unit is bound and alive). Optimized code, however, looks like this:
sensor __tmp0 @unit @dead
jump 4 notEqual __tmp0 0
print "alive"
jump 0 always 0 0
end
This will print "alive" even if there's no bound unit (and the @unit
variable is therefore null
).
I think ImproveConditionalJumps shouldn't modify strictEqual
comparisons.
Good point. I'm willing to see and merge a pull request that implements such a fix.
The fix itself would be trivial, but I see other problems with ImproveConditionalJumps:
- This class seems to predate other optimizers and doesn't use the state-based instruction traversal. I'd say the state-based approach is more readable, and is sort of a standard in other optimizers.
- The class calls
flush()
frequently; if there were other optimizers down the line, they might not work (I actually stumbled upon this). - Most importantly, the code merges an
op
comparison instruction with ajump notEqual
instruction without verifying - by inspecting the arguments - that the jump operates on the op output or that it really compares it totrue
. I believe it was based on the knowledge of how the compiler works. Future changes in code generation or other optimizers might cause the assumption to fail, leading to merging instructions that shouldn't be merged.
Would you be able to review a PR if I rewrote the class completely to use the state-based approach and verify the instructions matched the prerequisites for merging? It would be a slightly larger change. I'd separate the rewrite from the fix, so that the unit tests could be run on commit containing just the rewrite.