Skip to content

Commit 87d728d

Browse files
authored
Fix GH-24063: SCCP merges 0.0 and -0.0 into a single constant (#24064)
SCCP joins phi values with zend_is_identical(), which compares doubles with ==, so 0.0 and -0.0 count as the same value. A ternary like $n < 0 ? -0.0 : 0.0 then gets folded to whichever constant the first branch held, and the condition disappears. The same thing happens for constant arrays and for elements of partial arrays. The join now compares doubles bitwise (like compact_literals.c already does), recursing into arrays, and falls back to zend_is_identical() for everything else. Fixes GH-24063
1 parent 7b75d71 commit 87d728d

3 files changed

Lines changed: 72 additions & 2 deletions

File tree

‎NEWS‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,8 @@ PHP NEWS
108108
(David Carlier)
109109
. Fix multiple incorrect DCE due to unsound escape analysis. (ndossche,
110110
arnaud-lb)
111+
. Fixed bug GH-24063 (OPcache optimizer folds `$cond ? -0.0 : 0.0` into a
112+
single `-0.0` constant). (lazerg)
111113

112114
- OpenSSL:
113115
. Fixed stream_socket_enable_crypto() leaving the socket non-blocking

‎Zend/Optimizer/sccp.c‎

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1946,6 +1946,28 @@ static void sccp_mark_feasible_successors(
19461946
scdf_mark_edge_feasible(scdf, block_num, block->successors[s]);
19471947
}
19481948

1949+
/* Unlike zend_is_identical(), this does not treat 0.0 and -0.0 as the same value.
1950+
* Returns int to be usable as compare_func_t. */
1951+
static int sccp_values_differ(const void *p1, const void *p2)
1952+
{
1953+
const zval *a = p1;
1954+
const zval *b = p2;
1955+
1956+
if (Z_TYPE_P(a) != Z_TYPE_P(b)) {
1957+
return 1;
1958+
}
1959+
if (Z_TYPE_P(a) == IS_DOUBLE) {
1960+
return memcmp(&Z_DVAL_P(a), &Z_DVAL_P(b), sizeof(double)) != 0;
1961+
}
1962+
if (Z_TYPE_P(a) == IS_ARRAY) {
1963+
return Z_ARRVAL_P(a) != Z_ARRVAL_P(b)
1964+
&& zend_hash_compare(Z_ARRVAL_P(a), Z_ARRVAL_P(b), sccp_values_differ, 1) != 0;
1965+
}
1966+
ZEND_ASSERT(IS_PARTIAL_ARRAY(a) || IS_PARTIAL_OBJECT(a)
1967+
|| ((1 << Z_TYPE_P(a)) & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_BOOL|MAY_BE_LONG|MAY_BE_STRING)));
1968+
return !zend_is_identical(a, b);
1969+
}
1970+
19491971
static void join_hash_tables(HashTable *ret, HashTable *ht1, HashTable *ht2)
19501972
{
19511973
zend_ulong index;
@@ -1958,7 +1980,7 @@ static void join_hash_tables(HashTable *ret, HashTable *ht1, HashTable *ht2)
19581980
} else {
19591981
val2 = zend_hash_index_find(ht2, index);
19601982
}
1961-
if (val2 && zend_is_identical(val1, val2)) {
1983+
if (val2 && !sccp_values_differ(val1, val2)) {
19621984
if (key) {
19631985
val1 = zend_hash_add_new(ret, key, val1);
19641986
} else {
@@ -2026,7 +2048,7 @@ static void join_phi_values(zval *a, zval *b, bool escape) {
20262048
zval_ptr_dtor_nogc(a);
20272049
MAKE_BOT(a);
20282050
}
2029-
} else if (!zend_is_identical(a, b)) {
2051+
} else if (sccp_values_differ(a, b)) {
20302052
if (join_partial_arrays(a, b) == FAILURE) {
20312053
zval_ptr_dtor_nogc(a);
20322054
MAKE_BOT(a);

‎ext/opcache/tests/opt/gh24063.phpt‎

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
--TEST--
2+
GH-24063 (SCCP merges 0.0 and -0.0 into a single constant)
3+
--EXTENSIONS--
4+
opcache
5+
--INI--
6+
opcache.enable=1
7+
opcache.enable_cli=1
8+
opcache.optimization_level=-1
9+
--FILE--
10+
<?php
11+
12+
function scalar(float $n): float
13+
{
14+
$r = $n < 0 ? -0.0 : 0.0;
15+
return $r;
16+
}
17+
18+
function arr(float $n): array
19+
{
20+
$r = $n < 0 ? [-0.0] : [0.0];
21+
return $r;
22+
}
23+
24+
function partial(float $n, $x): float
25+
{
26+
$a = ['x' => $x];
27+
if ($n < 0) {
28+
$a['k'] = -0.0;
29+
} else {
30+
$a['k'] = 0.0;
31+
}
32+
return $a['k'];
33+
}
34+
35+
var_dump(scalar(1.0), scalar(-1.0));
36+
var_dump(arr(1.0)[0], arr(-1.0)[0]);
37+
var_dump(partial(1.0, 1), partial(-1.0, 1));
38+
39+
?>
40+
--EXPECT--
41+
float(0)
42+
float(-0)
43+
float(0)
44+
float(-0)
45+
float(0)
46+
float(-0)

0 commit comments

Comments
 (0)