]> granicus.if.org Git - php/commitdiff
JIT for ASSIGN_OBJ_OP
authorDmitry Stogov <dmitry@zend.com>
Mon, 14 Sep 2020 10:31:41 +0000 (13:31 +0300)
committerDmitry Stogov <dmitry@zend.com>
Mon, 14 Sep 2020 10:31:41 +0000 (13:31 +0300)
ext/opcache/jit/zend_jit.c
ext/opcache/jit/zend_jit_disasm_x86.c
ext/opcache/jit/zend_jit_helpers.c
ext/opcache/jit/zend_jit_trace.c
ext/opcache/jit/zend_jit_x86.dasc

index f57744c8de14388e0ddc29ce13e0b5e44bf69942..939a0a1cb4a478919fb6caa210e43001c0eb2d72 100644 (file)
@@ -2457,10 +2457,55 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op
                                                        goto jit_failure;
                                                }
                                                goto done;
-                                       case ZEND_ASSIGN_OBJ:
-                                               if (opline->op1_type == IS_VAR) {
+                                       case ZEND_ASSIGN_OBJ_OP:
+                                               if (opline->extended_value == ZEND_POW
+                                                || opline->extended_value == ZEND_DIV) {
+                                                       // TODO: check for division by zero ???
+                                                       break;
+                                               }
+                                               if (opline->result_type != IS_UNUSED) {
+                                                       break;
+                                               }
+                                               if (opline->op2_type != IS_CONST
+                                                || Z_TYPE_P(RT_CONSTANT(opline, opline->op2)) != IS_STRING
+                                                || Z_STRVAL_P(RT_CONSTANT(opline, opline->op2))[0] == '\0') {
                                                        break;
                                                }
+                                               if (PROFITABILITY_CHECKS && (!ssa->ops || !ssa->var_info)) {
+                                                       break;
+                                               }
+                                               ce = NULL;
+                                               ce_is_instanceof = 0;
+                                               if (opline->op1_type == IS_UNUSED) {
+                                                       op1_info = MAY_BE_OBJECT|MAY_BE_RC1|MAY_BE_RCN;
+                                                       ce = op_array->scope;
+                                                       ce_is_instanceof = (ce->ce_flags & ZEND_ACC_FINAL) != 0;
+                                                       op1_addr = 0;
+                                               } else {
+                                                       op1_info = OP1_INFO();
+                                                       if (!(op1_info & MAY_BE_OBJECT)) {
+                                                               break;
+                                                       }
+                                                       op1_addr = OP1_REG_ADDR();
+                                                       if (ssa->var_info && ssa->ops) {
+                                                               zend_ssa_op *ssa_op = &ssa->ops[opline - op_array->opcodes];
+                                                               if (ssa_op->op1_use >= 0) {
+                                                                       zend_ssa_var_info *op1_ssa = ssa->var_info + ssa_op->op1_use;
+                                                                       if (op1_ssa->ce && !op1_ssa->ce->create_object) {
+                                                                               ce = op1_ssa->ce;
+                                                                               ce_is_instanceof = op1_ssa->is_instanceof;
+                                                                       }
+                                                               }
+                                                       }
+                                               }
+                                               if (!zend_jit_assign_obj_op(&dasm_state, opline, op_array, ssa, ssa_op,
+                                                               op1_info, op1_addr, OP1_DATA_INFO(), OP1_DATA_RANGE(),
+                                                               0, ce, ce_is_instanceof, 0, NULL,
+                                                               zend_may_throw(opline, ssa_op, op_array, ssa))) {
+                                                       goto jit_failure;
+                                               }
+                                               goto done;
+                                       case ZEND_ASSIGN_OBJ:
                                                if (opline->op2_type != IS_CONST
                                                 || Z_TYPE_P(RT_CONSTANT(opline, opline->op2)) != IS_STRING
                                                 || Z_STRVAL_P(RT_CONSTANT(opline, opline->op2))[0] == '\0') {
index ffe290dc4b537bfe86047f1221b95552301517ee..8bae06d21985b3bdfc7ad3a1937d5dc5cab8f316 100644 (file)
@@ -457,6 +457,7 @@ static int zend_jit_disasm_init(void)
        REGISTER_HELPER(zend_jit_invalid_property_read);
        REGISTER_HELPER(zend_jit_invalid_property_write);
        REGISTER_HELPER(zend_jit_invalid_property_assign);
+       REGISTER_HELPER(zend_jit_invalid_property_assign_op);
        REGISTER_HELPER(zend_jit_prepare_assign_dim_ref);
        REGISTER_HELPER(zend_jit_pre_inc);
        REGISTER_HELPER(zend_jit_pre_dec);
@@ -468,7 +469,9 @@ static int zend_jit_disasm_init(void)
        REGISTER_HELPER(zend_jit_zval_array_dup);
        REGISTER_HELPER(zend_jit_add_arrays_helper);
        REGISTER_HELPER(zend_jit_assign_obj_helper);
+       REGISTER_HELPER(zend_jit_assign_obj_op_helper);
        REGISTER_HELPER(zend_jit_assign_to_typed_prop);
+       REGISTER_HELPER(zend_jit_assign_op_to_typed_prop);
 #undef  REGISTER_HELPER
 
 #ifndef _WIN32
index 995b69c57e92980fc3fb7d1eef960307f2d67cc0..c02ac4877912384d107b5309d267cf281f65a807 100644 (file)
@@ -1865,6 +1865,16 @@ static void ZEND_FASTCALL zend_jit_invalid_property_assign(zval *container, cons
                property_name, zend_zval_type_name(container));
 }
 
+static void ZEND_FASTCALL zend_jit_invalid_property_assign_op(zval *container, const char *property_name)
+{
+       if (Z_TYPE_P(container) == IS_UNDEF) {
+               const zend_execute_data *execute_data = EG(current_execute_data);
+
+               zend_jit_undefined_op_helper(EX(opline)->op1.var);
+       }
+       zend_jit_invalid_property_assign(container, property_name);
+}
+
 static zval * ZEND_FASTCALL zend_jit_prepare_assign_dim_ref(zval *ref) {
        zval *val = Z_REFVAL_P(ref);
        if (Z_TYPE_P(val) <= IS_FALSE) {
@@ -1965,3 +1975,88 @@ static void ZEND_FASTCALL zend_jit_assign_to_typed_prop(zval *property_val, zend
                ZVAL_COPY_DEREF(result, value);
        }
 }
+
+static zend_never_inline void _zend_jit_assign_op_overloaded_property(zend_object *object, zend_string *name, void **cache_slot, zval *value, binary_op_type binary_op)
+{
+       zval *z;
+       zval rv, res;
+
+       GC_ADDREF(object);
+       z = object->handlers->read_property(object, name, BP_VAR_R, cache_slot, &rv);
+       if (UNEXPECTED(EG(exception))) {
+               OBJ_RELEASE(object);
+//???          if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+//???                  ZVAL_UNDEF(EX_VAR(opline->result.var));
+//???          }
+               return;
+       }
+       if (binary_op(&res, z, value) == SUCCESS) {
+               object->handlers->write_property(object, name, &res, cache_slot);
+       }
+//???  if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+//???          ZVAL_COPY(EX_VAR(opline->result.var), &res);
+//???  }
+       zval_ptr_dtor(z);
+       zval_ptr_dtor(&res);
+       OBJ_RELEASE(object);
+}
+
+static void ZEND_FASTCALL zend_jit_assign_op_to_typed_prop(zval *zptr, zend_property_info *prop_info, zval *value, binary_op_type binary_op)
+{
+       zend_execute_data *execute_data = EG(current_execute_data);
+       zval z_copy;
+
+       binary_op(&z_copy, zptr, value);
+       if (EXPECTED(zend_verify_property_type(prop_info, &z_copy, EX_USES_STRICT_TYPES()))) {
+               zval_ptr_dtor(zptr);
+               ZVAL_COPY_VALUE(zptr, &z_copy);
+       } else {
+               zval_ptr_dtor(&z_copy);
+       }
+}
+
+static void ZEND_FASTCALL zend_jit_assign_obj_op_helper(zend_object *zobj, zend_string *name, zval *value, void **cache_slot, binary_op_type binary_op)
+{
+       zval *zptr;
+       zend_property_info *prop_info;
+
+       if (EXPECTED((zptr = zobj->handlers->get_property_ptr_ptr(zobj, name, BP_VAR_RW, cache_slot)) != NULL)) {
+               if (UNEXPECTED(Z_ISERROR_P(zptr))) {
+//???                  if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+//???                          ZVAL_NULL(EX_VAR(opline->result.var));
+//???                  }
+               } else {
+//???                  zval *orig_zptr = zptr;
+                       zend_reference *ref;
+
+                       do {
+                               if (UNEXPECTED(Z_ISREF_P(zptr))) {
+                                       ref = Z_REF_P(zptr);
+                                       zptr = Z_REFVAL_P(zptr);
+                                       if (UNEXPECTED(ZEND_REF_HAS_TYPE_SOURCES(ref))) {
+                                               zend_jit_assign_op_to_typed_ref(ref, value, binary_op);
+                                               break;
+                                       }
+                               }
+
+//???                          if (OP2_TYPE == IS_CONST) {
+                               prop_info = (zend_property_info*)CACHED_PTR_EX(cache_slot + 2);
+//???                          } else {
+//???                                  prop_info = zend_object_fetch_property_type_info(Z_OBJ_P(object), orig_zptr);
+//???                          }
+                               if (UNEXPECTED(prop_info)) {
+                                       /* special case for typed properties */
+                                       zend_jit_assign_op_to_typed_prop(zptr, prop_info, value, binary_op);
+                               } else {
+                                       binary_op(zptr, zptr, value);
+                               }
+                       } while (0);
+
+//???                  if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+//???                          ZVAL_COPY(EX_VAR(opline->result.var), zptr);
+//???                  }
+               }
+       } else {
+               _zend_jit_assign_op_overloaded_property(zobj, name, cache_slot, value, binary_op);
+       }
+}
index 30aa0a71fcde1d8ed4376ba3a33a222150f1fe26..3612a60b3ae8ae37e28ed3cde2f5445f7dd0c9ed 100644 (file)
@@ -1422,17 +1422,31 @@ static zend_ssa *zend_jit_trace_build_tssa(zend_jit_trace_rec *trace_buffer, uin
                                        } else if (orig_op1_type != IS_UNKNOWN
                                                && (orig_op1_type & IS_TRACE_INDIRECT)
                                                && opline->result_type == IS_UNUSED) {
-//                                             ADD_OP1_DATA_TRACE_GUARD();
+                                               if (opline->opcode == ZEND_ASSIGN_DIM_OP) {
+                                                       ADD_OP1_DATA_TRACE_GUARD();
+                                               }
                                                ADD_OP2_TRACE_GUARD();
                                        }
                                        break;
+                               case ZEND_ASSIGN_OBJ_OP:
+                                       if (opline->extended_value == ZEND_POW
+                                        || opline->extended_value == ZEND_DIV) {
+                                               // TODO: check for division by zero ???
+                                               break;
+                                       }
+                                       if (opline->result_type != IS_UNUSED) {
+                                               break;
+                                       }
+                                       /* break missing intentionally */
                                case ZEND_ASSIGN_OBJ:
                                        if (opline->op2_type != IS_CONST
                                         || Z_TYPE_P(RT_CONSTANT(opline, opline->op2)) != IS_STRING
                                         || Z_STRVAL_P(RT_CONSTANT(opline, opline->op2))[0] == '\0') {
                                                break;
                                        }
-//                                     ADD_OP1_DATA_TRACE_GUARD();
+                                       if (opline->opcode == ZEND_ASSIGN_OBJ_OP) {
+                                               ADD_OP1_DATA_TRACE_GUARD();
+                                       }
                                        ADD_OP1_TRACE_GUARD();
                                        break;
                                case ZEND_IS_EQUAL:
@@ -3718,6 +3732,80 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par
                                                        goto jit_failure;
                                                }
                                                goto done;
+                                       case ZEND_ASSIGN_OBJ_OP:
+                                               if (opline->extended_value == ZEND_POW
+                                                || opline->extended_value == ZEND_DIV) {
+                                                       // TODO: check for division by zero ???
+                                                       break;
+                                               }
+                                               if (opline->result_type != IS_UNUSED) {
+                                                       break;
+                                               }
+                                               if (opline->op2_type != IS_CONST
+                                                || Z_TYPE_P(RT_CONSTANT(opline, opline->op2)) != IS_STRING
+                                                || Z_STRVAL_P(RT_CONSTANT(opline, opline->op2))[0] == '\0') {
+                                                       break;
+                                               }
+                                               ce = NULL;
+                                               ce_is_instanceof = 0;
+                                               delayed_fetch_this = 0;
+                                               op1_indirect = 0;
+                                               if (opline->op1_type == IS_UNUSED) {
+                                                       op1_info = MAY_BE_OBJECT|MAY_BE_RC1|MAY_BE_RCN;
+                                                       ce = op_array->scope;
+                                                       ce_is_instanceof = (ce->ce_flags & ZEND_ACC_FINAL) != 0;
+                                                       op1_addr = 0;
+                                               } else {
+                                                       if (ssa_op->op1_use >= 0) {
+                                                               delayed_fetch_this = ssa->var_info[ssa_op->op1_use].delayed_fetch_this;
+                                                       }
+                                                       op1_info = OP1_INFO();
+                                                       if (!(op1_info & MAY_BE_OBJECT)) {
+                                                               break;
+                                                       }
+                                                       op1_addr = OP1_REG_ADDR();
+                                                       if (opline->op1_type == IS_VAR) {
+                                                               if (orig_op1_type != IS_UNKNOWN
+                                                                && (orig_op1_type & IS_TRACE_INDIRECT)) {
+                                                                       op1_indirect = 1;
+                                                                       if (!zend_jit_fetch_indirect_var(&dasm_state, opline, orig_op1_type,
+                                                                                       &op1_info, &op1_addr, !ssa->var_info[ssa_op->op1_use].indirect_reference)) {
+                                                                               goto jit_failure;
+                                                                       }
+                                                               }
+                                                       }
+                                                       if (orig_op1_type != IS_UNKNOWN
+                                                        && (orig_op1_type & IS_TRACE_REFERENCE)) {
+                                                               if (!zend_jit_fetch_reference(&dasm_state, opline, orig_op1_type, &op1_info, &op1_addr,
+                                                                               !ssa->var_info[ssa_op->op1_use].guarded_reference, 1)) {
+                                                                       goto jit_failure;
+                                                               }
+                                                               if (opline->op1_type == IS_CV
+                                                                && zend_jit_var_may_alias(op_array, op_array_ssa, EX_VAR_TO_NUM(opline->op1.var)) == NO_ALIAS) {
+                                                                       ssa->var_info[ssa_op->op1_use].guarded_reference = 1;
+                                                               }
+                                                       } else {
+                                                               CHECK_OP1_TRACE_TYPE();
+                                                       }
+                                                       if (ssa->var_info && ssa->ops) {
+                                                               if (ssa_op->op1_use >= 0) {
+                                                                       zend_ssa_var_info *op1_ssa = ssa->var_info + ssa_op->op1_use;
+                                                                       if (op1_ssa->ce && !op1_ssa->ce->create_object) {
+                                                                               ce = op1_ssa->ce;
+                                                                               ce_is_instanceof = op1_ssa->is_instanceof;
+                                                                       }
+                                                               }
+                                                       }
+                                               }
+                                               op1_data_info = OP1_DATA_INFO();
+                                               CHECK_OP1_DATA_TRACE_TYPE();
+                                               if (!zend_jit_assign_obj_op(&dasm_state, opline, op_array, ssa, ssa_op,
+                                                               op1_info, op1_addr, op1_data_info, OP1_DATA_RANGE(),
+                                                               op1_indirect, ce, ce_is_instanceof, delayed_fetch_this, op1_ce,
+                                                               zend_may_throw(opline, ssa_op, op_array, ssa))) {
+                                                       goto jit_failure;
+                                               }
+                                               goto done;
                                        case ZEND_ASSIGN_OBJ:
                                                if (opline->op2_type != IS_CONST
                                                 || Z_TYPE_P(RT_CONSTANT(opline, opline->op2)) != IS_STRING
index 7483a7e9e3ff4f55e4299038d5e3b566d9743730..ee325d5973d0b4fa28ad1f618e2c1a3b23ff8678 100644 (file)
@@ -6451,7 +6451,7 @@ static int zend_jit_assign_dim_op(dasm_State **Dst, const zend_op *opline, uint3
                                                IS_CV, opline->op1, var_addr, var_info, NULL,
                                                (opline+1)->op1_type, (opline+1)->op1, op3_addr, op1_data_info,
                                                op1_data_range,
-                                               0, var_addr, op1_def_info, var_info, may_throw)) {
+                                               0, var_addr, var_def_info, var_info, may_throw)) {
                                        return 0;
                                }
                                break;
@@ -12324,6 +12324,313 @@ static int zend_jit_fetch_obj(dasm_State          **Dst,
        return 1;
 }
 
+static int zend_jit_assign_obj_op(dasm_State          **Dst,
+                                  const zend_op        *opline,
+                                  const zend_op_array  *op_array,
+                                  zend_ssa             *ssa,
+                                  const zend_ssa_op    *ssa_op,
+                                  uint32_t              op1_info,
+                                  zend_jit_addr         op1_addr,
+                                  uint32_t              val_info,
+                                  zend_ssa_range       *val_range,
+                                  zend_bool             op1_indirect,
+                                  zend_class_entry     *ce,
+                                  zend_bool             ce_is_instanceof,
+                                  zend_bool             use_this,
+                                  zend_class_entry     *trace_ce,
+                                  int                   may_throw)
+{
+       zval *member;
+       zend_string *name;
+       zend_property_info *prop_info;
+       zend_jit_addr val_addr = OP1_DATA_ADDR();
+       zend_jit_addr this_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, offsetof(zend_execute_data, This));
+       zend_jit_addr prop_addr;
+       zend_bool needs_slow_path = 0;
+       binary_op_type binary_op = get_binary_op(opline->extended_value);
+
+       ZEND_ASSERT(opline->op2_type == IS_CONST);
+       ZEND_ASSERT(op1_info & MAY_BE_OBJECT);
+       ZEND_ASSERT(opline->result_type == IS_UNUSED);
+
+       member = RT_CONSTANT(opline, opline->op2);
+       ZEND_ASSERT(Z_TYPE_P(member) == IS_STRING && Z_STRVAL_P(member)[0] != '\0');
+       name = Z_STR_P(member);
+       prop_info = zend_get_known_property_info(ce, name, opline->op1_type == IS_UNUSED, op_array->filename);
+
+       if (opline->op1_type == IS_UNUSED || use_this) {
+               |       GET_ZVAL_PTR FCARG1a, this_addr
+       } else {
+               if (opline->op1_type == IS_VAR
+                && (op1_info & MAY_BE_INDIRECT)
+                && Z_REG(op1_addr) == ZREG_FP) {
+                       |       LOAD_ZVAL_ADDR FCARG1a, op1_addr
+                       |       IF_NOT_Z_TYPE FCARG1a, IS_INDIRECT, >1
+                       |       GET_Z_PTR FCARG1a, FCARG1a
+                       |1:
+                       op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1a, 0);
+               }
+               if (op1_info & MAY_BE_REF) {
+                       if (Z_REG(op1_addr) != ZREG_FCARG1a || Z_OFFSET(op1_addr) != 0) {
+                               |       LOAD_ZVAL_ADDR FCARG1a, op1_addr
+                       }
+                       |       ZVAL_DEREF FCARG1a, op1_info
+                       op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1a, 0);
+               }
+               if (op1_info & ((MAY_BE_UNDEF|MAY_BE_ANY)- MAY_BE_OBJECT)) {
+                       if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) {
+                               int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM);
+                               const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
+
+                               if (!exit_addr) {
+                                       return 0;
+                               }
+                               |       IF_NOT_ZVAL_TYPE op1_addr, IS_OBJECT, &exit_addr
+                       } else {
+                               |       IF_NOT_ZVAL_TYPE op1_addr, IS_OBJECT, >1
+                               |.cold_code
+                               |1:
+                               |       SET_EX_OPLINE opline, r0
+                               if (Z_REG(op1_addr) != ZREG_FCARG1a || Z_OFFSET(op1_addr) != 0) {
+                                       |       LOAD_ZVAL_ADDR FCARG1a, op1_addr
+                               }
+                               |       LOAD_ADDR FCARG2a, ZSTR_VAL(name)
+                               if (op1_info & MAY_BE_UNDEF) {
+                                       |       EXT_CALL zend_jit_invalid_property_assign_op, r0
+                               } else {
+                                       |       EXT_CALL zend_jit_invalid_property_assign, r0
+                               }
+                               if (((opline+1)->op1_type & (IS_VAR|IS_TMP_VAR))
+                                && (val_info & (MAY_BE_REF|MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
+                                       |       jmp >8
+                               } else {
+                                       |       jmp ->exception_handler
+                               }
+                               |.code
+                       }
+               }
+               |       GET_ZVAL_PTR FCARG1a, op1_addr
+       }
+
+       if (!prop_info && trace_ce && (trace_ce->ce_flags & ZEND_ACC_IMMUTABLE)) {
+               prop_info = zend_get_known_property_info(trace_ce, name, opline->op1_type == IS_UNUSED, op_array->filename);
+               if (prop_info) {
+                       ce = trace_ce;
+                       ce_is_instanceof = 0;
+                       if (!(op1_info & MAY_BE_CLASS_GUARD)) {
+                               if (!zend_jit_class_guard(Dst, opline, trace_ce)) {
+                                       return 0;
+                               }
+                               if (ssa->var_info && ssa_op->op1_use >= 0) {
+                                       ssa->var_info[ssa_op->op1_use].type |= MAY_BE_CLASS_GUARD;
+                                       ssa->var_info[ssa_op->op1_use].ce = ce;
+                                       ssa->var_info[ssa_op->op1_use].is_instanceof = ce_is_instanceof;
+                               }
+                               if (ssa->var_info && ssa_op->op1_def >= 0) {
+                                       ssa->var_info[ssa_op->op1_def].type |= MAY_BE_CLASS_GUARD;
+                                       ssa->var_info[ssa_op->op1_def].ce = ce;
+                                       ssa->var_info[ssa_op->op1_def].is_instanceof = ce_is_instanceof;
+                               }
+                       }
+               }
+       }
+
+       if (!prop_info) {
+               needs_slow_path = 1;
+
+               |       mov r0, EX->run_time_cache
+               |       mov r2, aword [r0 + (opline+1)->extended_value]
+               |       cmp r2, aword [FCARG1a + offsetof(zend_object, ce)]
+               |       jne >7
+               if (!ce || ce_is_instanceof || (ce->ce_flags & ZEND_ACC_HAS_TYPE_HINTS)) {
+                       |       cmp aword [r0 + ((opline+1)->extended_value & ~ZEND_FETCH_OBJ_FLAGS) + sizeof(void*) * 2], 0
+                       |       jnz >7
+               }
+               |       mov r0, aword [r0 + (opline+1)->extended_value + sizeof(void*)]
+               |       test r0, r0
+               |       jl >7
+               |       IF_TYPE byte [FCARG1a + r0 + 8], IS_UNDEF, >7
+               |       add FCARG1a, r0
+               prop_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1a, 0);
+       } else {
+               prop_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1a, prop_info->offset);
+               if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) {
+                       int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM);
+                       const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
+
+                       if (!exit_addr) {
+                               return 0;
+                       }
+                       |       IF_TYPE byte [FCARG1a + prop_info->offset + 8], IS_UNDEF, &exit_addr
+               } else {
+                       |       IF_TYPE byte [FCARG1a + prop_info->offset + 8], IS_UNDEF, >7
+                       needs_slow_path = 1;
+               }
+               if (ZEND_TYPE_IS_SET(prop_info->type)) {
+                       uint32_t info = val_info;
+
+                       |       // value = zend_assign_to_typed_prop(prop_info, property_val, value EXECUTE_DATA_CC);
+                       |       SET_EX_OPLINE opline, r0
+                       if (ce && ce->ce_flags & ZEND_ACC_IMMUTABLE) {
+                               |       LOAD_ADDR FCARG2a, prop_info
+                       } else {
+                               int prop_info_offset =
+                                       (((prop_info->offset - (sizeof(zend_object) - sizeof(zval))) / sizeof(zval)) * sizeof(void*));
+
+                               |       mov r0, aword [FCARG1a + offsetof(zend_object, ce)]
+                               |       mov     r0, aword [r0 + offsetof(zend_class_entry, properties_info_table)]
+                               |       mov FCARG2a, aword[r0 + prop_info_offset]
+                       }
+                       |       LOAD_ZVAL_ADDR FCARG1a, prop_addr
+                       |.if X64
+                               |       LOAD_ZVAL_ADDR CARG3, val_addr
+                               |       LOAD_ADDR CARG4, binary_op
+                       |.else
+                               |       sub r4, 8
+                               |       PUSH_ADDR binary_op, r0
+                               |       PUSH_ZVAL_ADDR val_addr, r0
+                       |.endif
+
+                       |       EXT_CALL zend_jit_assign_op_to_typed_prop, r0
+
+                       |.if not(X64)
+                               |       add r4, 8
+                       |.endif
+
+                       if (info & (MAY_BE_REF|MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) {
+                               info |= MAY_BE_RC1|MAY_BE_RCN;
+                       }
+
+                       |       FREE_OP (opline+1)->op1_type, (opline+1)->op1, info, 0, opline
+               }
+       }
+
+       if (!prop_info || !ZEND_TYPE_IS_SET(prop_info->type)) {
+               zend_jit_addr var_addr = prop_addr;
+               uint32_t var_info = MAY_BE_ANY|MAY_BE_REF|MAY_BE_RC1|MAY_BE_RCN;
+               uint32_t var_def_info = MAY_BE_ANY|MAY_BE_REF|MAY_BE_RC1|MAY_BE_RCN;
+
+               var_addr = ZEND_ADDR_MEM_ZVAL(ZREG_R0, 0);
+               |       LOAD_ZVAL_ADDR r0, prop_addr
+
+               |       IF_NOT_ZVAL_TYPE var_addr, IS_REFERENCE, >2
+               |       GET_ZVAL_PTR FCARG1a, var_addr
+               |       cmp aword [FCARG1a + offsetof(zend_reference, sources.ptr)], 0
+               |       jnz >1
+               |       lea r0, aword [FCARG1a + offsetof(zend_reference, val)]
+               |.cold_code
+               |1:
+               if (Z_MODE(val_addr) != IS_MEM_ZVAL || Z_REG(val_addr) != ZREG_FCARG2a || Z_OFFSET(val_addr) != 0) {
+                       |       LOAD_ZVAL_ADDR FCARG2a, val_addr
+               }
+               if (opline) {
+                       |       SET_EX_OPLINE opline, r0
+               }
+               |.if X64
+                       |       LOAD_ADDR CARG3, binary_op
+               |.else
+                       |       sub r4, 12
+                       |       PUSH_ADDR binary_op, r0
+               |.endif
+               |       EXT_CALL zend_jit_assign_op_to_typed_ref, r0
+               |.if not(X64)
+                       |       add r4, 12
+               |.endif
+               |       jmp >9
+               |.code
+               |2:
+
+               switch (opline->extended_value) {
+                       case ZEND_ADD:
+                       case ZEND_SUB:
+                       case ZEND_MUL:
+                       case ZEND_DIV:
+                               if (!zend_jit_math_helper(Dst, opline, opline->extended_value, IS_CV, opline->op1, var_addr, var_info, (opline+1)->op1_type, (opline+1)->op1, val_addr, val_info, 0, var_addr, var_def_info, var_info,
+                                               1 /* may overflow */, 0)) {
+                                       return 0;
+                               }
+                               break;
+                       case ZEND_BW_OR:
+                       case ZEND_BW_AND:
+                       case ZEND_BW_XOR:
+                       case ZEND_SL:
+                       case ZEND_SR:
+                       case ZEND_MOD:
+                               if (!zend_jit_long_math_helper(Dst, opline, opline->extended_value,
+                                               IS_CV, opline->op1, var_addr, var_info, NULL,
+                                               (opline+1)->op1_type, (opline+1)->op1, val_addr, val_info,
+                                               val_range,
+                                               0, var_addr, var_def_info, var_info, 0)) {
+                                       return 0;
+                               }
+                               break;
+                       case ZEND_CONCAT:
+                               if (!zend_jit_concat_helper(Dst, opline, IS_CV, opline->op1, var_addr, var_info, (opline+1)->op1_type, (opline+1)->op1, val_addr, val_info, var_addr,
+                                               0)) {
+                                       return 0;
+                               }
+                               break;
+                       default:
+                               ZEND_UNREACHABLE();
+               }
+       }
+
+       if (needs_slow_path) {
+               |.cold_code
+               |7:
+               |       SET_EX_OPLINE opline, r0
+               |       // value = zobj->handlers->write_property(zobj, name, value, CACHE_ADDR(opline->extended_value));
+               |       LOAD_ADDR FCARG2a, name
+               |.if X64
+                       |       LOAD_ZVAL_ADDR CARG3, val_addr
+                       |       mov CARG4, EX->run_time_cache
+                       |       add CARG4, (opline+1)->extended_value
+                       |.if X64WIN
+                       |       LOAD_ADDR r0, binary_op
+                       |       mov aword A5, r0
+                       |.else
+                       |       LOAD_ADDR CARG5, binary_op
+                       |.endif
+               |.else
+                       |       sub r4, 4
+                       |       PUSH_ADDR binary_op, r0
+                       |       mov r0, EX->run_time_cache
+                       |       add r0, (opline+1)->extended_value
+                       |       push r0
+                       |       PUSH_ZVAL_ADDR val_addr, r0
+               |.endif
+
+               |       EXT_CALL zend_jit_assign_obj_op_helper, r0
+
+               |.if not(X64)
+                       |       add r4, 4
+               |.endif
+
+               if (val_info & (MAY_BE_REF|MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) {
+                       val_info |= MAY_BE_RC1|MAY_BE_RCN;
+               }
+
+               |8:
+               |       // FREE_OP_DATA();
+               |       FREE_OP (opline+1)->op1_type, (opline+1)->op1, val_info, 0, opline
+               |       jmp >9
+               |.code
+       }
+
+       |9:
+       if (opline->op1_type != IS_UNUSED && !use_this && !op1_indirect) {
+               |       FREE_OP opline->op1_type, opline->op1, op1_info, 1, opline
+       }
+
+       if (may_throw) {
+               if (!zend_jit_check_exception(Dst)) {
+                       return 0;
+               }
+       }
+
+       return 1;
+}
+
 static int zend_jit_assign_obj(dasm_State          **Dst,
                                const zend_op        *opline,
                                const zend_op_array  *op_array,