From de30ba50426f1828a3385e350bf808faf50bcf51 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Mon, 25 Nov 2024 22:02:49 +0100 Subject: [PATCH] Fix GH-16879: JIT dead code skipping does not update call_level We intend to execute `MATCH_ERROR` in the VM and return to trace a hot function in BB1. We generate a tail handler and skip all remaining oplines of BB0. That means the `INIT_FCALL` in BB0 is missed and `call_level` is not increased to 1. This leads to the assertion failure. This patch fixes the issue by updating the `call_level` for the skipped oplines. Closes GH-16939. --- NEWS | 2 ++ ext/opcache/jit/zend_jit.c | 29 ++++++++++++++++++++++++++++- ext/opcache/tests/jit/gh16879.phpt | 22 ++++++++++++++++++++++ 3 files changed, 52 insertions(+), 1 deletion(-) create mode 100644 ext/opcache/tests/jit/gh16879.phpt diff --git a/NEWS b/NEWS index 348e58cc10f..8ff774ee5a4 100644 --- a/NEWS +++ b/NEWS @@ -19,6 +19,8 @@ PHP NEWS . Fixed bug GH-16851 (JIT_G(enabled) not set correctly on other threads). (dktapps) . Fixed bug GH-16902 (Set of opcache tests fail zts+aarch64). (nielsdos) + . Fixed bug GH-16879 (JIT dead code skipping does not update call_level). + (nielsdos) - Windows: . Fixed bug GH-16849 (Error dialog causes process to hang). (cmb) diff --git a/ext/opcache/jit/zend_jit.c b/ext/opcache/jit/zend_jit.c index 0c6ab6c5cbc..cc22e7375a8 100644 --- a/ext/opcache/jit/zend_jit.c +++ b/ext/opcache/jit/zend_jit.c @@ -2576,7 +2576,34 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op } /* THROW and EXIT may be used in the middle of BB */ /* don't generate code for the rest of BB */ - i = end; + + /* Skip current opline for call_level computation + * Don't include last opline because end of loop already checks call level of last opline */ + i++; + for (; i < end; i++) { + opline = op_array->opcodes + i; + switch (opline->opcode) { + case ZEND_INIT_FCALL: + case ZEND_INIT_FCALL_BY_NAME: + case ZEND_INIT_NS_FCALL_BY_NAME: + case ZEND_INIT_METHOD_CALL: + case ZEND_INIT_DYNAMIC_CALL: + case ZEND_INIT_STATIC_METHOD_CALL: + case ZEND_INIT_PARENT_PROPERTY_HOOK_CALL: + case ZEND_INIT_USER_CALL: + case ZEND_NEW: + call_level++; + break; + case ZEND_DO_FCALL: + case ZEND_DO_ICALL: + case ZEND_DO_UCALL: + case ZEND_DO_FCALL_BY_NAME: + case ZEND_CALLABLE_CONVERT: + call_level--; + break; + } + } + opline = op_array->opcodes + i; break; /* stackless execution */ case ZEND_INCLUDE_OR_EVAL: diff --git a/ext/opcache/tests/jit/gh16879.phpt b/ext/opcache/tests/jit/gh16879.phpt new file mode 100644 index 00000000000..7a17fd34135 --- /dev/null +++ b/ext/opcache/tests/jit/gh16879.phpt @@ -0,0 +1,22 @@ +--TEST-- +GH-16879 (JIT dead code skipping does not update call_level) +--EXTENSIONS-- +opcache +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.file_update_protection=0 +opcache.jit_buffer_size=32M +opcache.jit=1235 +opcache.jit_hot_func=1 +--FILE-- + +--EXPECTF-- +Fatal error: Uncaught UnhandledMatchError: Unhandled match case 0 in %s:%d +Stack trace: +#0 {main} + thrown in %s on line %d