Skip to content

Commit

Permalink
Introduce BIND_LEXICAL
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
nikic committed Dec 29, 2015
1 parent 4440436 commit 65e456f
Show file tree
Hide file tree
Showing 16 changed files with 157 additions and 98 deletions.
15 changes: 10 additions & 5 deletions Zend/zend_closures.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions Zend/zend_closures.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
42 changes: 31 additions & 11 deletions Zend/zend_compile.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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);
}
}
Expand Down Expand Up @@ -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;
Expand Down
1 change: 0 additions & 1 deletion Zend/zend_compile.h
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
2 changes: 0 additions & 2 deletions Zend/zend_types.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */

Expand Down
53 changes: 0 additions & 53 deletions Zend/zend_variables.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 0 additions & 2 deletions Zend/zend_variables.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
31 changes: 31 additions & 0 deletions Zend/zend_vm_def.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 changes: 56 additions & 0 deletions Zend/zend_vm_execute.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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;
Expand Down
6 changes: 4 additions & 2 deletions Zend/zend_vm_opcodes.c
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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) {
Expand Down
3 changes: 2 additions & 1 deletion Zend/zend_vm_opcodes.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 changes: 0 additions & 18 deletions ext/opcache/Optimizer/zend_cfg.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
1 change: 1 addition & 0 deletions ext/opcache/Optimizer/zend_dfg.c
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand Down
3 changes: 0 additions & 3 deletions ext/opcache/Optimizer/zend_dump.c
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
Loading

0 comments on commit 65e456f

Please sign in to comment.