Skip to content

Commit 8459665

Browse files
committed
Attempt to optimize typed property assign
1 parent 0b3c506 commit 8459665

File tree

5 files changed

+1550
-625
lines changed

5 files changed

+1550
-625
lines changed

Zend/Optimizer/dfa_pass.c

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1700,6 +1700,35 @@ void zend_dfa_optimize_op_array(zend_op_array *op_array, zend_optimizer_ctx *ctx
17001700
}
17011701
}
17021702

1703+
for (uint32_t i = 0; i < op_array->last; i++) {
1704+
zend_op *opline = &op_array->opcodes[i];
1705+
if (opline->opcode == ZEND_ASSIGN_OBJ && opline->op2_type == IS_CONST) {
1706+
const zend_property_info *zend_fetch_prop_info(const zend_op_array *op_array, zend_ssa *ssa, const zend_op *opline, const zend_ssa_op *ssa_op);
1707+
1708+
zend_ssa_op *ssa_op = &ssa->ops[i];
1709+
1710+
//if (opline->op1_type != IS_UNUSED && (ssa->var_info[ssa_op->op1_use].type & (MAY_BE_ANY | MAY_BE_REF)) != MAY_BE_OBJECT) {
1711+
// continue;
1712+
//}
1713+
1714+
uint32_t value_type_mask = ssa->var_info[(ssa_op + 1)->op1_use].type;
1715+
value_type_mask &= MAY_BE_ANY | MAY_BE_REF;
1716+
//fprintf(stderr, "found assign obj (type %x)\n", value_type_mask);
1717+
const zend_property_info *prop_info = zend_fetch_prop_info(op_array, ssa, opline, ssa_op);
1718+
if (prop_info
1719+
&& ZEND_TYPE_IS_ONLY_MASK(prop_info->type)
1720+
&& IS_VALID_PROPERTY_OFFSET(prop_info->offset)
1721+
&& !(prop_info->flags & ZEND_ACC_READONLY /* TODO: other flags? */)
1722+
&& ZEND_TYPE_PURE_MASK(prop_info->type) == value_type_mask
1723+
&& ((value_type_mask - 1) & value_type_mask) == 0) {
1724+
if (ZEND_TYPE_PURE_MASK(prop_info->type) == value_type_mask && ((value_type_mask-1)&value_type_mask)==0) {
1725+
// TODO: __set & friends
1726+
opline->extended_value |= 1;
1727+
}
1728+
}
1729+
}
1730+
}
1731+
17031732
if (ctx->debug_level & ZEND_DUMP_AFTER_DFA_PASS) {
17041733
zend_dump_op_array(op_array, ZEND_DUMP_SSA, "after dfa pass", ssa);
17051734
}

Zend/Optimizer/zend_inference.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2426,7 +2426,7 @@ static const zend_property_info *lookup_prop_info(const zend_class_entry *ce, ze
24262426
return NULL;
24272427
}
24282428

2429-
static const zend_property_info *zend_fetch_prop_info(const zend_op_array *op_array, zend_ssa *ssa, const zend_op *opline, const zend_ssa_op *ssa_op)
2429+
const zend_property_info *zend_fetch_prop_info(const zend_op_array *op_array, zend_ssa *ssa, const zend_op *opline, const zend_ssa_op *ssa_op)
24302430
{
24312431
const zend_property_info *prop_info = NULL;
24322432
if (opline->op2_type == IS_CONST) {

Zend/zend_vm_def.h

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2515,6 +2515,67 @@ ZEND_VM_C_LABEL(exit_assign_obj):
25152515
ZEND_VM_NEXT_OPCODE_EX(1, 2);
25162516
}
25172517

2518+
ZEND_VM_TYPE_SPEC_HANDLER(ZEND_ASSIGN_OBJ, (op->extended_value & 1) != 0, ZEND_ASSIGN_OBJ_CORRECTLY_TYPED, UNUSED|VAR|THIS|CV, CONST, CACHE_SLOT, SPEC(OP_DATA=CONST|TMP|VAR|CV))
2519+
{
2520+
USE_OPLINE
2521+
zval *object, *value;
2522+
zend_object *zobj;
2523+
zend_string *name;
2524+
zend_refcounted *garbage = NULL;
2525+
2526+
SAVE_OPLINE();
2527+
object = GET_OP1_OBJ_ZVAL_PTR_PTR_UNDEF(BP_VAR_W);
2528+
value = GET_OP_DATA_ZVAL_PTR(BP_VAR_R);
2529+
2530+
// TODO: get rid of this?
2531+
if (OP1_TYPE != IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) {
2532+
if (Z_ISREF_P(object) && Z_TYPE_P(Z_REFVAL_P(object)) == IS_OBJECT) {
2533+
object = Z_REFVAL_P(object);
2534+
ZEND_VM_C_GOTO(assign_object);
2535+
}
2536+
zend_throw_non_object_error(object, GET_OP2_ZVAL_PTR(BP_VAR_R) OPLINE_CC EXECUTE_DATA_CC);
2537+
value = &EG(uninitialized_zval);
2538+
ZEND_VM_C_GOTO(free_and_exit_assign_obj);
2539+
}
2540+
2541+
ZEND_VM_C_LABEL(assign_object):
2542+
zobj = Z_OBJ_P(object);
2543+
if (EXPECTED(zobj->ce == CACHED_PTR(opline->extended_value & ~1))) {
2544+
void **cache_slot = CACHE_ADDR(opline->extended_value & ~1);
2545+
uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1);
2546+
zval *property_val = OBJ_PROP(zobj, prop_offset);
2547+
2548+
ZEND_ASSERT(IS_VALID_PROPERTY_OFFSET(prop_offset));
2549+
2550+
if (Z_TYPE_P(property_val) != IS_UNDEF) {
2551+
value = zend_assign_to_variable_ex(property_val, value, OP_DATA_TYPE, EX_USES_STRICT_TYPES(), &garbage);
2552+
if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
2553+
ZVAL_COPY(EX_VAR(opline->result.var), value);
2554+
}
2555+
ZEND_VM_C_GOTO(exit_assign_obj);
2556+
}
2557+
}
2558+
name = Z_STR_P(GET_OP2_ZVAL_PTR(BP_VAR_R));
2559+
2560+
ZEND_ASSERT(!Z_ISREF_P(value));
2561+
2562+
value = zobj->handlers->write_property(zobj, name, value, (OP2_TYPE == IS_CONST) ? CACHE_ADDR(opline->extended_value & ~1) : NULL);
2563+
2564+
ZEND_VM_C_LABEL(free_and_exit_assign_obj):
2565+
if (UNEXPECTED(RETURN_VALUE_USED(opline)) && value) {
2566+
ZVAL_COPY_DEREF(EX_VAR(opline->result.var), value);
2567+
}
2568+
FREE_OP_DATA();
2569+
ZEND_VM_C_LABEL(exit_assign_obj):
2570+
if (garbage) {
2571+
GC_DTOR_NO_REF(garbage);
2572+
}
2573+
FREE_OP2();
2574+
FREE_OP1();
2575+
/* assign_obj has two opcodes! */
2576+
ZEND_VM_NEXT_OPCODE_EX(1, 2);
2577+
}
2578+
25182579
/* No specialization for op_types (CONST|TMPVAR|CV, UNUSED|CONST|VAR) */
25192580
ZEND_VM_HANDLER(25, ZEND_ASSIGN_STATIC_PROP, ANY, ANY, CACHE_SLOT, SPEC(OP_DATA=CONST|TMP|VAR|CV))
25202581
{

0 commit comments

Comments
 (0)