Skip to content

Commit 5789bb7

Browse files
committed
Initial prototyping implicit move
1 parent f06da77 commit 5789bb7

File tree

5 files changed

+177
-81
lines changed

5 files changed

+177
-81
lines changed

Zend/Optimizer/dfa_pass.c

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1066,6 +1066,42 @@ static bool zend_dfa_try_to_replace_result(zend_op_array *op_array, zend_ssa *ss
10661066
return 0;
10671067
}
10681068

1069+
/* Sets a flag on SEND ops when a copy can be a avoided. */
1070+
static void zend_dfa_optimize_send_copies(zend_op_array *op_array, zend_ssa *ssa, zend_call_info **call_map)
1071+
{
1072+
for (int v = 0; v < ssa->vars_count; v++) {
1073+
if (ssa->vars[v].var >= op_array->last_var) {
1074+
continue;
1075+
}
1076+
1077+
uint32_t type = ssa->var_info[v].type;
1078+
if (!(type & (MAY_BE_ANY-(MAY_BE_RC1|MAY_BE_ARRAY|MAY_BE_ARRAY_OF_ANY|MAY_BE_STRING|MAY_BE_NULL)))) {
1079+
continue;
1080+
}
1081+
1082+
int use = ssa->vars[v].use_chain;
1083+
if (use >= 0
1084+
&& op_array->opcodes[use].opcode == ZEND_SEND_VAR
1085+
&& op_array->opcodes[use].op2_type == IS_UNUSED) {
1086+
int next_use = zend_ssa_next_use(ssa->ops, v, use);
1087+
1088+
/* The next use must be an assignment of the call result, and there should be no other uses.
1089+
* If there is no next use or the next use is not an assignment, then we cannot safely
1090+
* perform the operation without a copy because the undef value would be observable indirectly
1091+
* through compact(), func_get_args(), ... */
1092+
if (next_use >= 0
1093+
&& op_array->opcodes[next_use].opcode == ZEND_ASSIGN
1094+
&& ssa->ops[next_use].op1_use == v
1095+
&& ssa->ops[next_use].op2_use >= 0
1096+
&& ssa->ops[call_map[use]->caller_call_opline - op_array->opcodes].result_def == ssa->ops[next_use].op2_use
1097+
&& zend_ssa_next_use(ssa->ops, v, next_use) < 0) {
1098+
ZEND_ASSERT(op_array->opcodes[use].extended_value == 0);
1099+
op_array->opcodes[use].extended_value = 1;
1100+
}
1101+
}
1102+
}
1103+
}
1104+
10691105
void zend_dfa_optimize_op_array(zend_op_array *op_array, zend_optimizer_ctx *ctx, zend_ssa *ssa, zend_call_info **call_map)
10701106
{
10711107
if (ctx->debug_level & ZEND_DUMP_BEFORE_DFA_PASS) {
@@ -1124,6 +1160,11 @@ void zend_dfa_optimize_op_array(zend_op_array *op_array, zend_optimizer_ctx *ctx
11241160
#endif
11251161
}
11261162

1163+
/* Optimization should not be done on main because of globals */
1164+
if (op_array->function_name) {
1165+
zend_dfa_optimize_send_copies(op_array, ssa, call_map);
1166+
}
1167+
11271168
for (v = op_array->last_var; v < ssa->vars_count; v++) {
11281169

11291170
op_1 = ssa->vars[v].definition;

Zend/zend_vm_def.h

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9900,7 +9900,7 @@ ZEND_VM_C_LABEL(fetch_dim_r_index_undef):
99009900
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
99019901
}
99029902

9903-
ZEND_VM_HOT_TYPE_SPEC_HANDLER(ZEND_SEND_VAR, op->op2_type == IS_UNUSED && (op1_info & (MAY_BE_UNDEF|MAY_BE_REF)) == 0, ZEND_SEND_VAR_SIMPLE, CV|VAR, NUM)
9903+
ZEND_VM_HOT_TYPE_SPEC_HANDLER(ZEND_SEND_VAR, op->op2_type == IS_UNUSED && !op->extended_value && (op1_info & (MAY_BE_UNDEF|MAY_BE_REF)) == 0, ZEND_SEND_VAR_SIMPLE, CV|VAR, NUM)
99049904
{
99059905
USE_OPLINE
99069906
zval *varptr, *arg;
@@ -9917,6 +9917,20 @@ ZEND_VM_HOT_TYPE_SPEC_HANDLER(ZEND_SEND_VAR, op->op2_type == IS_UNUSED && (op1_i
99179917
ZEND_VM_NEXT_OPCODE();
99189918
}
99199919

9920+
ZEND_VM_HOT_TYPE_SPEC_HANDLER(ZEND_SEND_VAR, op->extended_value /* extended_value implies here OP2 UNUSED and OP1 not UNDEF or REF */, ZEND_SEND_VAR_SIMPLE_EXT, CV, NUM)
9921+
{
9922+
USE_OPLINE
9923+
zval *varptr, *arg;
9924+
9925+
varptr = GET_OP1_ZVAL_PTR_UNDEF(BP_VAR_R);
9926+
arg = ZEND_CALL_VAR(EX(call), opline->result.var);
9927+
9928+
ZVAL_COPY_VALUE(arg, varptr);
9929+
ZVAL_UNDEF(varptr);
9930+
9931+
ZEND_VM_NEXT_OPCODE();
9932+
}
9933+
99209934
ZEND_VM_HOT_TYPE_SPEC_HANDLER(ZEND_SEND_VAR_EX, op->op2_type == IS_UNUSED && op->op2.num <= MAX_ARG_FLAG_NUM && (op1_info & (MAY_BE_UNDEF|MAY_BE_REF)) == 0, ZEND_SEND_VAR_EX_SIMPLE, CV|VAR, UNUSED|NUM)
99219935
{
99229936
USE_OPLINE

Zend/zend_vm_execute.h

Lines changed: 81 additions & 59 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Zend/zend_vm_handlers.h

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1850,10 +1850,11 @@
18501850
_(3455, ZEND_FETCH_DIM_R_INDEX_SPEC_CV_TMPVARCV) \
18511851
_(3458, ZEND_SEND_VAR_SIMPLE_SPEC_VAR) \
18521852
_(3460, ZEND_SEND_VAR_SIMPLE_SPEC_CV) \
1853-
_(3463, ZEND_SEND_VAR_EX_SIMPLE_SPEC_VAR_UNUSED) \
1854-
_(3465, ZEND_SEND_VAR_EX_SIMPLE_SPEC_CV_UNUSED) \
1855-
_(3466, ZEND_SEND_VAL_SIMPLE_SPEC_CONST) \
1856-
_(3467, ZEND_SEND_VAL_EX_SIMPLE_SPEC_CONST) \
1857-
_(3468, ZEND_FE_FETCH_R_SIMPLE_SPEC_VAR_CV_RETVAL_UNUSED) \
1858-
_(3469, ZEND_FE_FETCH_R_SIMPLE_SPEC_VAR_CV_RETVAL_USED) \
1859-
_(3469+1, ZEND_NULL)
1853+
_(3461, ZEND_SEND_VAR_SIMPLE_EXT_SPEC_CV) \
1854+
_(3464, ZEND_SEND_VAR_EX_SIMPLE_SPEC_VAR_UNUSED) \
1855+
_(3466, ZEND_SEND_VAR_EX_SIMPLE_SPEC_CV_UNUSED) \
1856+
_(3467, ZEND_SEND_VAL_SIMPLE_SPEC_CONST) \
1857+
_(3468, ZEND_SEND_VAL_EX_SIMPLE_SPEC_CONST) \
1858+
_(3469, ZEND_FE_FETCH_R_SIMPLE_SPEC_VAR_CV_RETVAL_UNUSED) \
1859+
_(3470, ZEND_FE_FETCH_R_SIMPLE_SPEC_VAR_CV_RETVAL_USED) \
1860+
_(3470+1, ZEND_NULL)

ext/standard/array.c

Lines changed: 32 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3943,24 +3943,37 @@ static zend_always_inline void php_array_merge_wrapper(INTERNAL_FUNCTION_PARAMET
39433943
}
39443944
}
39453945

3946+
bool in_place = false;
39463947
arg = args;
39473948
src = Z_ARRVAL_P(arg);
3948-
/* copy first array */
3949-
array_init_size(return_value, count);
3950-
dest = Z_ARRVAL_P(return_value);
3949+
/* copy first array if necessary */
39513950
if (HT_IS_PACKED(src)) {
3952-
zend_hash_real_init_packed(dest);
3953-
ZEND_HASH_FILL_PACKED(dest) {
3954-
ZEND_HASH_PACKED_FOREACH_VAL(src, src_entry) {
3955-
if (UNEXPECTED(Z_ISREF_P(src_entry) &&
3956-
Z_REFCOUNT_P(src_entry) == 1)) {
3957-
src_entry = Z_REFVAL_P(src_entry);
3958-
}
3959-
Z_TRY_ADDREF_P(src_entry);
3960-
ZEND_HASH_FILL_ADD(src_entry);
3961-
} ZEND_HASH_FOREACH_END();
3962-
} ZEND_HASH_FILL_END();
3951+
/* Note: If it has holes, it might get sequentialized */
3952+
if (HT_IS_WITHOUT_HOLES(src) && !(GC_FLAGS(Z_ARRVAL_P(arg)) & (IS_ARRAY_IMMUTABLE | IS_ARRAY_PERSISTENT)) && Z_REFCOUNT_P(arg) == 1) {
3953+
dest = src;
3954+
in_place = true;
3955+
ZVAL_ARR(return_value, dest);
3956+
//fprintf(stderr, "in-place %d\n", GC_REFCOUNT(Z_ARRVAL_P(return_value)));
3957+
} else {
3958+
array_init_size(return_value, count);
3959+
dest = Z_ARRVAL_P(return_value);
3960+
3961+
zend_hash_real_init_packed(dest);
3962+
ZEND_HASH_FILL_PACKED(dest) {
3963+
ZEND_HASH_PACKED_FOREACH_VAL(src, src_entry) {
3964+
if (UNEXPECTED(Z_ISREF_P(src_entry) &&
3965+
Z_REFCOUNT_P(src_entry) == 1)) {
3966+
src_entry = Z_REFVAL_P(src_entry);
3967+
}
3968+
Z_TRY_ADDREF_P(src_entry);
3969+
ZEND_HASH_FILL_ADD(src_entry);
3970+
} ZEND_HASH_FOREACH_END();
3971+
} ZEND_HASH_FILL_END();
3972+
}
39633973
} else {
3974+
array_init_size(return_value, count);
3975+
dest = Z_ARRVAL_P(return_value);
3976+
39643977
zend_string *string_key;
39653978
zend_hash_real_init_mixed(dest);
39663979
ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(src, string_key, src_entry) {
@@ -3987,6 +4000,11 @@ static zend_always_inline void php_array_merge_wrapper(INTERNAL_FUNCTION_PARAMET
39874000
php_array_merge(dest, Z_ARRVAL_P(arg));
39884001
}
39894002
}
4003+
4004+
if (in_place) {
4005+
GC_ADDREF(Z_ARRVAL_P(return_value));
4006+
}
4007+
//fprintf(stderr, "after, in-place %d %d\n", in_place, GC_REFCOUNT(Z_ARRVAL_P(return_value)));
39904008
}
39914009
/* }}} */
39924010

0 commit comments

Comments
 (0)