]> granicus.if.org Git - php/commitdiff
Use return type hints for type inference and eliminate useless VERIFY_RETRUN_TYPE...
authorDmitry Stogov <dmitry@zend.com>
Thu, 7 Apr 2016 14:34:53 +0000 (17:34 +0300)
committerDmitry Stogov <dmitry@zend.com>
Thu, 7 Apr 2016 14:34:53 +0000 (17:34 +0300)
Zend/zend_compile.c
ext/opcache/Optimizer/dfa_pass.c
ext/opcache/Optimizer/zend_dfg.c
ext/opcache/Optimizer/zend_inference.c
ext/opcache/Optimizer/zend_inference.h
ext/opcache/Optimizer/zend_optimizer.c
ext/opcache/Optimizer/zend_ssa.c

index 5f3114d10e1aed1a163c559d945b8029adb83d60..da2c83c55ecd31612b5071c65b13a2b404554747 100644 (file)
@@ -2265,12 +2265,30 @@ static zend_op *zend_delayed_compile_end(uint32_t offset) /* {{{ */
 static void zend_emit_return_type_check(znode *expr, zend_arg_info *return_info) /* {{{ */
 {
        /* `return ...;` is illegal in a void function (but `return;` isn't) */
-       if (expr && return_info->type_hint == IS_VOID) {
-               zend_error_noreturn(E_COMPILE_ERROR, "A void function must not return a value");
+       if (return_info->type_hint == IS_VOID) {
+               if (expr) {
+                       zend_error_noreturn(E_COMPILE_ERROR, "A void function must not return a value");
+               }
+               /* we don't need run-time check */
+               return;
        }
 
        if (return_info->type_hint != IS_UNDEF) {
-               zend_op *opline = zend_emit_op(NULL, ZEND_VERIFY_RETURN_TYPE, expr, NULL);
+               zend_op *opline;
+
+               if (expr && expr->op_type == IS_CONST) {
+                       if ((return_info->type_hint == Z_TYPE(expr->u.constant))
+                        ||((return_info->type_hint == _IS_BOOL)
+                         && (Z_TYPE(expr->u.constant) == IS_FALSE
+                          || Z_TYPE(expr->u.constant) == IS_TRUE))
+                        || (return_info->allow_null
+                         && Z_TYPE(expr->u.constant) == IS_NULL)) {
+                               /* we don't need run-time check */
+                               return;
+                       }
+               }
+
+               opline = zend_emit_op(NULL, ZEND_VERIFY_RETURN_TYPE, expr, NULL);
                if (expr && expr->op_type == IS_CONST) {
                        opline->result_type = expr->op_type = IS_TMP_VAR;
                        opline->result.var = expr->u.op.var = get_temporary_variable(CG(active_op_array));
index 74edad6361145430d11f3db7defe72bf8ecd0f88..16dfd169322edf7c3716bb43e0295bc6b528968f 100644 (file)
@@ -429,6 +429,28 @@ void zend_dfa_optimize_op_array(zend_op_array *op_array, zend_optimizer_ctx *ctx
                                op_array->opcodes[op2].opcode = ZEND_PRE_DEC;
                                SET_UNUSED(op_array->opcodes[op2].op2);
 
+                       } else if (op_array->opcodes[op2].opcode == ZEND_VERIFY_RETURN_TYPE
+                        && ssa->ops[op2].op1_def == i
+                        && ssa->ops[op2].op1_use >= 0
+                        && ssa->ops[op2].op1_use_chain == -1
+                        && ssa->vars[i].use_chain >= 0
+                        && (ssa->var_info[ssa->ops[op2].op1_use].type & (MAY_BE_ANY|MAY_BE_UNDEF)) == (ssa->var_info[ssa->ops[op2].op1_def].type & MAY_BE_ANY)) {
+                               /* remove useless type check */
+                               int var1 = ssa->ops[op2].op1_use;
+                               int ret = ssa->vars[i].use_chain;
+
+                               ssa->vars[var1].use_chain = ret;
+                               ssa->ops[ret].op1_use = var1;
+
+                               ssa->vars[i].definition = -1;
+                               ssa->vars[i].use_chain = -1;
+
+                               ssa->ops[op2].op1_def = -1;
+                               ssa->ops[op2].op1_use = -1;
+
+                               MAKE_NOP(&op_array->opcodes[op2]);
+                               remove_nops = 1;
+
                        } else if (ssa->ops[op2].op1_def == i
                         && !RETURN_VALUE_USED(&op_array->opcodes[op2])
                         && ssa->ops[op2].op1_use >= 0
index 91c447799ef31285425a2813a73ba745890816f2..b45aac41fbdf63b462e4876f008cb67cb786784d 100644 (file)
@@ -131,6 +131,7 @@ op1_def:
                                        case ZEND_FETCH_OBJ_RW:
                                        case ZEND_FETCH_OBJ_FUNC_ARG:
                                        case ZEND_FETCH_OBJ_UNSET:
+                                       case ZEND_VERIFY_RETURN_TYPE:
                                                DFG_SET(gen, set_size, j, var_num);
                                        default:
 op1_use:
@@ -140,6 +141,9 @@ op1_use:
                                        }
                                } else if (opline->op1_type & (IS_VAR|IS_TMP_VAR)) {
                                        var_num = EX_VAR_TO_NUM(opline->op1.var);
+                                       if (opline->opcode == ZEND_VERIFY_RETURN_TYPE) {
+                                               DFG_SET(gen, set_size, j, var_num);
+                                       }
                                        if (!DFG_ISSET(def, set_size, j, var_num)) {
                                                DFG_SET(use, set_size, j, var_num);
                                        }
index d7f2f26dad119f7346a9c67d985dec52db359034..51817ab5a9cc6a0a299ddd344a52375bba9aaa8f 100644 (file)
@@ -2203,6 +2203,39 @@ static inline zend_class_entry *get_class_entry(const zend_script *script, zend_
        return NULL;
 }
 
+static uint32_t zend_fetch_arg_info(const zend_script *script, zend_arg_info *arg_info, zend_class_entry **pce)
+{
+       uint32_t tmp = 0;
+
+       *pce = NULL;
+       if (arg_info->class_name) {
+               // class type hinting...
+               zend_string *lcname = zend_string_tolower(arg_info->class_name);
+               tmp |= MAY_BE_OBJECT;
+               *pce = get_class_entry(script, lcname);
+               zend_string_release(lcname);
+       } else if (arg_info->type_hint != IS_UNDEF) {
+               if (arg_info->type_hint == IS_VOID) {
+                       tmp |= MAY_BE_NULL;
+               } else if (arg_info->type_hint == IS_CALLABLE) {
+                       tmp |= MAY_BE_STRING|MAY_BE_OBJECT|MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF;
+               } else if (arg_info->type_hint == IS_ARRAY) {
+                       tmp |= MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF;
+               } else if (arg_info->type_hint == _IS_BOOL) {
+                       tmp |= MAY_BE_TRUE|MAY_BE_FALSE;
+               } else {
+                       ZEND_ASSERT(arg_info->type_hint < IS_REFERENCE);
+                       tmp |= 1 << arg_info->type_hint;
+               }
+       } else {
+               tmp |= MAY_BE_ANY|MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF;
+       }
+       if (arg_info->allow_null) {
+               tmp |= MAY_BE_NULL;
+       }
+       return tmp;
+}
+
 static void zend_update_type_info(const zend_op_array *op_array,
                                   zend_ssa            *ssa,
                                   const zend_script   *script,
@@ -3153,30 +3186,8 @@ static void zend_update_type_info(const zend_op_array *op_array,
 
                        ce = NULL;
                        if (arg_info) {
-                               tmp = 0;
-                               if (arg_info->class_name) {
-                                       // class type hinting...
-                                       zend_string *lcname = zend_string_tolower(arg_info->class_name);
-                                       tmp |= MAY_BE_OBJECT;
-                                       ce = get_class_entry(script, lcname);
-                                       zend_string_release(lcname);
-                               } else if (arg_info->type_hint != IS_UNDEF) {
-                                       if (arg_info->type_hint == IS_CALLABLE) {
-                                               tmp |= MAY_BE_STRING|MAY_BE_OBJECT|MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF;
-                                       } else if (arg_info->type_hint == IS_ARRAY) {
-                                               tmp |= MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF;
-                                       } else if (arg_info->type_hint == _IS_BOOL) {
-                                               tmp |= MAY_BE_TRUE|MAY_BE_FALSE;
-                                       } else {
-                                               ZEND_ASSERT(arg_info->type_hint < IS_REFERENCE);
-                                               tmp |= 1 << arg_info->type_hint;
-                                       }
-                               } else {
-                                       tmp |= MAY_BE_ANY|MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF;
-                               }
-                               if (arg_info->allow_null) {
-                                       tmp |= MAY_BE_NULL;
-                               } else if (opline->opcode == ZEND_RECV_INIT &&
+                               tmp = zend_fetch_arg_info(script, arg_info, &ce);
+                               if (opline->opcode == ZEND_RECV_INIT &&
                                           Z_CONSTANT_P(CRT_CONSTANT_EX(op_array, opline->op2, ssa->rt_constants))) {
                                        /* The constant may resolve to NULL */
                                        tmp |= MAY_BE_NULL;
@@ -3649,6 +3660,29 @@ static void zend_update_type_info(const zend_op_array *op_array,
                case ZEND_DEFINED:
                        UPDATE_SSA_TYPE(MAY_BE_RC1|MAY_BE_FALSE|MAY_BE_TRUE, ssa_ops[i].result_def);
                        break;
+               case ZEND_VERIFY_RETURN_TYPE:
+               {
+                       zend_arg_info *ret_info = op_array->arg_info - 1;
+
+                       tmp = zend_fetch_arg_info(script, ret_info, &ce);
+                       tmp |= MAY_BE_RC1 | MAY_BE_RCN;
+                       if (opline->op1_type == IS_CONST) {
+                               UPDATE_SSA_TYPE(tmp, ssa_ops[i].result_def);
+                               if (ce) {
+                                       UPDATE_SSA_OBJ_TYPE(ce, 1, ssa_ops[i].result_def);
+                               } else {
+                                       UPDATE_SSA_OBJ_TYPE(NULL, 0, ssa_ops[i].result_def);
+                               }
+                       } else {
+                               UPDATE_SSA_TYPE(tmp, ssa_ops[i].op1_def);
+                               if (ce) {
+                                       UPDATE_SSA_OBJ_TYPE(ce, 1, ssa_ops[i].op1_def);
+                               } else {
+                                       UPDATE_SSA_OBJ_TYPE(NULL, 0, ssa_ops[i].op1_def);
+                               }
+                       }
+                       break;
+               }
                default:
 unknown_opcode:
                        if (ssa_ops[i].op1_def >= 0) {
@@ -3869,7 +3903,23 @@ static int is_recursive_tail_call(const zend_op_array *op_array,
        return 0;
 }
 
+void zend_init_func_return_info(const zend_op_array   *op_array,
+                                const zend_script     *script,
+                                zend_ssa_var_info     *ret)
+{
+       if (op_array->fn_flags & ZEND_ACC_HAS_RETURN_TYPE) {
+               zend_arg_info *ret_info = op_array->arg_info - 1;
+               zend_ssa_range tmp_range = {0, 0, 0, 0};
+
+               ret->type = zend_fetch_arg_info(script, ret_info, &ret->ce);
+               ret->is_instanceof = (ret->ce) ? 1 : 0;
+               ret->range = tmp_range;
+               ret->has_range = 0;
+       }
+}
+
 void zend_func_return_info(const zend_op_array   *op_array,
+                           const zend_script     *script,
                            int                    recursive,
                            int                    widening,
                            zend_ssa_var_info     *ret)
@@ -4047,16 +4097,19 @@ void zend_func_return_info(const zend_op_array   *op_array,
                        }
                }
        }
-       if (tmp_is_instanceof < 0) {
-               tmp_is_instanceof = 0;
-               tmp_ce = NULL;
-       }
-       if (tmp_has_range < 0) {
-               tmp_has_range = 0;
+
+       if (!(op_array->fn_flags & ZEND_ACC_HAS_RETURN_TYPE)) {
+               if (tmp_is_instanceof < 0) {
+                       tmp_is_instanceof = 0;
+                       tmp_ce = NULL;
+               }
+               if (tmp_has_range < 0) {
+                       tmp_has_range = 0;
+               }
+               ret->type = tmp;
+               ret->ce = tmp_ce;
+               ret->is_instanceof = tmp_is_instanceof;
        }
-       ret->type = tmp;
-       ret->ce = tmp_ce;
-       ret->is_instanceof = tmp_is_instanceof;
        ret->range = tmp_range;
        ret->has_range = tmp_has_range;
 }
@@ -4087,7 +4140,7 @@ static int zend_infer_types(const zend_op_array *op_array, const zend_script *sc
        zend_type_narrowing(op_array, script, ssa);
 
        if (ZEND_FUNC_INFO(op_array)) {
-               zend_func_return_info(op_array, 1, 0, &ZEND_FUNC_INFO(op_array)->return_info);
+               zend_func_return_info(op_array, script, 1, 0, &ZEND_FUNC_INFO(op_array)->return_info);
        }
 
        free_alloca(worklist,  use_heap);
index b82d7b1c3976517a19bd2a0ddd99d5a22b365df1..6bf68090c52dd9bbda2249fd9946c7966d8caa88 100644 (file)
@@ -241,7 +241,11 @@ void zend_inference_check_recursive_dependencies(zend_op_array *op_array);
 
 int  zend_infer_types_ex(const zend_op_array *op_array, const zend_script *script, zend_ssa *ssa, zend_bitset worklist);
 
+void zend_init_func_return_info(const zend_op_array   *op_array,
+                                const zend_script     *script,
+                                zend_ssa_var_info     *ret);
 void zend_func_return_info(const zend_op_array   *op_array,
+                           const zend_script     *script,
                            int                    recursive,
                            int                    widening,
                            zend_ssa_var_info     *ret);
index 42c608416f0dd72da53a228cecc35a4a4c26ae3d..a18f79a8438d0125e2546e47788349c94b3274c1 100644 (file)
@@ -811,6 +811,15 @@ int zend_optimize_script(zend_script *script, zend_long optimization_level, zend
                        zend_revert_pass_two(call_graph.op_arrays[i]);
                }
 
+               for (i = 0; i < call_graph.op_arrays_count; i++) {
+                       if (call_graph.op_arrays[i]->fn_flags & ZEND_ACC_HAS_RETURN_TYPE) {
+                               func_info = ZEND_FUNC_INFO(call_graph.op_arrays[i]);
+                               if (func_info) {
+                                       zend_init_func_return_info(call_graph.op_arrays[i], script, &func_info->return_info);
+                               }
+                       }
+               }
+
                for (i = 0; i < call_graph.op_arrays_count; i++) {
                        func_info = ZEND_FUNC_INFO(call_graph.op_arrays[i]);
                        if (func_info) {
index 1144d89fb36a21c4ece7c566347c5afe52184acd..1df5ec032e68792c267c4385b4713a9720c3caf3 100644 (file)
@@ -664,6 +664,14 @@ static int zend_ssa_rename(const zend_op_array *op_array, uint32_t build_flags,
                                                var[EX_VAR_TO_NUM(opline->op1.var)] = ssa_vars_count;
                                                ssa_vars_count++;
                                        }
+                               case ZEND_VERIFY_RETURN_TYPE:
+                                       if (opline->op1_type != IS_CONST) {
+                                               ssa_ops[k].op1_def = ssa_vars_count;
+                                               var[EX_VAR_TO_NUM(opline->op1.var)] = ssa_vars_count;
+                                               ssa_vars_count++;
+                                               //NEW_SSA_VAR(opline->op1.var)
+                                       }
+                                       break;
                                default:
                                        break;
                        }