Skip to content

Commit e07fdd4

Browse files
committed
Implement ARRAY_DUP and ARRAY_SET_PLACEHOLDER instructions
1 parent 8b68274 commit e07fdd4

File tree

15 files changed

+996
-496
lines changed

15 files changed

+996
-496
lines changed

Zend/Optimizer/block_pass.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -911,6 +911,7 @@ static void zend_optimize_block(zend_basic_block *block, zend_op_array *op_array
911911
src->opcode != ZEND_COPY_TMP &&
912912
src->opcode != ZEND_ADD_ARRAY_ELEMENT &&
913913
src->opcode != ZEND_ADD_ARRAY_UNPACK &&
914+
src->opcode != ZEND_ARRAY_SET_PLACEHOLDER &&
914915
(src->opcode != ZEND_DECLARE_LAMBDA_FUNCTION ||
915916
src == opline -1)) {
916917
src->result.var = opline->result.var;
@@ -1474,6 +1475,7 @@ static void zend_t_usage(zend_cfg *cfg, zend_op_array *op_array, zend_bitset use
14741475
switch (opline->opcode) {
14751476
case ZEND_ADD_ARRAY_ELEMENT:
14761477
case ZEND_ADD_ARRAY_UNPACK:
1478+
case ZEND_ARRAY_SET_PLACEHOLDER:
14771479
case ZEND_ROPE_ADD:
14781480
/* these opcodes use the result as argument */
14791481
if (!zend_bitset_in(defined_here, var_num)) {
@@ -1568,6 +1570,7 @@ static void zend_t_usage(zend_cfg *cfg, zend_op_array *op_array, zend_bitset use
15681570
break;
15691571
case ZEND_ADD_ARRAY_ELEMENT:
15701572
case ZEND_ADD_ARRAY_UNPACK:
1573+
case ZEND_ARRAY_SET_PLACEHOLDER:
15711574
case ZEND_ROPE_ADD:
15721575
zend_bitset_incl(usage, VAR_NUM(opline->result.var));
15731576
break;
@@ -1576,6 +1579,7 @@ static void zend_t_usage(zend_cfg *cfg, zend_op_array *op_array, zend_bitset use
15761579
switch (opline->opcode) {
15771580
case ZEND_ADD_ARRAY_ELEMENT:
15781581
case ZEND_ADD_ARRAY_UNPACK:
1582+
case ZEND_ARRAY_SET_PLACEHOLDER:
15791583
case ZEND_ROPE_ADD:
15801584
break;
15811585
default:

Zend/Optimizer/dce.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,8 @@ static inline bool may_have_side_effects(
111111
case ZEND_ROPE_INIT:
112112
case ZEND_ROPE_ADD:
113113
case ZEND_INIT_ARRAY:
114+
case ZEND_ARRAY_DUP:
115+
case ZEND_ARRAY_SET_PLACEHOLDER:
114116
case ZEND_SPACESHIP:
115117
case ZEND_STRLEN:
116118
case ZEND_COUNT:

Zend/Optimizer/dfa_pass.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -359,6 +359,12 @@ static bool opline_supports_assign_contraction(
359359
&& (opline->op2_type != IS_CV || opline->op2.var != cv_var);
360360
}
361361

362+
if (opline->opcode == ZEND_ARRAY_DUP) {
363+
/* FIXME: Is this ok? Array initialization might be incomplete if value
364+
* evaluation fails, but this is no different from ZEND_INIT_ARRAY. */
365+
return false;
366+
}
367+
362368
if (opline->opcode == ZEND_CAST
363369
&& (opline->extended_value == IS_ARRAY || opline->extended_value == IS_OBJECT)) {
364370
/* CAST to array/object may initialize the result to an empty array/object before

Zend/Optimizer/escape_analysis.c

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,7 @@ static bool is_allocation_def(zend_op_array *op_array, zend_ssa *ssa, int def, i
155155
if (ssa_op->result_def == var) {
156156
switch (opline->opcode) {
157157
case ZEND_INIT_ARRAY:
158+
case ZEND_ARRAY_DUP:
158159
return 1;
159160
case ZEND_NEW: {
160161
/* objects with destructors should escape */
@@ -227,6 +228,8 @@ static bool is_local_def(zend_op_array *op_array, zend_ssa *ssa, int def, int va
227228
switch (opline->opcode) {
228229
case ZEND_INIT_ARRAY:
229230
case ZEND_ADD_ARRAY_ELEMENT:
231+
case ZEND_ARRAY_DUP:
232+
case ZEND_ARRAY_SET_PLACEHOLDER:
230233
case ZEND_QM_ASSIGN:
231234
case ZEND_ASSIGN:
232235
return 1;
@@ -312,6 +315,8 @@ static bool is_escape_use(zend_op_array *op_array, zend_ssa *ssa, int use, int v
312315
if (opline->extended_value & ZEND_ARRAY_ELEMENT_REF) {
313316
return 1;
314317
}
318+
ZEND_FALLTHROUGH;
319+
case ZEND_ARRAY_SET_PLACEHOLDER:
315320
if (OP1_INFO() & MAY_BE_OBJECT) {
316321
/* object aliasing */
317322
return 1;
@@ -369,6 +374,8 @@ static bool is_escape_use(zend_op_array *op_array, zend_ssa *ssa, int use, int v
369374
case ZEND_QM_ASSIGN:
370375
case ZEND_INIT_ARRAY:
371376
case ZEND_ADD_ARRAY_ELEMENT:
377+
case ZEND_ARRAY_DUP:
378+
case ZEND_ARRAY_SET_PLACEHOLDER:
372379
break;
373380
default:
374381
return 1;
@@ -488,7 +495,8 @@ zend_result zend_ssa_escape_analysis(const zend_script *script, zend_op_array *o
488495
(op-1)->op1_use >= 0) {
489496
enclosing_root = ees[(op-1)->op1_use];
490497
} else if ((opline->opcode == ZEND_INIT_ARRAY ||
491-
opline->opcode == ZEND_ADD_ARRAY_ELEMENT) &&
498+
opline->opcode == ZEND_ADD_ARRAY_ELEMENT ||
499+
opline->opcode == ZEND_ARRAY_SET_PLACEHOLDER) &&
492500
op->op1_use == i &&
493501
op->result_def >= 0) {
494502
enclosing_root = ees[op->result_def];

Zend/Optimizer/sccp.c

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1182,6 +1182,67 @@ static void sccp_visit_instr(scdf_ctx *scdf, zend_op *opline, zend_ssa_op *ssa_o
11821182
}
11831183
return;
11841184
}
1185+
case ZEND_ARRAY_DUP: {
1186+
SET_RESULT(result, op1);
1187+
return;
1188+
}
1189+
case ZEND_ARRAY_SET_PLACEHOLDER: {
1190+
zval *result = &ctx->values[ssa_op->result_use];
1191+
1192+
SKIP_IF_TOP(result);
1193+
SKIP_IF_TOP(op1);
1194+
1195+
#if 1
1196+
/* FIXME: See below. */
1197+
if (IS_BOT(result)) {
1198+
SET_RESULT_BOT(result);
1199+
return;
1200+
}
1201+
#else
1202+
ZEND_ASSERT(!IS_BOT(result));
1203+
#endif
1204+
1205+
/* FIXME: Avoid dup-ing the array in every step. */
1206+
zend_array *array = zend_array_dup(Z_ARRVAL_P(result));
1207+
zval *element = (zval*)((uintptr_t)array->arData + opline->op2.num);
1208+
1209+
zval tmp;
1210+
ZVAL_ARR(&tmp, array);
1211+
1212+
if (IS_BOT(op1)) {
1213+
#if 1
1214+
/* FIXME: Handle partial arrays. Deleting hashes is problematic
1215+
* because it changes the offsets for the placeholders. */
1216+
SET_RESULT_BOT(result);
1217+
zval_ptr_dtor_nogc(&tmp);
1218+
return;
1219+
#else
1220+
/* Remove unknown index and mark array as partial. */
1221+
if (HT_IS_PACKED(array)) {
1222+
zval_ptr_dtor(element);
1223+
ZVAL_UNDEF(element);
1224+
} else {
1225+
Bucket *bucket = (Bucket *)((uintptr_t)element - XtOffsetOf(Bucket, val));
1226+
if (bucket->key) {
1227+
zend_hash_del(array, bucket->key);
1228+
} else {
1229+
zend_hash_index_del(array, bucket->h);
1230+
}
1231+
}
1232+
MAKE_PARTIAL_ARRAY(&tmp);
1233+
#endif
1234+
} else {
1235+
ZVAL_COPY(element, op1);
1236+
if (IS_PARTIAL_ARRAY(op1)) {
1237+
MAKE_PARTIAL_ARRAY(&tmp);
1238+
}
1239+
}
1240+
1241+
1242+
SET_RESULT(result, &tmp);
1243+
zval_ptr_dtor_nogc(&tmp);
1244+
return;
1245+
}
11851246
case ZEND_ADD_ARRAY_UNPACK: {
11861247
zval *result = &ctx->values[ssa_op->result_use];
11871248
if (IS_BOT(result) || IS_BOT(op1)) {
@@ -2196,7 +2257,9 @@ static int try_remove_definition(sccp_ctx *ctx, int var_num, zend_ssa_var *var,
21962257
&& opline->opcode != ZEND_ROPE_ADD
21972258
&& opline->opcode != ZEND_INIT_ARRAY
21982259
&& opline->opcode != ZEND_ADD_ARRAY_ELEMENT
2199-
&& opline->opcode != ZEND_ADD_ARRAY_UNPACK) {
2260+
&& opline->opcode != ZEND_ADD_ARRAY_UNPACK
2261+
&& opline->opcode != ZEND_ARRAY_DUP
2262+
&& opline->opcode != ZEND_ARRAY_SET_PLACEHOLDER) {
22002263
/* Replace with QM_ASSIGN */
22012264
uint8_t old_type = opline->result_type;
22022265
uint32_t old_var = opline->result.var;

Zend/Optimizer/zend_dfg.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,18 @@ static zend_always_inline void _zend_dfg_add_use_def_op(const zend_op_array *op_
204204
goto add_op1_def;
205205
}
206206
break;
207+
/* FIXME: Verify whether this is correct. */
208+
case ZEND_ARRAY_SET_PLACEHOLDER:
209+
var_num = EX_VAR_TO_NUM(opline->result.var);
210+
if (!zend_bitset_in(def, var_num)) {
211+
zend_bitset_incl(use, var_num);
212+
}
213+
ZEND_FALLTHROUGH;
214+
case ZEND_ARRAY_DUP:
215+
if ((build_flags & ZEND_SSA_RC_INFERENCE) && opline->op1_type == IS_CV) {
216+
goto add_op1_def;
217+
}
218+
break;
207219
case ZEND_YIELD:
208220
if (opline->op1_type == IS_CV
209221
&& ((op_array->fn_flags & ZEND_ACC_RETURN_REFERENCE)

Zend/Optimizer/zend_inference.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3443,6 +3443,12 @@ static zend_always_inline zend_result _zend_update_type_info(
34433443
UPDATE_SSA_TYPE(tmp, ssa_op->result_def);
34443444
}
34453445
break;
3446+
case ZEND_ARRAY_DUP:
3447+
case ZEND_ARRAY_SET_PLACEHOLDER:
3448+
/* FIXME: This can of course be more specific. However, one challenge
3449+
* will be removing filled NULL slots from type inference. */
3450+
UPDATE_SSA_TYPE(MAY_BE_RC1|MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY, ssa_op->result_def);
3451+
break;
34463452
case ZEND_ADD_ARRAY_UNPACK:
34473453
tmp = ssa_var_info[ssa_op->result_use].type;
34483454
ZEND_ASSERT(tmp & MAY_BE_ARRAY);
@@ -3662,6 +3668,8 @@ static zend_always_inline zend_result _zend_update_type_info(
36623668
case ZEND_YIELD:
36633669
case ZEND_INIT_ARRAY:
36643670
case ZEND_ADD_ARRAY_ELEMENT:
3671+
case ZEND_ARRAY_DUP:
3672+
case ZEND_ARRAY_SET_PLACEHOLDER:
36653673
case ZEND_RETURN_BY_REF:
36663674
case ZEND_VERIFY_RETURN_TYPE:
36673675
case ZEND_MAKE_REF:
@@ -5024,6 +5032,8 @@ ZEND_API bool zend_may_throw_ex(const zend_op *opline, const zend_ssa_op *ssa_op
50245032
case ZEND_COPY_TMP:
50255033
case ZEND_JMP_NULL:
50265034
case ZEND_JMP_FRAMELESS:
5035+
case ZEND_ARRAY_DUP:
5036+
case ZEND_ARRAY_SET_PLACEHOLDER:
50275037
return 0;
50285038
case ZEND_IS_IDENTICAL:
50295039
case ZEND_IS_NOT_IDENTICAL:

Zend/Optimizer/zend_ssa.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -726,6 +726,14 @@ static zend_always_inline int _zend_ssa_rename_op(const zend_op_array *op_array,
726726
goto add_op1_def;
727727
}
728728
break;
729+
case ZEND_ARRAY_SET_PLACEHOLDER:
730+
ssa_ops[k].result_use = var[EX_VAR_TO_NUM(opline->result.var)];
731+
ZEND_FALLTHROUGH;
732+
case ZEND_ARRAY_DUP:
733+
if ((build_flags & ZEND_SSA_RC_INFERENCE) && opline->op1_type == IS_CV) {
734+
goto add_op1_def;
735+
}
736+
break;
729737
case ZEND_YIELD:
730738
if (opline->op1_type == IS_CV
731739
&& ((op_array->fn_flags & ZEND_ACC_RETURN_REFERENCE)

0 commit comments

Comments
 (0)