Fix GH-20838: JIT compiler produces wrong arithmetic results#21383
Fix GH-20838: JIT compiler produces wrong arithmetic results#21383dstogov merged 1 commit intophp:PHP-8.4from
Conversation
|
@iliaal thanks for the deep analyses.
So your identified the problem completely right, but I;m not sure about the fix. I think, the proper fix for the problem should be emitting the type guard code before calling the VM handler. Do you like to finalize the fix yourself? |
|
@dstogov Let me re-review the patch and suggest an update |
1ee0d4e to
6de21c9
Compare
|
@dstogov You're right, the proper fix is emitting the type guards before the handler call. Updated the patch -- now CHECK_OP1_TRACE_TYPE() and CHECK_OP2_TRACE_TYPE() are called in the handler fallback path (before the MAY_BE_GUARD widening to MAY_BE_ANY), and the workaround in SET_STACK_TYPE is reverted. If the guard passes, MAY_BE_GUARD is cleared and the subsequent widening is skipped, so the TSSA result type prediction stays valid. If the guard fails, we side-exit. Full JIT test suite passes with no regressions. |
| op2_info = OP2_INFO(); | ||
| CHECK_OP1_TRACE_TYPE(); | ||
| CHECK_OP2_TRACE_TYPE(); | ||
| if (op1_info & MAY_BE_GUARD) { |
There was a problem hiding this comment.
I thought, about moving the while following if statements with zend_jit_fetch_reference() up, but probably your solution is even better. It should generate less code.
It makes sense to remove CHECK_OP*_TRACE_TYPE() from the else parts of the following ifs.
This shouldn't change any behavior.
Please re-target this PR to PHP-8.4.
There was a problem hiding this comment.
It should be verified, that hoisting CHECK_OP*_TRACE_TYPE() doesn't affect zend_jit_fetch_reference() path. I'll try to do this tomorrow...
6de21c9 to
c07b753
Compare
|
@dstogov Rebased onto PHP-8.4 and re-targeted the PR. Regarding removing |
|
After all I think that the best fix is inserting "necessary" type guards on the "sensitive" paths. diff --git a/ext/opcache/jit/zend_jit_trace.c b/ext/opcache/jit/zend_jit_trace.c
index 10be237d06b..82e173ba499 100644
--- a/ext/opcache/jit/zend_jit_trace.c
+++ b/ext/opcache/jit/zend_jit_trace.c
@@ -4556,6 +4556,12 @@ static zend_vm_opcode_handler_t zend_jit_trace(zend_jit_trace_rec *trace_buffer,
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 &&
@@ -4564,6 +4570,12 @@ static zend_vm_opcode_handler_t zend_jit_trace(zend_jit_trace_rec *trace_buffer,
/* 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_UNKNOWNIf you don't see problems or possible improvements - just commit this. (I saw your other 2 PRs, but I can't dedicate them the required time right now. They are in my queue and I'll review them later). |
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. Co-authored-by: Dmitry Stogov <dstogov@gmail.com> Fixes phpGH-20838
c07b753 to
e8edafe
Compare
|
@dstogov Applied your diff as-is -- guards only on the sensitive bailout paths, conditioned on IS_LONG || IS_DOUBLE. Makes sense to narrow the scope and doesn't affect the end-result according to tests. P.S. If looks good to you then feel free to merge latest version, I don't have direct commit/merge access at the moment |
|
Thanks! |
Summary
MAY_BE_GUARD, the guard is widened toMAY_BE_ANYbut never emitted as a runtime type checkIS_LONGinstead ofIS_DOUBLEfor MUL with mixed string/numeric operands), butSET_STACK_TYPEunconditionally records the predicted concrete type asmem_typeIS_LONGbytes as IEEE 754 double (producing values like3.7054923438093E-322instead of75)MAY_BE_GUARDFixes #20838