mirror of
https://github.com/php/php-src.git
synced 2026-03-24 00:02:20 +01:00
Fix GH-20838: JIT compiler produces wrong arithmetic results (#21383)
Insert type guards (CHECK_OP1_TRACE_TYPE / CHECK_OP2_TRACE_TYPE) on the sensitive bailout paths in ADD/SUB/MUL JIT compilation: the MAY_BE_UNDEF and non-numeric operand breaks. Guards are only emitted when the traced operand type is IS_LONG or IS_DOUBLE, ensuring TSSA result type predictions stay valid for side traces without affecting the normal numeric fast path. Fixes GH-20838 Co-authored-by: Dmitry Stogov <dmitrystogov@gmail.com>
This commit is contained in:
@@ -4510,6 +4510,12 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par
|
||||
op2_info = OP2_INFO();
|
||||
op2_addr = OP2_REG_ADDR();
|
||||
if ((op1_info & MAY_BE_UNDEF) || (op2_info & MAY_BE_UNDEF)) {
|
||||
if (op1_type == IS_LONG || op1_type == IS_DOUBLE) {
|
||||
CHECK_OP1_TRACE_TYPE();
|
||||
}
|
||||
if (op2_type == IS_LONG || op2_type == IS_DOUBLE) {
|
||||
CHECK_OP2_TRACE_TYPE();
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (opline->opcode == ZEND_ADD &&
|
||||
@@ -4518,6 +4524,12 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par
|
||||
/* pass */
|
||||
} else if (!(op1_info & (MAY_BE_LONG|MAY_BE_DOUBLE)) ||
|
||||
!(op2_info & (MAY_BE_LONG|MAY_BE_DOUBLE))) {
|
||||
if (op1_type == IS_LONG || op1_type == IS_DOUBLE) {
|
||||
CHECK_OP1_TRACE_TYPE();
|
||||
}
|
||||
if (op2_type == IS_LONG || op2_type == IS_DOUBLE) {
|
||||
CHECK_OP2_TRACE_TYPE();
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (orig_op1_type != IS_UNKNOWN
|
||||
|
||||
53
ext/opcache/tests/jit/gh20838.inc
Normal file
53
ext/opcache/tests/jit/gh20838.inc
Normal file
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
$result = json_decode('[{"ID":"49"},{"ID":"1080"},{"ID":"4415"},{"ID":"4763"},{"ID":"4926"},{"ID":"4933"},{"ID":"4935"},{"ID":"4938"},{"ID":"4939"},{"ID":"4947"},{"ID":"4952"},{"ID":"4953"},{"ID":"4962"},{"ID":"8558"},{"ID":"14888"},{"ID":"16879"},{"ID":"100003"},{"ID":"100007"},{"ID":"100013"},{"ID":"100017"},{"ID":"100019"},{"ID":"100027"},{"ID":"100031"},{"ID":"106427"},{"ID":"217955"},{"ID":"240000"},{"ID":"240010"},{"ID":"240021"},{"ID":"240079"},{"ID":"240210"},{"ID":"240227"},{"ID":"240249"},{"ID":"240273"},{"ID":"240333"},{"ID":"240335"},{"ID":"300024"},{"ID":"310001"},{"ID":"310025"},{"ID":"310034"},{"ID":"310042"},{"ID":"310111"},{"ID":"310191"},{"ID":"310219"},{"ID":"310236"},{"ID":"310322"},{"ID":"310356"},{"ID":"310360"},{"ID":"310394"},{"ID":"310405"},{"ID":"310411"}]', true);
|
||||
|
||||
$result1 = json_decode('[{"ID":"1080","cat":"6","vote":"2"},{"ID":"4415","cat":"1","vote":"5"},{"ID":"4415","cat":"5","vote":"-5"},{"ID":"4763","cat":"1","vote":"5"},{"ID":"4926","cat":"1","vote":"5"},{"ID":"4933","cat":"1","vote":"4"},{"ID":"4935","cat":"1","vote":"5"},{"ID":"4938","cat":"1","vote":"5"},{"ID":"4939","cat":"1","vote":"5"},{"ID":"4947","cat":"1","vote":"4"},{"ID":"4952","cat":"1","vote":"3"},{"ID":"4953","cat":"1","vote":"5"},{"ID":"4962","cat":"1","vote":"4"},{"ID":"8558","cat":"1","vote":"3"},{"ID":"14888","cat":"4","vote":"5"},{"ID":"16879","cat":"4","vote":"-2"},{"ID":"16879","cat":"6","vote":"3"},{"ID":"100003","cat":"1","vote":"0"},{"ID":"100007","cat":"1","vote":"4"},{"ID":"100013","cat":"1","vote":"0"},{"ID":"100017","cat":"6","vote":"4"},{"ID":"100017","cat":"4","vote":"-4"},{"ID":"100019","cat":"1","vote":"1"},{"ID":"100027","cat":"1","vote":"3"},{"ID":"100031","cat":"1","vote":"2"},{"ID":"106427","cat":"5","vote":"-5"},{"ID":"217955","cat":"4","vote":"-1"},{"ID":"217955","cat":"4","vote":"-1"},{"ID":"240000","cat":"1","vote":"2"},{"ID":"240010","cat":"1","vote":"4"},{"ID":"240021","cat":"1","vote":"3"},{"ID":"240079","cat":"1","vote":"4"},{"ID":"240079","cat":"7","vote":"4"},{"ID":"240079","cat":"6","vote":"2"},{"ID":"240210","cat":"1","vote":"3"},{"ID":"240227","cat":"1","vote":"3"},{"ID":"240249","cat":"1","vote":"5"},{"ID":"240273","cat":"1","vote":"5"},{"ID":"240333","cat":"1","vote":"2"},{"ID":"240335","cat":"1","vote":"4"},{"ID":"300024","cat":"3","vote":"4"},{"ID":"300024","cat":"1","vote":"5"},{"ID":"310001","cat":"1","vote":"3"},{"ID":"310025","cat":"1","vote":"3"},{"ID":"310034","cat":"1","vote":"4"},{"ID":"310042","cat":"1","vote":"2"},{"ID":"310111","cat":"1","vote":"5"},{"ID":"310191","cat":"1","vote":"3"},{"ID":"310219","cat":"1","vote":"5"},{"ID":"310236","cat":"1","vote":"1"},{"ID":"310322","cat":"1","vote":"3"},{"ID":"310356","cat":"1","vote":"2"},{"ID":"310360","cat":"1","vote":"3"},{"ID":"310360","cat":"4","vote":"-5"},{"ID":"310394","cat":"1","vote":"1"},{"ID":"310394","cat":"3","vote":"2"},{"ID":"310405","cat":"1","vote":"5"},{"ID":"310411","cat":"1","vote":"4"}]', true);
|
||||
|
||||
foreach ($result as $arr) {
|
||||
$v = [];
|
||||
foreach ($result1 as $arr1) {
|
||||
if ($arr1['ID'] == $arr['ID']) $v[] = $arr1;
|
||||
}
|
||||
TLVotesStatUpdateBAD($arr['ID'], $v);
|
||||
}
|
||||
|
||||
function TLVotesStatUpdateBAD(int $id, ?array $v = null): float
|
||||
{
|
||||
static $k;
|
||||
if (!isset($k)) $k = json_decode('{"4":{"tp":"20","st":"sum"},"3":{"tp":"30","st":"caa"},"2":{"tp":"60","st":"sum"},"1":{"tp":"50","st":"unique"},"5":{"tp":"50","st":"caa"},"6":{"tp":"20","st":"caa"},"7":{"tp":"60","st":"caa"},"8":{"tp":"60","st":"caa"},"255":{"tp":"0","st":"correction"}}', true);
|
||||
|
||||
$pav = 50;
|
||||
$s = 0.0;
|
||||
$corrections = 0;
|
||||
$av = [];
|
||||
$avc = [];
|
||||
|
||||
foreach ($v as $arr) {
|
||||
if ($k[$arr['cat']]['st'] == 'unique') $av[$arr['cat']] = $arr['vote'];
|
||||
elseif ($k[$arr['cat']]['st'] == 'correction') {
|
||||
$av[$arr['cat']] = $arr['vote'];
|
||||
$corrections++;
|
||||
} else {
|
||||
if ($arr['vote'] < 0) $arr['vote'] = $arr['vote'] / 2;
|
||||
$av[$arr['cat']] ??= 0;
|
||||
$avc[$arr['cat']] ??= 0;
|
||||
$av[$arr['cat']] += $arr['vote'];
|
||||
$avc[$arr['cat']]++;
|
||||
}
|
||||
}
|
||||
|
||||
if (($c = count($av))) {
|
||||
$c -= $corrections;
|
||||
foreach ($av as $key => $value) {
|
||||
if ($k[$key]['st'] == 'correction') $s += $value;
|
||||
else {
|
||||
if ($k[$key]['st'] == 'caa') $value = $value / $avc[$key];
|
||||
$s += $value / 5 * $k[$key]['tp'] * (1 + 50 / $k[$key]['tp'] * $pav / (100 * $c));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
echo("$id $s\n");
|
||||
|
||||
return $s;
|
||||
}
|
||||
66
ext/opcache/tests/jit/gh20838.phpt
Normal file
66
ext/opcache/tests/jit/gh20838.phpt
Normal file
@@ -0,0 +1,66 @@
|
||||
--TEST--
|
||||
GH-20838 (JIT compiler produces wrong arithmetic results)
|
||||
--INI--
|
||||
opcache.enable=1
|
||||
opcache.enable_cli=1
|
||||
opcache.jit=tracing
|
||||
opcache.jit_buffer_size=64M
|
||||
opcache.jit_hot_loop=61
|
||||
opcache.jit_hot_func=127
|
||||
opcache.jit_hot_return=8
|
||||
opcache.jit_hot_side_exit=8
|
||||
--EXTENSIONS--
|
||||
opcache
|
||||
--FILE_EXTERNAL--
|
||||
gh20838.inc
|
||||
--EXPECT--
|
||||
49 0
|
||||
1080 18
|
||||
4415 31.25
|
||||
4763 75
|
||||
4926 75
|
||||
4933 60
|
||||
4935 75
|
||||
4938 75
|
||||
4939 75
|
||||
4947 60
|
||||
4952 45
|
||||
4953 75
|
||||
4962 60
|
||||
8558 45
|
||||
14888 45
|
||||
16879 13
|
||||
100003 0
|
||||
100007 60
|
||||
100013 0
|
||||
100017 13
|
||||
100019 15
|
||||
100027 45
|
||||
100031 30
|
||||
106427 -37.5
|
||||
217955 -9
|
||||
240000 30
|
||||
240010 60
|
||||
240021 45
|
||||
240079 112.66666666667
|
||||
240210 45
|
||||
240227 45
|
||||
240249 75
|
||||
240273 75
|
||||
240333 30
|
||||
240335 60
|
||||
300024 96.5
|
||||
310001 45
|
||||
310025 45
|
||||
310034 60
|
||||
310042 30
|
||||
310111 75
|
||||
310191 45
|
||||
310219 75
|
||||
310236 15
|
||||
310322 45
|
||||
310356 30
|
||||
310360 21.25
|
||||
310394 29.5
|
||||
310405 75
|
||||
310411 60
|
||||
Reference in New Issue
Block a user