Bug #81015 [Ver->Ana]: Opcache optimization assumes wrong part of ternary operator in if-condition
Edit report at https://bugs.php.net/bug.php?id=81015&edit=1
ID: 81015
Updated by: nikic@php.net
Reported by: redwormik at gmail dot com
Summary: Opcache optimization assumes wrong part of ternary
operator in if-condition
-Status: Verified
+Status: Analyzed
Type: Bug
Package: opcache
Operating System: Alpine 3.13 (Docker)
PHP Version: 8.0.5
Block user comment: N
Private report: N
New Comment:
This happens due to an incorrect pi-node placement:
0000 #2.CV0($enabled) [bool] RANGE[0..1] = RECV 1
0001 #3.CV1($value) [null, string] = RECV 2
0002 JMPZ #2.CV0($enabled) [bool] RANGE[0..1] BB2
BB1:
; follow lines=[3-4]
; from=(BB0)
; to=(BB3)
; idom=BB0
; level=1
0003 #4.T2 [true] RANGE[1..1] = QM_ASSIGN bool(true)
0004 JMP BB3
BB2:
; target lines=[5-5]
; from=(BB0)
; to=(BB3)
; idom=BB0
; level=1
0005 #5.T2 [bool] = TYPE_CHECK (null) #3.CV1($value) [null, string]
BB3:
; follow target lines=[6-6]
; from=(BB1, BB2)
; to=(BB7, BB4)
; idom=BB0
; level=1
; children=(BB4, BB7)
#6.X2 [bool] = Phi(#4.X2 [true] RANGE[1..1], #5.X2 [bool])
0006 JMPZ #6.T2 [bool] BB7
BB4:
; follow lines=[7-7]
; from=(BB3)
; to=(BB6, BB5)
; idom=BB3
; level=2
; children=(BB5, BB6)
#7.CV1($value) [null] = Pi<BB3>(#3.CV1($value) [null, string] & TYPE [undef, ref,
null])
0007 #8.T4 [] = COALESCE #7.CV1($value) [null] BB6
A pi node was placed in BB4 based on the JMPZ of TYPE_CHECK, but what that didn't account for
is that this TYPE_CHECK is only one of the possible values of T2 at that point.
Previous Comments:
------------------------------------------------------------------------
[2021-05-06 04:17:55] redwormik at gmail dot com
Sorry, changed the output at last minute, but did not change the script. The condition should be
(the else-branch output changed):
if ($enabled ? true : $value === null) {
echo ($value ?? 'NULL') . "\n";
} else {
echo "INVALID " . ($value ?? 'NULL') . "\n";
}
------------------------------------------------------------------------
[2021-05-06 04:14:41] redwormik at gmail dot com
Description:
------------
I have opcache enabled and opcache.optimization_level at least 0x000000b0 (bits 4, 5 and 8 all set,
other irrelevant).
When I compare a variable to NULL in a "false" part of a ternary operator in an
if-condition, the variable changes to NULL in the respective branch of the if-condition (i.e. if
comparison is === NULL, the variable changes to NULL in the if-branch; if comparison is !== NULL,
the variable changes to NULL in the else-branch), even though the ternary operator condition is
true.
Returning the value, changing the condition so it does not use ternary operator, moving the
NULL-equality out of the condition to a local variable or comparing to anything else other than NULL
all work as expected.
Test script:
---------------
<?php
declare(strict_types=1);
function ternary(bool $enabled, ?string $value): void
{
// the "true" part is not as trivial in the real case
if ($enabled ? true : $value === null) {
echo ($value ?? 'NULL') . "\n";
} else {
echo "INVALID\n";
}
}
ternary(true, 'value');
ternary(true, null);
ternary(false, 'value');
ternary(false, null);
Expected result:
----------------
value
NULL
INVALID value
NULL
Actual result:
--------------
NULL
NULL
INVALID value
NULL
------------------------------------------------------------------------
--
Edit this bug report at https://bugs.php.net/bug.php?id=81015&edit=1
Thread (5 messages)