]> granicus.if.org Git - php/commitdiff
Allow using prototypes when optimizing arg passing
authorNikita Popov <nikita.ppv@gmail.com>
Fri, 21 Feb 2020 12:13:36 +0000 (13:13 +0100)
committerNikita Popov <nikita.ppv@gmail.com>
Thu, 16 Apr 2020 10:15:19 +0000 (12:15 +0200)
Closes GH-5193.

ext/opcache/Optimizer/optimize_func_calls.c
ext/opcache/Optimizer/zend_call_graph.c
ext/opcache/Optimizer/zend_optimizer.c
ext/opcache/Optimizer/zend_optimizer_internal.h

index 2894ca89f4d54d06a8071fb211f48dec32f7d72e..309a1401671ae9ed95b9e6e2d9769bd02ddd18f8 100644 (file)
@@ -39,6 +39,7 @@
 typedef struct _optimizer_call_info {
        zend_function *func;
        zend_op       *opline;
+       zend_bool      is_prototype;
        zend_bool      try_inline;
        uint32_t       func_arg_num;
 } optimizer_call_info;
@@ -172,9 +173,12 @@ void zend_optimize_func_calls(zend_op_array *op_array, zend_optimizer_ctx *ctx)
                        case ZEND_INIT_METHOD_CALL:
                        case ZEND_INIT_FCALL:
                        case ZEND_NEW:
+                               /* The argument passing optimizations are valid for prototypes as well,
+                                * as inheritance cannot change between ref <-> non-ref arguments. */
                                call_stack[call].func = zend_optimizer_get_called_func(
-                                       ctx->script, op_array, opline);
-                               call_stack[call].try_inline = opline->opcode != ZEND_NEW;
+                                       ctx->script, op_array, opline, &call_stack[call].is_prototype);
+                               call_stack[call].try_inline =
+                                       !call_stack[call].is_prototype && opline->opcode != ZEND_NEW;
                                /* break missing intentionally */
                        case ZEND_INIT_DYNAMIC_CALL:
                        case ZEND_INIT_USER_CALL:
index 51b307bf5e1757c14f1b6f64071bb9bb64df4e15..3b79bc445d53846e1b69e7af949993409ae0bb51 100644 (file)
@@ -93,6 +93,7 @@ int zend_analyze_calls(zend_arena **arena, zend_script *script, uint32_t build_f
        int call = 0;
        zend_call_info **call_stack;
        ALLOCA_FLAG(use_heap);
+       zend_bool is_prototype;
 
        call_stack = do_alloca((op_array->last / 2) * sizeof(zend_call_info*), use_heap);
        call_info = NULL;
@@ -103,8 +104,9 @@ int zend_analyze_calls(zend_arena **arena, zend_script *script, uint32_t build_f
                        case ZEND_INIT_STATIC_METHOD_CALL:
                                call_stack[call] = call_info;
                                func = zend_optimizer_get_called_func(
-                                       script, op_array, opline);
-                               if (func) {
+                                       script, op_array, opline, &is_prototype);
+                               /* TODO: Support prototypes? */
+                               if (func && !is_prototype) {
                                        call_info = zend_arena_calloc(arena, 1, sizeof(zend_call_info) + (sizeof(zend_send_arg_info) * ((int)opline->extended_value - 1)));
                                        call_info->caller_op_array = op_array;
                                        call_info->caller_init_opline = opline;
index facb238c21cb210f2b3486af3e1af22c09a5de39..6f896af27ce41bbacd152dbd049d6ea1943db07a 100644 (file)
@@ -777,8 +777,9 @@ static zend_class_entry *get_class_entry_from_op1(
 }
 
 zend_function *zend_optimizer_get_called_func(
-               zend_script *script, zend_op_array *op_array, zend_op *opline)
+               zend_script *script, zend_op_array *op_array, zend_op *opline, zend_bool *is_prototype)
 {
+       *is_prototype = 0;
        switch (opline->opcode) {
                case ZEND_INIT_FCALL:
                {
@@ -825,7 +826,7 @@ zend_function *zend_optimizer_get_called_func(
                                        if (fbc) {
                                                zend_bool is_public = (fbc->common.fn_flags & ZEND_ACC_PUBLIC) != 0;
                                                zend_bool same_scope = fbc->common.scope == op_array->scope;
-                                               if (is_public|| same_scope) {
+                                               if (is_public || same_scope) {
                                                        return fbc;
                                                }
                                        }
@@ -843,10 +844,15 @@ zend_function *zend_optimizer_get_called_func(
                                        zend_bool is_private = (fbc->common.fn_flags & ZEND_ACC_PRIVATE) != 0;
                                        zend_bool is_final = (fbc->common.fn_flags & ZEND_ACC_FINAL) != 0;
                                        zend_bool same_scope = fbc->common.scope == op_array->scope;
-                                       if ((is_private && same_scope)
-                                                       || (is_final && (!is_private || same_scope))) {
-                                               return fbc;
+                                       if (is_private) {
+                                               /* Only use private method if in the same scope. We can't even use it
+                                                * as a prototype, as it may be overridden with changed signature. */
+                                               return same_scope ? fbc : NULL;
                                        }
+                                       /* If the method is non-final, it may be overriden,
+                                        * but only with a compatible method signature. */
+                                       *is_prototype = !is_final;
+                                       return fbc;
                                }
                        }
                        break;
index 5207e6cb749fab47a4dda7b24ac04329f8c0973e..ed0dac3e1921dfa29283b40a478dffa6efb944fd 100644 (file)
@@ -110,7 +110,7 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx
 void zend_optimizer_compact_vars(zend_op_array *op_array);
 int zend_optimizer_is_disabled_func(const char *name, size_t len);
 zend_function *zend_optimizer_get_called_func(
-               zend_script *script, zend_op_array *op_array, zend_op *opline);
+               zend_script *script, zend_op_array *op_array, zend_op *opline, zend_bool *is_prototype);
 uint32_t zend_optimizer_classify_function(zend_string *name, uint32_t num_args);
 void zend_optimizer_migrate_jump(zend_op_array *op_array, zend_op *new_opline, zend_op *opline);
 void zend_optimizer_shift_jump(zend_op_array *op_array, zend_op *opline, uint32_t *shiftlist);