Edit report at https://bugs.php.net/bug.php?id=81249&edit=1
ID: 81249
Updated by: git@php.net
Reported by: smokey101stair at gmail dot com
Summary: Intermittent property assignment failure with JIT
enabled
-Status: Verified
+Status: Closed
Type: Bug
Package: JIT
Operating System: Ubuntu 20.04
PHP Version: 8.0.8
Assigned To: dmitry
Block user comment: N
Private report: N
New Comment:
Automatic comment on behalf of dstogov
Revision: https://github.com/php/php-src/commit/c0e49328168bee315e72f21543b2ef2c01ad9169
Log: Fixed bug #81249 (Intermittent property assignment failure with JIT enabled)
Previous Comments:
------------------------------------------------------------------------
[2021-07-19 06:27:07] hao dot sun at arm dot com
Based on my local debugging, I suspected function zend_jit_may_skip_comparison().
Code is not generated as expected for the hot side trace for statement: "if ($byte === 0x8E ||
$byte === 0x8F || ($byte >= 0xA1 && $byte <= 0xFE)) {"
Here is trace #5: (Note several "echo" statements are inserted in my local debugging). For
"$byte === 0x8E || $byte === 0x8F", there are two IS_IDENTICAL opcodes.
TRACE 3 exit 1 EucJpDecoder::handle() /home/haosun01/php/7.php:91
---- TRACE 5 start (side trace 3/1) EucJpDecoder::handle() /home/haosun01/php/7.php:91
========3333333 143
===== 44444444 set lead here: 143 : 143
0086 T7 = ROPE_INIT 3 string("========3333333 ")
0087 T7 = ROPE_ADD 1 T7 CV1($byte) ; op2(int)
0088 T6 = ROPE_END 2 T7 string("
")
0089 ECHO T6 ; op1(string)
0090 T6 = IS_IDENTICAL CV1($byte) int(142) ; op1(int)
0091 ;JMPNZ T6 0098
0092 T6 = IS_IDENTICAL CV1($byte) int(143) ; op1(int)
0093 ;JMPNZ T6 0098
0098 ASSIGN_OBJ THIS string("lead") ; op3(int)
0099 ;OP_DATA CV1($byte)
0100 T7 = ROPE_INIT 5 string("===== 44444444 set lead here: ")
0101 T7 = ROPE_ADD 1 T7 CV1($byte) ; op2(int)
0102 T7 = ROPE_ADD 2 T7 string(" : ")
0103 T6 = FETCH_OBJ_R THIS string("lead")
0104 T7 = ROPE_ADD 3 T7 T6 ; op2(int)
0105 T6 = ROPE_END 4 T7 string("
")
0106 ECHO T6 ; op1(string)
0107 RETURN int(-1)
---- TRACE 5 stop (return)
However, in the compiled code, I found that only the code for "$byte === 0x8E" is
generated. Here is part of the code. See "cmp x15, #0x8e".
.L1:
ffffbb9c8250: movz x15, #0x51c0
ffffbb9c8254: movk x15, #0xabb6, lsl #16
ffffbb9c8258: movk x15, #0xaaaa, lsl #32
ffffbb9c825c: ldr x8, [x15]
ffffbb9c8260: cbnz x8, #JIT$$exception_handler
ffffbb9c8264: ldr x15, [x27, #0x60]
ffffbb9c8268: cmp x15, #0x8e
ffffbb9c826c: b.eq #jit$$trace_exit_0
ffffbb9c8270: b.ne #jit$$trace_exit_1
ffffbb9c8274: ldr x0, [x27, #0x20]
ffffbb9c8278: ldrb w16, [x0, #0x30]
ffffbb9c827c: cbz w16, #jit$$trace_exit_2
ffffbb9c8280: add x0, x0, #0x28
ffffbb9c8284: add x1, x27, #0x60
ffffbb9c8288: adrp x8, #0xffffb4298000
ffffbb9c828c: add x8, x8, #0xa70
ffffbb9c8290: str x8, [x27]
ffffbb9c8294: bl #JIT$$assign_tmp
ffffbb9c8298: movz x15, #0x51c0
ffffbb9c829c: movk x15, #0xabb6, lsl #16
ffffbb9c82a0: movk x15, #0xaaaa, lsl #32
ffffbb9c82a4: ldr x8, [x15]
ffffbb9c82a8: cbnz x8, #JIT$$exception_handler
ffffbb9c82ac: movz x15, #0x160
ffffbb9c82b0: add x28, x28, x15
That is because "skip_comparison" is set as true for the second ZEND_IS_IDENTICAL, i.e.
JIT is bypassed.
In details, in file zend_jit_trace.c, these code
(https://github.com/php/php-src/blob/master/ext/opcache/jit/zend_jit_trace.c#L4762-L4812) is used to
generate the JIT code for ZEND_IS_IDENTICAL. However, I found that variable
"skip_comparison" is set as false for "$byte === 0x8E", but is set as true for
"$byte === 0x8F".
I further looked at function zend_jit_may_skip_comparison(). I noticed that for $byte === 0x8F, this
function returns "1" at https://github.com/php/php-src/blob/master/ext/opcache/jit/zend_jit_trace.c#L3568
In one word, I guess the root cause of this bug lies in how to determine "skip_comparison"
for the case of two consecutive "ZEND_IS_IDENTICAL".
As I tried, if we always set variable "skip_comparison" as false, this bug can be gone.
------------------------------------------------------------------------
[2021-07-19 00:30:59] hao dot sun at arm dot com
FYI.
1. This bug occurred for master branch as well, for both JIT/x86 and JIT/arm64.
2. In my test, the test case can be further downsized, that is, Lines 85 to 87 can be removed as
well, i.e. https://gist.github.com/TRowbotham/4dc7c8dc1714edab69019e0d71c17c02#file-jit1-php-L85-L87
3. I suspect this bug is due to "hot side exit".
1) -d opcache.jit_hot_side_exit=0, this bug is gone.
2) -d opcache.jit_hot_side_exit=N, the smaller the number we assigned, the earlier this bug showed
up.
------------------------------------------------------------------------
[2021-07-12 03:46:12] smokey101stair at gmail dot com
Description:
------------
PHP 8.0.8 (cli) (built: Jul 4 2021 21:22:34) ( NTS )
Copyright (c) The PHP Group
Zend Engine v4.0.8, Copyright (c) Zend Technologies
with Zend OPcache v8.0.8, Copyright (c), by Zend Technologies
PHP was compiled using PHPBrew, if that makes a difference.
opcache.jit=1255
opcache.memory_consumption=256
opcache.max_accelerated_files=20000
opcache.jit_buffer_size=256M
zend.assertions=1
The test script decodes EUC-JP text to Unicode Code Points. I tried to reduce the script as much as
possible, but it was very temperamental when removing if statements.
When processing the first byte, 8F, the assignment at https://gist.github.com/TRowbotham/4dc7c8dc1714edab69019e0d71c17c02#file-jit1-php-L90
fails intermittently. This leaves $this->lead set to its initial value of 0, which eventually
leads to the script returning an error instead of a code point.
The script is expected to transform the EUC-JP encoded bytes to the Unicode U+2D8 code point.
Test script:
---------------
https://gist.github.com/TRowbotham/4dc7c8dc1714edab69019e0d71c17c02
------------------------------------------------------------------------
--
Edit this bug report at https://bugs.php.net/bug.php?id=81249&edit=1