Browse Source

Introduce BIND_LEXICAL

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.
pull/1699/head
Nikita Popov 10 years ago
parent
commit
65e456f364
  1. 15
      Zend/zend_closures.c
  2. 1
      Zend/zend_closures.h
  3. 42
      Zend/zend_compile.c
  4. 1
      Zend/zend_compile.h
  5. 2
      Zend/zend_types.h
  6. 53
      Zend/zend_variables.c
  7. 2
      Zend/zend_variables.h
  8. 31
      Zend/zend_vm_def.h
  9. 56
      Zend/zend_vm_execute.h
  10. 6
      Zend/zend_vm_opcodes.c
  11. 3
      Zend/zend_vm_opcodes.h
  12. 18
      ext/opcache/Optimizer/zend_cfg.c
  13. 1
      ext/opcache/Optimizer/zend_dfg.c
  14. 3
      ext/opcache/Optimizer/zend_dump.c
  15. 14
      ext/opcache/Optimizer/zend_inference.c
  16. 7
      ext/opcache/Optimizer/zend_ssa.c

15
Zend/zend_closures.c

@ -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

1
Zend/zend_closures.h

@ -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;

42
Zend/zend_compile.c

@ -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;

1
Zend/zend_compile.h

@ -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

2
Zend/zend_types.h

@ -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 */

53
Zend/zend_variables.c

@ -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

2
Zend/zend_variables.h

@ -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);

31
Zend/zend_vm_def.h

@ -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);

56
Zend/zend_vm_execute.h

@ -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;

6
Zend/zend_vm_opcodes.c

@ -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) {

3
Zend/zend_vm_opcodes.h

@ -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

18
ext/opcache/Optimizer/zend_cfg.c

@ -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;

1
ext/opcache/Optimizer/zend_dfg.c

@ -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));

3
ext/opcache/Optimizer/zend_dump.c

@ -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) {

14
ext/opcache/Optimizer/zend_inference.c

@ -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:

7
ext/opcache/Optimizer/zend_ssa.c

@ -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;
}

Loading…
Cancel
Save