Previously pi placement happened after initial phi placement.
Afterwards a second phi placement pass was performed, however it
incorrectly only placed phis on the dominance frontier, rather
than the iterated dominance frontier.
This is fixed by moving pi placement before the propagation of
defs on the iterated DFs, and adding a def for each added pi.
While this ensures that we generate correct conservative SSA, there
is still one remaining case in which we may generate non-minimal
SSA form. Consider:
|1|
|pi
v
|2|<--\
| |
\----/
The pi is semanically located along the edge 1->2, however we place
it (and its def point) in 2, thus leading to the generation of an
additional (trivial) phi in 2.
Conflicts:
ext/opcache/Optimizer/zend_ssa.c
This interdependence is problematic because we can't propagate pi
def points in the initial dominance frontier propagation. The used
rule for multiple-predecessor blocks may also miss cases where
placing the pi would have been useful.
The new heuristic for pi placement checks that a) the variable is
live-in and b) for the "from" block that generated the pi, the
other successor does not dominate all other predecessors of the
"to" block.
The purpose of case b) may be illustrated with an example:
if (is_int($i)) {
// place pi here
}
// but don't place pi here
The reason we do not want to place the second pi is that we generally
place pis in positive+negative pairs, and in this case the pair
would merge in a phi and cancel out, so we get no useful information
out of it.
For live-variable analysis it does not matter if def includes
variables that are previously use in the same block, the data flow
equations still have the same result. As such there is no need to
compute separate gen & def sets.
I'm keeping the name "def", because use of "gen" in this context is
pretty confusing (gen is usually the use set, not the def set).
Yield-by-ref defs a ref var, yield-by-var only defs an rc1/rcn var
if rc inference is used.
Also move BIND_LEXICAL where it belongs in DFG construction.
This opcodes inserts a local CV into the closure static variable
table. This replaces the previous mechanism of having static
variables marked as LEXICAL, which perform a symtable lookup
during copying.
This means a) functions which contain closures no longer have to
rebuild their symtable (better performance) and b) we can now track
used variables in SSA.
-LONG_MIN == LONG_MIN so bad things will happen.
Example of JIT miscompile:
function test($n) {
if ($n + PHP_INT_MIN == 0) {
$n2 = (int) ($n + PHP_INT_MAX);
var_dump($n2);
}
}
test(PHP_INT_MAX + 1);