Skip to content
4 changes: 4 additions & 0 deletions Zend/Optimizer/block_pass.c
Original file line number Diff line number Diff line change
Expand Up @@ -911,6 +911,7 @@ static void zend_optimize_block(zend_basic_block *block, zend_op_array *op_array
src->opcode != ZEND_COPY_TMP &&
src->opcode != ZEND_ADD_ARRAY_ELEMENT &&
src->opcode != ZEND_ADD_ARRAY_UNPACK &&
src->opcode != ZEND_ARRAY_SET_PLACEHOLDER &&
(src->opcode != ZEND_DECLARE_LAMBDA_FUNCTION ||
src == opline -1)) {
src->result.var = opline->result.var;
Expand Down Expand Up @@ -1474,6 +1475,7 @@ static void zend_t_usage(zend_cfg *cfg, zend_op_array *op_array, zend_bitset use
switch (opline->opcode) {
case ZEND_ADD_ARRAY_ELEMENT:
case ZEND_ADD_ARRAY_UNPACK:
case ZEND_ARRAY_SET_PLACEHOLDER:
case ZEND_ROPE_ADD:
/* these opcodes use the result as argument */
if (!zend_bitset_in(defined_here, var_num)) {
Expand Down Expand Up @@ -1568,6 +1570,7 @@ static void zend_t_usage(zend_cfg *cfg, zend_op_array *op_array, zend_bitset use
break;
case ZEND_ADD_ARRAY_ELEMENT:
case ZEND_ADD_ARRAY_UNPACK:
case ZEND_ARRAY_SET_PLACEHOLDER:
case ZEND_ROPE_ADD:
zend_bitset_incl(usage, VAR_NUM(opline->result.var));
break;
Expand All @@ -1576,6 +1579,7 @@ static void zend_t_usage(zend_cfg *cfg, zend_op_array *op_array, zend_bitset use
switch (opline->opcode) {
case ZEND_ADD_ARRAY_ELEMENT:
case ZEND_ADD_ARRAY_UNPACK:
case ZEND_ARRAY_SET_PLACEHOLDER:
case ZEND_ROPE_ADD:
break;
default:
Expand Down
2 changes: 2 additions & 0 deletions Zend/Optimizer/dce.c
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,8 @@ static inline bool may_have_side_effects(
case ZEND_ROPE_INIT:
case ZEND_ROPE_ADD:
case ZEND_INIT_ARRAY:
case ZEND_ARRAY_DUP:
case ZEND_ARRAY_SET_PLACEHOLDER:
case ZEND_SPACESHIP:
case ZEND_STRLEN:
case ZEND_COUNT:
Expand Down
6 changes: 6 additions & 0 deletions Zend/Optimizer/dfa_pass.c
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,12 @@ static bool opline_supports_assign_contraction(
&& (opline->op2_type != IS_CV || opline->op2.var != cv_var);
}

if (opline->opcode == ZEND_ARRAY_DUP) {
/* FIXME: Is this ok? Array initialization might be incomplete if value
* evaluation fails, but this is no different from ZEND_INIT_ARRAY. */
return false;
}

if (opline->opcode == ZEND_CAST
&& (opline->extended_value == IS_ARRAY || opline->extended_value == IS_OBJECT)) {
/* CAST to array/object may initialize the result to an empty array/object before
Expand Down
10 changes: 9 additions & 1 deletion Zend/Optimizer/escape_analysis.c
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ static bool is_allocation_def(zend_op_array *op_array, zend_ssa *ssa, int def, i
if (ssa_op->result_def == var) {
switch (opline->opcode) {
case ZEND_INIT_ARRAY:
case ZEND_ARRAY_DUP:
return 1;
case ZEND_NEW: {
/* objects with destructors should escape */
Expand Down Expand Up @@ -227,6 +228,8 @@ static bool is_local_def(zend_op_array *op_array, zend_ssa *ssa, int def, int va
switch (opline->opcode) {
case ZEND_INIT_ARRAY:
case ZEND_ADD_ARRAY_ELEMENT:
case ZEND_ARRAY_DUP:
case ZEND_ARRAY_SET_PLACEHOLDER:
case ZEND_QM_ASSIGN:
case ZEND_ASSIGN:
return 1;
Expand Down Expand Up @@ -312,6 +315,8 @@ static bool is_escape_use(zend_op_array *op_array, zend_ssa *ssa, int use, int v
if (opline->extended_value & ZEND_ARRAY_ELEMENT_REF) {
return 1;
}
ZEND_FALLTHROUGH;
case ZEND_ARRAY_SET_PLACEHOLDER:
if (OP1_INFO() & MAY_BE_OBJECT) {
/* object aliasing */
return 1;
Expand Down Expand Up @@ -369,6 +374,8 @@ static bool is_escape_use(zend_op_array *op_array, zend_ssa *ssa, int use, int v
case ZEND_QM_ASSIGN:
case ZEND_INIT_ARRAY:
case ZEND_ADD_ARRAY_ELEMENT:
case ZEND_ARRAY_DUP:
case ZEND_ARRAY_SET_PLACEHOLDER:
break;
default:
return 1;
Expand Down Expand Up @@ -488,7 +495,8 @@ zend_result zend_ssa_escape_analysis(const zend_script *script, zend_op_array *o
(op-1)->op1_use >= 0) {
enclosing_root = ees[(op-1)->op1_use];
} else if ((opline->opcode == ZEND_INIT_ARRAY ||
opline->opcode == ZEND_ADD_ARRAY_ELEMENT) &&
opline->opcode == ZEND_ADD_ARRAY_ELEMENT ||
opline->opcode == ZEND_ARRAY_SET_PLACEHOLDER) &&
op->op1_use == i &&
op->result_def >= 0) {
enclosing_root = ees[op->result_def];
Expand Down
65 changes: 64 additions & 1 deletion Zend/Optimizer/sccp.c
Original file line number Diff line number Diff line change
Expand Up @@ -1182,6 +1182,67 @@ static void sccp_visit_instr(scdf_ctx *scdf, zend_op *opline, zend_ssa_op *ssa_o
}
return;
}
case ZEND_ARRAY_DUP: {
SET_RESULT(result, op1);
return;
}
case ZEND_ARRAY_SET_PLACEHOLDER: {
zval *result = &ctx->values[ssa_op->result_use];

SKIP_IF_TOP(result);
SKIP_IF_TOP(op1);

#if 1
/* FIXME: See below. */
if (IS_BOT(result)) {
SET_RESULT_BOT(result);
return;
}
#else
ZEND_ASSERT(!IS_BOT(result));
#endif

/* FIXME: Avoid dup-ing the array in every step. */
zend_array *array = zend_array_dup(Z_ARRVAL_P(result));
zval *element = (zval*)((uintptr_t)array->arData + opline->op2.num);

zval tmp;
ZVAL_ARR(&tmp, array);

if (IS_BOT(op1)) {
#if 1
/* FIXME: Handle partial arrays. Deleting hashes is problematic
* because it changes the offsets for the placeholders. */
SET_RESULT_BOT(result);
zval_ptr_dtor_nogc(&tmp);
return;
#else
/* Remove unknown index and mark array as partial. */
if (HT_IS_PACKED(array)) {
zval_ptr_dtor(element);
ZVAL_UNDEF(element);
} else {
Bucket *bucket = (Bucket *)((uintptr_t)element - XtOffsetOf(Bucket, val));
if (bucket->key) {
zend_hash_del(array, bucket->key);
} else {
zend_hash_index_del(array, bucket->h);
}
}
MAKE_PARTIAL_ARRAY(&tmp);
#endif
} else {
ZVAL_COPY(element, op1);
if (IS_PARTIAL_ARRAY(op1)) {
MAKE_PARTIAL_ARRAY(&tmp);
}
}


SET_RESULT(result, &tmp);
zval_ptr_dtor_nogc(&tmp);
return;
}
case ZEND_ADD_ARRAY_UNPACK: {
zval *result = &ctx->values[ssa_op->result_use];
if (IS_BOT(result) || IS_BOT(op1)) {
Expand Down Expand Up @@ -2196,7 +2257,9 @@ static int try_remove_definition(sccp_ctx *ctx, int var_num, zend_ssa_var *var,
&& opline->opcode != ZEND_ROPE_ADD
&& opline->opcode != ZEND_INIT_ARRAY
&& opline->opcode != ZEND_ADD_ARRAY_ELEMENT
&& opline->opcode != ZEND_ADD_ARRAY_UNPACK) {
&& opline->opcode != ZEND_ADD_ARRAY_UNPACK
&& opline->opcode != ZEND_ARRAY_DUP
&& opline->opcode != ZEND_ARRAY_SET_PLACEHOLDER) {
/* Replace with QM_ASSIGN */
uint8_t old_type = opline->result_type;
uint32_t old_var = opline->result.var;
Expand Down
12 changes: 12 additions & 0 deletions Zend/Optimizer/zend_dfg.c
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,18 @@ static zend_always_inline void _zend_dfg_add_use_def_op(const zend_op_array *op_
goto add_op1_def;
}
break;
/* FIXME: Verify whether this is correct. */
case ZEND_ARRAY_SET_PLACEHOLDER:
var_num = EX_VAR_TO_NUM(opline->result.var);
if (!zend_bitset_in(def, var_num)) {
zend_bitset_incl(use, var_num);
}
ZEND_FALLTHROUGH;
case ZEND_ARRAY_DUP:
if ((build_flags & ZEND_SSA_RC_INFERENCE) && opline->op1_type == IS_CV) {
goto add_op1_def;
}
break;
case ZEND_YIELD:
if (opline->op1_type == IS_CV
&& ((op_array->fn_flags & ZEND_ACC_RETURN_REFERENCE)
Expand Down
10 changes: 10 additions & 0 deletions Zend/Optimizer/zend_inference.c
Original file line number Diff line number Diff line change
Expand Up @@ -3443,6 +3443,12 @@ static zend_always_inline zend_result _zend_update_type_info(
UPDATE_SSA_TYPE(tmp, ssa_op->result_def);
}
break;
case ZEND_ARRAY_DUP:
case ZEND_ARRAY_SET_PLACEHOLDER:
/* FIXME: This can of course be more specific. However, one challenge
* will be removing filled NULL slots from type inference. */
UPDATE_SSA_TYPE(MAY_BE_RC1|MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY, ssa_op->result_def);
break;
case ZEND_ADD_ARRAY_UNPACK:
tmp = ssa_var_info[ssa_op->result_use].type;
ZEND_ASSERT(tmp & MAY_BE_ARRAY);
Expand Down Expand Up @@ -3662,6 +3668,8 @@ static zend_always_inline zend_result _zend_update_type_info(
case ZEND_YIELD:
case ZEND_INIT_ARRAY:
case ZEND_ADD_ARRAY_ELEMENT:
case ZEND_ARRAY_DUP:
case ZEND_ARRAY_SET_PLACEHOLDER:
case ZEND_RETURN_BY_REF:
case ZEND_VERIFY_RETURN_TYPE:
case ZEND_MAKE_REF:
Expand Down Expand Up @@ -5024,6 +5032,8 @@ ZEND_API bool zend_may_throw_ex(const zend_op *opline, const zend_ssa_op *ssa_op
case ZEND_COPY_TMP:
case ZEND_JMP_NULL:
case ZEND_JMP_FRAMELESS:
case ZEND_ARRAY_DUP:
case ZEND_ARRAY_SET_PLACEHOLDER:
return 0;
case ZEND_IS_IDENTICAL:
case ZEND_IS_NOT_IDENTICAL:
Expand Down
8 changes: 8 additions & 0 deletions Zend/Optimizer/zend_ssa.c
Original file line number Diff line number Diff line change
Expand Up @@ -726,6 +726,14 @@ static zend_always_inline int _zend_ssa_rename_op(const zend_op_array *op_array,
goto add_op1_def;
}
break;
case ZEND_ARRAY_SET_PLACEHOLDER:
ssa_ops[k].result_use = var[EX_VAR_TO_NUM(opline->result.var)];
ZEND_FALLTHROUGH;
case ZEND_ARRAY_DUP:
if ((build_flags & ZEND_SSA_RC_INFERENCE) && opline->op1_type == IS_CV) {
goto add_op1_def;
}
break;
case ZEND_YIELD:
if (opline->op1_type == IS_CV
&& ((op_array->fn_flags & ZEND_ACC_RETURN_REFERENCE)
Expand Down
Loading
Loading