-
Notifications
You must be signed in to change notification settings - Fork 7.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
JIT dead code skipping does not update call_level #16879
Comments
Simplified: <?php
match(y){};
var_dump(new stdClass);
var_dump(3); This is the bytecode that is used for JITting:
We intend to execute |
Here is a patch that fixes the problem for me, but this may not be the best approach. It also duplicates code, although that can be solved by refactoring the call_level increment/decrement checks into separate functions. I may also be wrong, but intuitively I think at least the idea is right, I can update the patch with suggested improvements. cc @dstogov Patch: 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:
|
@nielsdos thanks for the patch. It's right. Do we need this for PHP-8.4 or for PHP-8.2/3 as well? |
@dstogov We only need this for PHP 8.4 and higher. This is because 8.2-8.3 JIT still loops over all the opcodes and generates dead code after ZEND_MATCH_ERROR opline is handled. Therefore I will make a PR for PHP 8.4. I mentioned factoring out the check when call level should be increased or decreased to reduce repetition, I can make a separate PR for that later against the master branch. |
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.
Thanks. Please do. I don't think a separate PR for master is required. |
* PHP-8.4: Fix GH-16879: JIT dead code skipping does not update call_level
Description
The following code:
Resulted in this output:
To reproduce:
PHP Version
nightly
Operating System
ubuntu 22.04
The text was updated successfully, but these errors were encountered: