[php-src] PHP-8.4: Fix GH-24063: SCCP merges 0.0 and -0.0 into a single constant (#24064)

From: Date: Fri, 02 Oct 2026 14:20:55 +0000
Subject: [php-src] PHP-8.4: Fix GH-24063: SCCP merges 0.0 and -0.0 into a single constant (#24064)
Groups: php.cvs 
Request: Send a blank email to php-cvs+get-141880@lists.php.net to get a copy of this message
Author: Lazizbek Ergashev (lazerg) Committer: GitHub (web-flow) Pusher: arnaud-lb Date: 2026-10-02T16:20:51+02:00 Commit: https://github.com/php/php-src/commit/87d728d24895068a7e886ad04368447797399c7f Raw diff: https://github.com/php/php-src/commit/87d728d24895068a7e886ad04368447797399c7f.diff 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 Changed paths: A ext/opcache/tests/opt/gh24063.phpt M NEWS M Zend/Optimizer/sccp.c Diff: diff --git a/NEWS b/NEWS index 7b24c94899c9..99df5910c8b5 100644 --- a/NEWS +++ b/NEWS @@ -108,6 +108,8 @@ PHP NEWS (David Carlier) . Fix multiple incorrect DCE due to unsound escape analysis. (ndossche, arnaud-lb) + . Fixed bug GH-24063 (OPcache optimizer folds $cond ? -0.0 : 0.0 into a + single -0.0 constant). (lazerg) - OpenSSL: . Fixed stream_socket_enable_crypto() leaving the socket non-blocking diff --git a/Zend/Optimizer/sccp.c b/Zend/Optimizer/sccp.c index 24b63a9cf407..ca5184d8d5ab 100644 --- a/Zend/Optimizer/sccp.c +++ b/Zend/Optimizer/sccp.c @@ -1946,6 +1946,28 @@ static void sccp_mark_feasible_successors( scdf_mark_edge_feasible(scdf, block_num, block->successors[s]); } +/* Unlike zend_is_identical(), this does not treat 0.0 and -0.0 as the same value. + * Returns int to be usable as compare_func_t. */ +static int sccp_values_differ(const void *p1, const void *p2) +{ + const zval *a = p1; + const zval *b = p2; + + if (Z_TYPE_P(a) != Z_TYPE_P(b)) { + return 1; + } + if (Z_TYPE_P(a) == IS_DOUBLE) { + return memcmp(&Z_DVAL_P(a), &Z_DVAL_P(b), sizeof(double)) != 0; + } + if (Z_TYPE_P(a) == IS_ARRAY) { + return Z_ARRVAL_P(a) != Z_ARRVAL_P(b) + && zend_hash_compare(Z_ARRVAL_P(a), Z_ARRVAL_P(b), sccp_values_differ, 1) != 0; + } + ZEND_ASSERT(IS_PARTIAL_ARRAY(a) || IS_PARTIAL_OBJECT(a) + || ((1 << Z_TYPE_P(a)) & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_BOOL|MAY_BE_LONG|MAY_BE_STRING))); + return !zend_is_identical(a, b); +} + static void join_hash_tables(HashTable *ret, HashTable *ht1, HashTable *ht2) { zend_ulong index; @@ -1958,7 +1980,7 @@ static void join_hash_tables(HashTable *ret, HashTable *ht1, HashTable *ht2) } else { val2 = zend_hash_index_find(ht2, index); } - if (val2 && zend_is_identical(val1, val2)) { + if (val2 && !sccp_values_differ(val1, val2)) { if (key) { val1 = zend_hash_add_new(ret, key, val1); } else { @@ -2026,7 +2048,7 @@ static void join_phi_values(zval *a, zval *b, bool escape) { zval_ptr_dtor_nogc(a); MAKE_BOT(a); } - } else if (!zend_is_identical(a, b)) { + } else if (sccp_values_differ(a, b)) { if (join_partial_arrays(a, b) == FAILURE) { zval_ptr_dtor_nogc(a); MAKE_BOT(a); diff --git a/ext/opcache/tests/opt/gh24063.phpt b/ext/opcache/tests/opt/gh24063.phpt new file mode 100644 index 000000000000..36f3ddbdca13 --- /dev/null +++ b/ext/opcache/tests/opt/gh24063.phpt @@ -0,0 +1,46 @@ +--TEST-- +GH-24063 (SCCP merges 0.0 and -0.0 into a single constant) +--EXTENSIONS-- +opcache +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.optimization_level=-1 +--FILE-- +<?php + +function scalar(float $n): float +{ + $r = $n < 0 ? -0.0 : 0.0; + return $r; +} + +function arr(float $n): array +{ + $r = $n < 0 ? [-0.0] : [0.0]; + return $r; +} + +function partial(float $n, $x): float +{ + $a = ['x' => $x]; + if ($n < 0) { + $a['k'] = -0.0; + } else { + $a['k'] = 0.0; + } + return $a['k']; +} + +var_dump(scalar(1.0), scalar(-1.0)); +var_dump(arr(1.0)[0], arr(-1.0)[0]); +var_dump(partial(1.0, 1), partial(-1.0, 1)); + +?> +--EXPECT-- +float(0) +float(-0) +float(0) +float(-0) +float(0) +float(-0)

« previous php.cvs (#141880) next »