Skip to content

JIT: Check exception on exit #18297

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

Open
wants to merge 3 commits into
base: PHP-8.4
Choose a base branch
from
Open

JIT: Check exception on exit #18297

wants to merge 3 commits into from

Conversation

arnaud-lb
Copy link
Member

@arnaud-lb arnaud-lb commented Apr 10, 2025

Fixes GH-18262. See #18262 (comment).

In zend_jit_fetch_obj(), we may emit a type guard (in zend_jit_guard_fetch_result_type()) before the exception check. When an exception is throw while fetching a property and the guard fails, we ignore the exception and start side tracing. In GH-18262 an assertion fails during tracing because EG(exception) is set.

Here I add a new exit flag ZEND_JIT_EXIT_CHECK_EXCEPTION, that enables exception checking during exit/deoptimization. This seems ideal because this ensures that house keeping is done before exception handling (freeing op1), and avoids duplicating the exception check in zend_jit_fetch_obj(). Also, it is my understanding that deoptimization is required before handling the exception in this case.

We already checked for exceptions during exit/deoptimization, but only when ZEND_JIT_EXIT_FREE_OP1 or ZEND_JIT_EXIT_FREE_OP2 were set (presumably to handle exceptions thrown during dtor). Here I make it possible to request it explicitly with ZEND_JIT_EXIT_CHECK_EXCEPTION.

I had to change exception checking in zend_jit_trace_exit() to handle two issues:

  • By returning 1, we were telling the caller (zend_jit_trace_exit_stub()) to execute the original op handler of EG(current_execute_data)->opline, but in reality we want to execute EX(opline), which should be EG(exception_op).
  • EX(opline) is set to %r15 in zend_jit_trace_exit_stub() before calling zend_jit_trace_exit(), but this may be the address of a zend_execute_data when IP is being reused to cache EX(call) (which was the case here). This is usually not an issue because we override EX(opline) again in zend_jit_trace_exit_stub(), in most cases, except when checking exceptions.

BTW, I think it may be possible to stop reusing IP / %r15 for holding the value of EX(opline) (edit: I meant EX(call)) in the new JIT? From my understanding this was very convenient in the old JIT, but it seems unnecessary now.

@arnaud-lb arnaud-lb changed the base branch from master to PHP-8.4 April 10, 2025 15:59
Copy link
Member

@nielsdos nielsdos left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense to me. Please see my 2 minor remarks.

@@ -14476,7 +14476,7 @@ static int zend_jit_fetch_obj(zend_jit_ctx *jit,
ZEND_ASSERT(end_inputs == IR_UNUSED);
if ((res_info & MAY_BE_GUARD) && JIT_G(current_frame)) {
uint8_t type = concrete_type(res_info);
uint32_t flags = 0;
uint32_t flags = ZEND_JIT_EXIT_CHECK_EXCEPTION;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't comment on the line, but maybe it's better to change line 14484 to flags |= ZEND_JIT_EXIT_FREE_OP1;. It doesn't matter here because you also check exceptions when OP1 needs to be freed, but it would be more "future-proof"/"semantically right".

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh right, that's actually a mistake. Thank you for spotting this.

#define ZEND_JIT_EXIT_CLOSURE_CALL (1<<8) /* exit because of polymorphic INIT_DYNAMIC_CALL call */
#define ZEND_JIT_EXIT_METHOD_CALL (1<<9) /* exit because of polymorphic INIT_METHOD_CALL call */
#define ZEND_JIT_EXIT_INVALIDATE (1<<10) /* invalidate current trace */
#define ZEND_JIT_EXIT_CHECK_EXCEPTION (1<<11)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe also make sure this gets printed out in zend_jit_dump_exit_info?

@dstogov
Copy link
Member

dstogov commented Apr 11, 2025

The patch looks good and I suppose it's completely right.
Please hold it by Monday/Tuesday. I hope I'll find time to play with it.

@dstogov dstogov mentioned this pull request Apr 11, 2025
Copy link
Member

@dstogov dstogov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Everything is fine!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Assertion failure Zend/zend_vm_execute.h JIT
3 participants