Skip to content

[wip] DFA based func call optimizations #13750

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

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

arnaud-lb
Copy link
Member

@arnaud-lb arnaud-lb commented Mar 18, 2024

The func call optimization pass updates the INIT_FCALL, SEND_, and DO_FCALL opcodes to specialized variants when the function is known. In particular, there are variants of the SEND_ operands that never pass by ref, which I assume improves the effectiveness of later passes. It also removes CHECK_FUNC ops and performs inlining.

Similarly, the call graph optimization builds a graph of known callers/callees that other passes can use.

@kocsismate noticed that method calls were ignored by these passes unless op1 is $this, because the type of op1 is only known after these passes.

This PR attempts to improve that by updating the call graph during type inference, and by running the func call optimization pass again after that:

  • The call graph is updated to also include unknown callees
  • Type inference updates callees when possible
  • Type inference uses callees to determine if SEND_ ops may pass by ref instead of relying exclusively on the opcode
  • The func call optimization pass is executed again after callee inference

The third point requires to run type inference before zend_mark_cv_references, otherwise we may narrow types, so we run zend_infer_types twice: Once for its effect on callees, and a second time after the func call optimization.

The DFA needs to admit a dependency between the INIT_METHOD_CALL op1_use and the related SEND and DO_FCALL ops. Currently the SSA can not represent this: I've tried adding a fake operand to SEND and DO_FCALL ops, or adding fake use/defs; but I wasn't happy with either, so instead I've added a special case in add_usages().

Unfortunately the effectiveness of these changes is limited by the fact that we must ignore classes declared in other compilation units, and usually there is only one class per file. Here is a comparison of the number of SEND opcodes emitted when executing the symfony demo before and after this change:

opcode              before  after
SEND_FUNC_ARG       794     791
SEND_REF            13      14
SEND_UNPACK         26      26
SEND_USER           1       1
SEND_VAL            622     627
SEND_VAL_EX         4965    4960
SEND_VAR            1506    1523
SEND_VAR_EX         4068    4058
SEND_VAR_NO_REF_EX  1470    1465

Benchmarks also show no noticeable improvement, so I don't plan on attempting to merge this as-is, but this may benefit other optimizations (or benefit from them) in the future.

@dktapps
Copy link
Contributor

dktapps commented Mar 19, 2024

Unfortunately the effectiveness of these changes is limited by the fact that we must ignore classes declared in other compilation units, and usually there is only one class per file.

Not super related to the PR itself, I'm just curious - I understand why these optimisations can't be performed on general autoloaded code. However, is it not possible to perform multiple-compile-unit optimisations on preloaded code?

@arnaud-lb
Copy link
Member Author

Yes I expect this optimization to be more effective in the context of preloading and I want to continue exploring in this direction :)

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.

2 participants