-
Notifications
You must be signed in to change notification settings - Fork 368
Description
Phan's -x (dead code detection) mode is quite simplistic and basically only looks for functions and methods that are never called. Starting with PHP 7.2, but much more so in PHP 7.3 the opcache optimizer does pretty advanced DCE & SCCP. Take this example:
<?php
class C {
public $i;
}
function fn(int $x) {
$c = new C;
$c->i = 1;
if($x) {
$a = [1, 2, 3];
} else {
$a = [3, 2, 1];
}
return $a[$c->i];
$c->i++;
return $x;
}
fn(1);This is obviously horrible code, but phan -x has no complaints. Not even for the 2nd return in the function. If we run it through the optimizer like this:
php -d opcache.optimization_level=-1 -d opcache.opt_debug_level=0x20020000 -l script.php (using PHP 7.3) we get:
SCCP Values for "fn":
#4.X3 = {}
#5.CV1($c) = {}
#6.CV1($c) = {"i" => int(1)}
#7.CV2($a) = array(...)
#8.CV2($a) = array(...)
#9.CV2($a) = [1 => int(2)]
#10.X4 = int(1)
#11.X3 = int(2)
$_main: ; (lines=4, args=0, vars=0, tmps=0)
; (after optimizer)
; script.php:1-19
L0: INIT_FCALL 1 96 string("fn")
L1: SEND_VAL int(1) 1
L2: DO_UCALL
L3: RETURN int(1)
fn: ; (lines=2, args=1, vars=1, tmps=0)
; (after optimizer)
; script.php:6-17
L0: CV0($x) = RECV 1
L1: RETURN int(2)
No syntax errors detected in d6The big block of SCCP values for the fn function tells us that the Sparse Conditional Constant Propogation pass kicked in big time on this code and we see from the final opcodes that the entire fn function was reduced to the equivalent of:
function fn($x) {
return 2;
}We should figure out some way to surface this from Phan. The opcache debug output has a range of line numbers on each block of opcodes, but it isn't showing it on a per-opcode basis even though each opcode does have a line number internally. We might need to change that debug output, or at least add an option to make it more verbose so we can provide more specific warnings from Phan on code like this. But since it also rewrites the code (replacing $c->i with 1 in the return call in this case) we can't easily exactly describe what the code was reduced to unless we write some translator to go from opcodes back to PHP code. The easiest is to just indicate that the block of code indicated by the script.php:6-17 range contains dead/redundant code.