]> granicus.if.org Git - php/commitdiff
Introduce BIND_LEXICAL
authorNikita Popov <nikic@php.net>
Tue, 29 Dec 2015 10:16:08 +0000 (11:16 +0100)
committerNikita Popov <nikic@php.net>
Tue, 29 Dec 2015 22:14:53 +0000 (23:14 +0100)
This opcodes inserts a local CV into the closure static variable
table. This replaces the previous mechanism of having static
variables marked as LEXICAL, which perform a symtable lookup
during copying.

This means a) functions which contain closures no longer have to
rebuild their symtable (better performance) and b) we can now track
used variables in SSA.

16 files changed:
Zend/zend_closures.c
Zend/zend_closures.h
Zend/zend_compile.c
Zend/zend_compile.h
Zend/zend_types.h
Zend/zend_variables.c
Zend/zend_variables.h
Zend/zend_vm_def.h
Zend/zend_vm_execute.h
Zend/zend_vm_opcodes.c
Zend/zend_vm_opcodes.h
ext/opcache/Optimizer/zend_cfg.c
ext/opcache/Optimizer/zend_dfg.c
ext/opcache/Optimizer/zend_dump.c
ext/opcache/Optimizer/zend_inference.c
ext/opcache/Optimizer/zend_ssa.c

index beca1cef0750c6e74eb94aa77c287f11e212a63b..4d3a97c0acecd5dafe051b19cc4b1eeb7ec1b9ce 100644 (file)
@@ -570,11 +570,8 @@ ZEND_API void zend_create_closure(zval *res, zend_function *func, zend_class_ent
                closure->func.common.prototype = (zend_function*)closure;
                closure->func.common.fn_flags |= ZEND_ACC_CLOSURE;
                if (closure->func.op_array.static_variables) {
-                       HashTable *static_variables = closure->func.op_array.static_variables;
-
-                       ALLOC_HASHTABLE(closure->func.op_array.static_variables);
-                       zend_hash_init(closure->func.op_array.static_variables, zend_hash_num_elements(static_variables), NULL, ZVAL_PTR_DTOR, 0);
-                       zend_hash_apply_with_arguments(static_variables, zval_copy_static_var, 1, closure->func.op_array.static_variables);
+                       closure->func.op_array.static_variables =
+                               zend_array_dup(closure->func.op_array.static_variables);
                }
                if (UNEXPECTED(!closure->func.op_array.run_time_cache)) {
                        closure->func.op_array.run_time_cache = func->op_array.run_time_cache = zend_arena_alloc(&CG(arena), func->op_array.cache_size);
@@ -629,6 +626,14 @@ ZEND_API void zend_create_fake_closure(zval *res, zend_function *func, zend_clas
 }
 /* }}} */
 
+void zend_closure_bind_var(zval *closure_zv, zend_string *var_name, zval *var) /* {{{ */
+{
+       zend_closure *closure = (zend_closure *) Z_OBJ_P(closure_zv);
+       HashTable *static_variables = closure->func.op_array.static_variables;
+       zend_hash_update(static_variables, var_name, var);
+}
+/* }}} */
+
 /*
  * Local variables:
  * tab-width: 4
index 8d4edfd37a4bdadbc272639762d2afe161e1f5f3..c6dff9b4bfa696c74fba12fcf3e7db51d4e8eb15 100644 (file)
@@ -25,6 +25,7 @@
 BEGIN_EXTERN_C()
 
 void zend_register_closure_ce(void);
+void zend_closure_bind_var(zval *closure_zv, zend_string *var_name, zval *var);
 
 extern ZEND_API zend_class_entry *zend_ce_closure;
 
index 3a7e0b552cdce885ed9d71941b9b5125b4f867b5..9c07afe694d9fa95c97c73febf41249b0b81eddc 100644 (file)
@@ -2055,7 +2055,8 @@ static void zend_check_live_ranges(zend_op *opline) /* {{{ */
                               opline->opcode == ZEND_ROPE_END ||
                               opline->opcode == ZEND_END_SILENCE ||
                               opline->opcode == ZEND_FETCH_LIST ||
-                              opline->opcode == ZEND_VERIFY_RETURN_TYPE) {
+                              opline->opcode == ZEND_VERIFY_RETURN_TYPE ||
+                              opline->opcode == ZEND_BIND_LEXICAL) {
                        /* these opcodes are handled separately */
                } else {
                        zend_find_live_range(opline, opline->op1_type, opline->op1.var);
@@ -4893,28 +4894,44 @@ void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast) /* {{{ */
 }
 /* }}} */
 
-void zend_compile_closure_uses(zend_ast *ast) /* {{{ */
+static void zend_compile_closure_binding(znode *closure, zend_ast *uses_ast) /* {{{ */
 {
-       zend_ast_list *list = zend_ast_get_list(ast);
+       zend_ast_list *list = zend_ast_get_list(uses_ast);
        uint32_t i;
 
        for (i = 0; i < list->children; ++i) {
-               zend_ast *var_ast = list->child[i];
-               zend_string *name = zend_ast_get_str(var_ast);
-               zend_bool by_ref = var_ast->attr;
-               zval zv;
+               zend_ast *var_name_ast = list->child[i];
+               zend_string *var_name = zend_ast_get_str(var_name_ast);
+               zend_bool by_ref = var_name_ast->attr;
+               zend_op *opline;
 
-               if (zend_string_equals_literal(name, "this")) {
+               if (zend_string_equals_literal(var_name, "this")) {
                        zend_error_noreturn(E_COMPILE_ERROR, "Cannot use $this as lexical variable");
                }
 
-               if (zend_is_auto_global(name)) {
+               if (zend_is_auto_global(var_name)) {
                        zend_error_noreturn(E_COMPILE_ERROR, "Cannot use auto-global as lexical variable");
                }
 
-               ZVAL_NULL(&zv);
-               Z_CONST_FLAGS(zv) = by_ref ? IS_LEXICAL_REF : IS_LEXICAL_VAR;
+               opline = zend_emit_op(NULL, ZEND_BIND_LEXICAL, closure, NULL);
+               opline->op2_type = IS_CV;
+               opline->op2.var = lookup_cv(CG(active_op_array), zend_string_copy(var_name));
+               opline->extended_value = by_ref;
+       }
+}
+/* }}} */
 
+void zend_compile_closure_uses(zend_ast *ast) /* {{{ */
+{
+       zend_ast_list *list = zend_ast_get_list(ast);
+       uint32_t i;
+
+       for (i = 0; i < list->children; ++i) {
+               zend_ast *var_ast = list->child[i];
+               zend_bool by_ref = var_ast->attr;
+
+               zval zv;
+               ZVAL_NULL(&zv);
                zend_compile_static_var_common(var_ast, &zv, by_ref);
        }
 }
@@ -5166,6 +5183,9 @@ void zend_compile_func_decl(znode *result, zend_ast *ast) /* {{{ */
                zend_begin_method_decl(op_array, decl->name, has_body);
        } else {
                zend_begin_func_decl(result, op_array, decl);
+               if (uses_ast) {
+                       zend_compile_closure_binding(result, uses_ast);
+               }
        }
 
        CG(active_op_array) = op_array;
index b157db5f63dae2c8c4a246559f54dcee63f18f33..d145ffc0a05950905a6cc287b01dd49ac311cc33 100644 (file)
@@ -892,7 +892,6 @@ ZEND_API void zend_assert_valid_class_name(const zend_string *const_name);
 #define ZEND_FETCH_LOCAL                       0x10000000
 #define ZEND_FETCH_STATIC                      0x20000000
 #define ZEND_FETCH_GLOBAL_LOCK         0x40000000
-#define ZEND_FETCH_LEXICAL                     0x50000000
 
 #define ZEND_FETCH_TYPE_MASK           0x70000000
 
index 65360619fdf666459f23a3574d650e53b1e19c90..17845e05380b00092cb1d18541e34335365d9416 100644 (file)
@@ -412,8 +412,6 @@ static zend_always_inline zend_uchar zval_get_type(const zval* pz) {
 
 /* zval.u1.v.const_flags */
 #define IS_CONSTANT_UNQUALIFIED                0x010
-#define IS_LEXICAL_VAR                         0x020
-#define IS_LEXICAL_REF                         0x040
 #define IS_CONSTANT_CLASS           0x080  /* __CLASS__ in trait */
 #define IS_CONSTANT_IN_NAMESPACE       0x100  /* used only in opline->extended_value */
 
index 7f99d71a14b95b243b294f4f4c9206a1e24b3a31..fa57a83e705f76b0c8c33c352d71dea9c817c1c9 100644 (file)
@@ -267,59 +267,6 @@ ZEND_API void _zval_internal_ptr_dtor_wrapper(zval *zval_ptr)
 }
 #endif
 
-ZEND_API int zval_copy_static_var(zval *p, int num_args, va_list args, zend_hash_key *key) /* {{{ */
-{
-       zend_array *symbol_table;
-       HashTable *target = va_arg(args, HashTable*);
-       zend_bool is_ref;
-       zval tmp;
-
-       if (Z_CONST_FLAGS_P(p) & (IS_LEXICAL_VAR|IS_LEXICAL_REF)) {
-               is_ref = Z_CONST_FLAGS_P(p) & IS_LEXICAL_REF;
-
-               symbol_table = zend_rebuild_symbol_table();
-               p = zend_hash_find(symbol_table, key->key);
-               if (!p) {
-                       p = &tmp;
-                       ZVAL_NULL(&tmp);
-                       if (is_ref) {
-                               ZVAL_NEW_REF(&tmp, &tmp);
-                               zend_hash_add_new(symbol_table, key->key, &tmp);
-                               Z_ADDREF_P(p);
-                       } else {
-                               zend_error(E_NOTICE,"Undefined variable: %s", ZSTR_VAL(key->key));
-                       }
-               } else {
-                       if (Z_TYPE_P(p) == IS_INDIRECT) {
-                               p = Z_INDIRECT_P(p);
-                               if (Z_TYPE_P(p) == IS_UNDEF) {
-                                       if (!is_ref) {
-                                               zend_error(E_NOTICE,"Undefined variable: %s", ZSTR_VAL(key->key));
-                                               p = &tmp;
-                                               ZVAL_NULL(&tmp);
-                                       } else {
-                                               ZVAL_NULL(p);
-                                       }
-                               }
-                       }
-                       if (is_ref) {
-                               ZVAL_MAKE_REF(p);
-                               Z_ADDREF_P(p);
-                       } else if (Z_ISREF_P(p)) {
-                               ZVAL_DUP(&tmp, Z_REFVAL_P(p));
-                               p = &tmp;
-                       } else if (Z_REFCOUNTED_P(p)) {
-                               Z_ADDREF_P(p);
-                       }
-               }
-       } else if (Z_REFCOUNTED_P(p)) {
-               Z_ADDREF_P(p);
-       }
-       zend_hash_add(target, key->key, p);
-       return ZEND_HASH_APPLY_KEEP;
-}
-/* }}} */
-
 /*
  * Local variables:
  * tab-width: 4
index 9748a4ad2411583a30a7b45bfa54607e4341cdff..41027d18aa67207dc93e5f479c15e3fdcaddc2c1 100644 (file)
@@ -106,8 +106,6 @@ static zend_always_inline void _zval_opt_copy_ctor_no_imm(zval *zvalue ZEND_FILE
        }
 }
 
-ZEND_API int zval_copy_static_var(zval *p, int num_args, va_list args, zend_hash_key *key);
-
 ZEND_API size_t zend_print_variable(zval *var);
 ZEND_API void _zval_ptr_dtor(zval *zval_ptr ZEND_FILE_LINE_DC);
 ZEND_API void _zval_internal_dtor_for_ptr(zval *zvalue ZEND_FILE_LINE_DC);
index 263e2fa756716939c47cb118e53fde9cb1f7d5d9..0efbd93f5922b83577b6d15e6c63156b5c422950 100644 (file)
@@ -7997,4 +7997,35 @@ ZEND_VM_C_LABEL(call_trampoline_end):
        ZEND_VM_LEAVE();
 }
 
+ZEND_VM_HANDLER(182, ZEND_BIND_LEXICAL, TMP, CV, REF)
+{
+       USE_OPLINE
+       zend_free_op free_op1, free_op2;
+       zval *closure, *var;
+       zend_string *var_name;
+
+       closure = GET_OP1_ZVAL_PTR(BP_VAR_R);
+       if (opline->extended_value) {
+               /* By-ref binding */
+               var = GET_OP2_ZVAL_PTR(BP_VAR_W);
+               ZVAL_MAKE_REF(var);
+               Z_ADDREF_P(var);
+       } else {
+               var = GET_OP2_ZVAL_PTR_UNDEF(BP_VAR_R);
+               if (UNEXPECTED(Z_ISUNDEF_P(var))) {
+                       SAVE_OPLINE();
+                       var = GET_OP2_UNDEF_CV(var, BP_VAR_R);
+                       if (UNEXPECTED(EG(exception))) {
+                               HANDLE_EXCEPTION();
+                       }
+               }
+               ZVAL_DEREF(var);
+               Z_TRY_ADDREF_P(var);
+       }
+
+       var_name = CV_DEF_OF(EX_VAR_TO_NUM(opline->op2.var));
+       zend_closure_bind_var(closure, var_name, var);
+       ZEND_VM_NEXT_OPCODE();
+}
+
 ZEND_VM_DEFINE_OP(137, ZEND_OP_DATA);
index a923dffd9cf5977b3f11ce79f60fc4aab799245d..cfe8a620ad40fea36c0bd79c531a067a097727cd 100644 (file)
@@ -14210,6 +14210,37 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_TMP_CV_HANDLER(ZEND
        ZEND_VM_RETURN();
 }
 
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_BIND_LEXICAL_SPEC_TMP_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+       USE_OPLINE
+       zend_free_op free_op1;
+       zval *closure, *var;
+       zend_string *var_name;
+
+       closure = _get_zval_ptr_tmp(opline->op1.var, execute_data, &free_op1);
+       if (opline->extended_value) {
+               /* By-ref binding */
+               var = _get_zval_ptr_cv_BP_VAR_W(execute_data, opline->op2.var);
+               ZVAL_MAKE_REF(var);
+               Z_ADDREF_P(var);
+       } else {
+               var = _get_zval_ptr_cv_undef(execute_data, opline->op2.var);
+               if (UNEXPECTED(Z_ISUNDEF_P(var))) {
+                       SAVE_OPLINE();
+                       var = GET_OP2_UNDEF_CV(var, BP_VAR_R);
+                       if (UNEXPECTED(EG(exception))) {
+                               HANDLE_EXCEPTION();
+                       }
+               }
+               ZVAL_DEREF(var);
+               Z_TRY_ADDREF_P(var);
+       }
+
+       var_name = CV_DEF_OF(EX_VAR_TO_NUM(opline->op2.var));
+       zend_closure_bind_var(closure, var_name, var);
+       ZEND_VM_NEXT_OPCODE();
+}
+
 static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_FUNC_ARG_SPEC_TMP_TMPVAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
 {
        USE_OPLINE
@@ -49334,6 +49365,31 @@ void zend_init_opcodes_handlers(void)
        ZEND_NULL_HANDLER,
        ZEND_NULL_HANDLER,
        ZEND_NULL_HANDLER,
+       ZEND_NULL_HANDLER,
+       ZEND_NULL_HANDLER,
+       ZEND_NULL_HANDLER,
+       ZEND_NULL_HANDLER,
+       ZEND_NULL_HANDLER,
+       ZEND_NULL_HANDLER,
+       ZEND_NULL_HANDLER,
+       ZEND_NULL_HANDLER,
+       ZEND_NULL_HANDLER,
+       ZEND_BIND_LEXICAL_SPEC_TMP_CV_HANDLER,
+       ZEND_NULL_HANDLER,
+       ZEND_NULL_HANDLER,
+       ZEND_NULL_HANDLER,
+       ZEND_NULL_HANDLER,
+       ZEND_NULL_HANDLER,
+       ZEND_NULL_HANDLER,
+       ZEND_NULL_HANDLER,
+       ZEND_NULL_HANDLER,
+       ZEND_NULL_HANDLER,
+       ZEND_NULL_HANDLER,
+       ZEND_NULL_HANDLER,
+       ZEND_NULL_HANDLER,
+       ZEND_NULL_HANDLER,
+       ZEND_NULL_HANDLER,
+       ZEND_NULL_HANDLER,
        ZEND_NULL_HANDLER
   };
   zend_opcode_handlers = labels;
index 2776a80e6ff2acf74e47895fb29545a87330d397..1bf19d01e3ff87179224c98154201f953974a806 100644 (file)
@@ -21,7 +21,7 @@
 #include <stdio.h>
 #include <zend.h>
 
-static const char *zend_vm_opcodes_names[182] = {
+static const char *zend_vm_opcodes_names[183] = {
        "ZEND_NOP",
        "ZEND_ADD",
        "ZEND_SUB",
@@ -204,9 +204,10 @@ static const char *zend_vm_opcodes_names[182] = {
        "ZEND_UNSET_STATIC_PROP",
        "ZEND_ISSET_ISEMPTY_STATIC_PROP",
        "ZEND_FETCH_CLASS_CONSTANT",
+       "ZEND_BIND_LEXICAL",
 };
 
-static uint32_t zend_vm_opcodes_flags[182] = {
+static uint32_t zend_vm_opcodes_flags[183] = {
        0x00000000,
        0x00000707,
        0x00000707,
@@ -389,6 +390,7 @@ static uint32_t zend_vm_opcodes_flags[182] = {
        0x00007307,
        0x00027307,
        0x00000373,
+       0x00100101,
 };
 
 ZEND_API const char* zend_get_opcode_name(zend_uchar opcode) {
index 21f48cdb2b1cd965a26b9153070fccbf66fe2073..03793910b2a87e0a045fa4f47e38f1c1166a71c9 100644 (file)
@@ -254,7 +254,8 @@ END_EXTERN_C()
 #define ZEND_UNSET_STATIC_PROP               179
 #define ZEND_ISSET_ISEMPTY_STATIC_PROP       180
 #define ZEND_FETCH_CLASS_CONSTANT            181
+#define ZEND_BIND_LEXICAL                    182
 
-#define ZEND_VM_LAST_OPCODE                  181
+#define ZEND_VM_LAST_OPCODE                  182
 
 #endif
index 9446954e518e5b71c87e76672670ffb2609f2cab..9ada8df1a0667c10899a2af5245394d74b811d80 100644 (file)
@@ -372,24 +372,6 @@ int zend_build_cfg(zend_arena **arena, const zend_op_array *op_array, uint32_t b
                                BB_START(OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes);
                                BB_START(i + 1);
                                break;
-                       case ZEND_DECLARE_LAMBDA_FUNCTION: {
-//???                                  zend_op_array *lambda_op_array;
-//???
-//???                                  zv = CRT_CONSTANT(opline->op1);
-//???                                  if (ctx->main_script &&
-//???                                      (lambda_op_array = zend_hash_find_ptr(&ctx->main_script->function_table, Z_STR_P(zv))) != NULL) {
-//???                                          if (lambda_op_array->type == ZEND_USER_FUNCTION &&
-//???                                              lambda_op_array->static_variables) {
-//???                                                  // FIXME: Really we should try to perform alias
-//???                                                  // analysis on variables used by the closure
-//???                                                  info->flags |= ZEND_FUNC_TOO_DYNAMIC;
-//???                                          }
-//???                                  } else {
-//???                                          // FIXME: how to find the lambda function?
-                                               flags |= ZEND_FUNC_TOO_DYNAMIC;
-//???                                  }
-                               }
-                               break;
                        case ZEND_UNSET_VAR:
                                if (!(opline->extended_value & ZEND_QUICK_SET)) {
                                        flags |= ZEND_FUNC_TOO_DYNAMIC;
index 7c3e80db1fc4a2c5762d396ccccfdc200342cc7c..ea81e288d938f31a3eea3b08a839909bfb8cf42d 100644 (file)
@@ -99,6 +99,7 @@ int zend_build_dfg(const zend_op_array *op_array, const zend_cfg *cfg, zend_dfg
                                        case ZEND_FE_RESET_RW:
                                        case ZEND_ADD_ARRAY_ELEMENT:
                                        case ZEND_INIT_ARRAY:
+                                       case ZEND_BIND_LEXICAL:
                                                if (!DFG_ISSET(use, set_size, j, EX_VAR_TO_NUM(opline->op1.var))) {
                                                        // FIXME: include into "use" to ...?
                                                        DFG_SET(use, set_size, j, EX_VAR_TO_NUM(opline->op1.var));
index 0b4848baf11d4b6b24c8f71acbe214456ab92355..c8fb945067ccf7d349a2711f518ab10a83a9ca1e 100644 (file)
@@ -516,9 +516,6 @@ static void zend_dump_op(const zend_op_array *op_array, const zend_basic_block *
                                case ZEND_FETCH_GLOBAL_LOCK:
                                        fprintf(stderr, " (global+lock)");
                                        break;
-                               case ZEND_FETCH_LEXICAL:
-                                       fprintf(stderr, " (lexical)");
-                                       break;
                        }
                }
                if (ZEND_VM_EXT_ISSET & flags) {
index 8fa9bfdbb5bdd120259ff2b9bba024e50a2f5cb5..84abb27a530539ca074500f3a044bc4c3a113d8e 100644 (file)
@@ -3062,6 +3062,20 @@ static void zend_update_type_info(const zend_op_array *op_array,
                                UPDATE_SSA_OBJ_TYPE(NULL, 0, ssa_ops[i].op1_def);
                        }
                        break;
+               case ZEND_BIND_LEXICAL:
+                       if (ssa_ops[i].op2_def >= 0) {
+                               tmp = t2 | MAY_BE_RC1 | MAY_BE_RCN;
+                               if (opline->extended_value) {
+                                       tmp |= MAY_BE_REF;
+                               }
+                               UPDATE_SSA_TYPE(tmp, ssa_ops[i].op2_def);
+                               if ((t2 & MAY_BE_OBJECT) && ssa_var_info[ssa_ops[i].op2_use].ce) {
+                                       UPDATE_SSA_OBJ_TYPE(ssa_var_info[ssa_ops[i].op2_use].ce, ssa_var_info[ssa_ops[i].op2_use].is_instanceof, ssa_ops[i].op2_def);
+                               } else {
+                                       UPDATE_SSA_OBJ_TYPE(NULL, 0, ssa_ops[i].op2_def);
+                               }
+                       }
+                       break;
                case ZEND_SEND_VAR_EX:
                case ZEND_SEND_VAR_NO_REF:
                case ZEND_SEND_REF:
index 505c0b978fa0ea434f04f184685359768aa21ff4..de9263026b7facc9cd96cf286f3a679c9172daa2 100644 (file)
@@ -354,6 +354,13 @@ static int zend_ssa_rename(const zend_op_array *op_array, uint32_t build_flags,
                                                //NEW_SSA_VAR(opline->op1.var)
                                        }
                                        break;
+                               case ZEND_BIND_LEXICAL:
+                                       if (opline->extended_value || (build_flags & ZEND_SSA_RC_INFERENCE)) {
+                                               ssa_ops[k].op2_def = ssa_vars_count;
+                                               var[EX_VAR_TO_NUM(opline->op2.var)] = ssa_vars_count;
+                                               ssa_vars_count++;
+                                       }
+                                       break;
                                default:
                                        break;
                        }