|
php.net | support | documentation | report a bug | advanced search | search howto | statistics | random bug | login |
[2021-07-12 03:46 UTC] 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
PatchesPull RequestsHistoryAllCommentsChangesGit/SVN commits
|
|||||||||||||||||||||||||||
Copyright © 2001-2025 The PHP GroupAll rights reserved. |
Last updated: Sat Oct 25 11:00:01 2025 UTC |
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.