ADD_OP2_TRACE_GUARD();
}
break;
+ 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();
+ ADD_OP1_TRACE_GUARD();
+ break;
case ZEND_IS_EQUAL:
case ZEND_IS_NOT_EQUAL:
case ZEND_IS_SMALLER:
}
} else if (opline->opcode != ZEND_FETCH_OBJ_R
&& opline->opcode != ZEND_FETCH_OBJ_IS
- && opline->opcode != ZEND_FETCH_OBJ_W) {
+ && opline->opcode != ZEND_FETCH_OBJ_W
+ && opline->opcode != ZEND_ASSIGN_OBJ) {
return 0;
}
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') {
+ 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(&dasm_state, opline, op_array, ssa, ssa_op,
+ op1_info, op1_addr, op1_data_info,
+ 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_DIM:
op1_info = OP1_INFO();
op1_addr = OP1_REG_ADDR();
return 1;
}
+static int zend_jit_assign_to_variable_call(dasm_State **Dst,
+ const zend_op *opline,
+ zend_jit_addr __var_use_addr,
+ zend_jit_addr var_addr,
+ uint32_t __var_info,
+ uint32_t __var_def_info,
+ zend_uchar val_type,
+ zend_jit_addr val_addr,
+ uint32_t val_info,
+ zend_jit_addr __res_addr,
+ zend_bool __check_exception)
+{
+ if (Z_MODE(var_addr) != IS_MEM_ZVAL || Z_REG(var_addr) != ZREG_FCARG1a || Z_OFFSET(var_addr) != 0) {
+ | LOAD_ZVAL_ADDR FCARG1a, var_addr
+ }
+ 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 (!(val_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) {
+ | call ->assign_tmp
+ } else if (val_type == IS_CONST) {
+ | call ->assign_const
+ } else if (val_type == IS_TMP_VAR) {
+ | call ->assign_tmp
+ } else if (val_type == IS_VAR) {
+ if (!(val_info & MAY_BE_REF)) {
+ | call ->assign_tmp
+ } else {
+ | call ->assign_var
+ }
+ } else if (val_type == IS_CV) {
+ if (!(val_info & MAY_BE_REF)) {
+ | call ->assign_cv_noref
+ } else {
+ | call ->assign_cv
+ }
+ } else {
+ ZEND_UNREACHABLE();
+ }
+
+ return 1;
+}
+
static int zend_jit_assign_to_variable(dasm_State **Dst,
const zend_op *opline,
zend_jit_addr var_use_addr,
| // value = zend_assign_to_variable(variable_ptr, value, OP_DATA_TYPE);
if (opline->op1_type == IS_VAR) {
ZEND_ASSERT(opline->result_type == IS_UNUSED);
- if (Z_MODE(var_addr) != IS_MEM_ZVAL || Z_REG(var_addr) != ZREG_FCARG1a || Z_OFFSET(var_addr) != 0) {
- | LOAD_ZVAL_ADDR FCARG1a, var_addr
- }
- if (Z_MODE(op3_addr) != IS_MEM_ZVAL || Z_REG(op3_addr) != ZREG_FCARG2a || Z_OFFSET(op3_addr) != 0) {
- | LOAD_ZVAL_ADDR FCARG2a, op3_addr
+ if (!zend_jit_assign_to_variable_call(Dst, opline, var_addr, var_addr, var_info, -1, (opline+1)->op1_type, op3_addr, val_info, res_addr, 0)) {
+ return 0;
}
- | SET_EX_OPLINE opline, r0
- if (!(val_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) {
- | call ->assign_tmp
- } else if ((opline+1)->op1_type == IS_CONST) {
- | call ->assign_const
- } else if ((opline+1)->op1_type == IS_TMP_VAR) {
- | call ->assign_tmp
- } else if ((opline+1)->op1_type == IS_VAR) {
- if (!(val_info & MAY_BE_REF)) {
- | call ->assign_tmp
- } else {
- | call ->assign_var
- }
- } else if ((opline+1)->op1_type == IS_CV) {
- if (!(val_info & MAY_BE_REF)) {
- | call ->assign_cv_noref
- } else {
- | call ->assign_cv
- }
- } else {
- ZEND_UNREACHABLE();
+ } else {
+ if (!zend_jit_assign_to_variable(Dst, opline, var_addr, var_addr, var_info, -1, (opline+1)->op1_type, op3_addr, val_info, res_addr, 0)) {
+ return 0;
}
- } else if (!zend_jit_assign_to_variable(Dst, opline, var_addr, var_addr, var_info, -1, (opline+1)->op1_type, op3_addr, val_info, res_addr, 0)) {
- return 0;
}
}
}
return 1;
}
+static int zend_jit_assign_obj(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_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 res_addr = 0;
+ zend_jit_addr this_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, offsetof(zend_execute_data, This));
+ zend_jit_addr prop_addr;
+
+ if (RETURN_VALUE_USED(opline)) {
+ res_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->result.var);
+ }
+
+ ZEND_ASSERT(opline->op2_type == IS_CONST);
+ ZEND_ASSERT(op1_info & MAY_BE_OBJECT);
+
+ 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)
+ | EXT_CALL zend_jit_invalid_property_assign, r0
+ if (RETURN_VALUE_USED(opline)) {
+ | SET_ZVAL_TYPE_INFO res_addr, IS_NULL
+ }
+ 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;
+ }
+ }
+ }
+ }
+
+ if (!prop_info) {
+ | mov r0, EX->run_time_cache
+ | mov r2, aword [r0 + opline->extended_value]
+ | cmp r2, aword [FCARG1a + offsetof(zend_object, ce)]
+ | jne >5
+ if (!ce || ce_is_instanceof || (ce->ce_flags & ZEND_ACC_HAS_TYPE_HINTS)) {
+ | mov FCARG2a, aword [r0 + (opline->extended_value & ~ZEND_FETCH_OBJ_FLAGS) + sizeof(void*) * 2]
+ }
+ | mov r0, aword [r0 + opline->extended_value + sizeof(void*)]
+ | test r0, r0
+ | jl >5
+ | IF_TYPE byte [FCARG1a + r0 + 8], IS_UNDEF, >5
+ | add FCARG1a, r0
+ prop_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1a, 0);
+ if (!ce || ce_is_instanceof || (ce->ce_flags & ZEND_ACC_HAS_TYPE_HINTS)) {
+ | test FCARG2a, FCARG2a
+ | jnz >1
+ |.cold_code
+ |1:
+ | // value = zend_assign_to_typed_prop(prop_info, property_val, value EXECUTE_DATA_CC);
+ | SET_EX_OPLINE opline, r0
+ |.if X64
+ | LOAD_ZVAL_ADDR CARG3, val_addr
+ if (RETURN_VALUE_USED(opline)) {
+ | LOAD_ZVAL_ADDR CARG4, res_addr
+ } else {
+ | xor CARG4, CARG4
+ }
+ |.else
+ | sub r4, 8
+ if (RETURN_VALUE_USED(opline)) {
+ | PUSH_ZVAL_ADDR res_addr, r0
+ } else {
+ | push 0
+ }
+ | PUSH_ZVAL_ADDR val_addr, r0
+ |.endif
+
+ | EXT_CALL zend_jit_assign_to_typed_prop, r0
+
+ |.if not(X64)
+ | add r4, 8
+ |.endif
+
+ if ((opline+1)->op1_type == IS_CONST) {
+ | // TODO: ???
+ | // if (Z_TYPE_P(value) == orig_type) {
+ | // CACHE_PTR_EX(cache_slot + 2, NULL);
+ }
+
+ 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 >9
+ }
+ |.code
+ }
+ } else {
+ prop_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1a, prop_info->offset);
+ // Undefined property with magic __get()/__set()
+ 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, >5
+ }
+ 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
+ if (RETURN_VALUE_USED(opline)) {
+ | LOAD_ZVAL_ADDR CARG4, res_addr
+ } else {
+ | xor CARG4, CARG4
+ }
+ |.else
+ | sub r4, 8
+ if (RETURN_VALUE_USED(opline)) {
+ | PUSH_ZVAL_ADDR res_addr, r0
+ } else {
+ | push 0
+ }
+ | PUSH_ZVAL_ADDR val_addr, r0
+ |.endif
+
+ | EXT_CALL zend_jit_assign_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)) {
+ // value = zend_assign_to_variable(property_val, value, OP_DATA_TYPE, EX_USES_STRICT_TYPES());
+ if (opline->result_type == IS_UNUSED) {
+ if (!zend_jit_assign_to_variable_call(Dst, opline, prop_addr, prop_addr, -1, -1, (opline+1)->op1_type, val_addr, val_info, res_addr, 0)) {
+ return 0;
+ }
+ } else {
+ if (!zend_jit_assign_to_variable(Dst, opline, prop_addr, prop_addr, -1, -1, (opline+1)->op1_type, val_addr, val_info, res_addr, 0)) {
+ return 0;
+ }
+ }
+ }
+
+ if (!prop_info || JIT_G(trigger) != ZEND_JIT_ON_HOT_TRACE) {
+ |.cold_code
+ |5:
+ | 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->extended_value
+ if (RETURN_VALUE_USED(opline)) {
+ |.if X64WIN
+ | LOAD_ZVAL_ADDR r0, res_addr
+ | mov aword A5, r0
+ |.else
+ | LOAD_ZVAL_ADDR CARG5, res_addr
+ |.endif
+ } else {
+ |.if X64WIN
+ | mov aword A5, 0
+ |.else
+ | xor CARG5, CARG5
+ |.endif
+ }
+ |.else
+ | sub r4, 4
+ if (RETURN_VALUE_USED(opline)) {
+ | PUSH_ZVAL_ADDR res_addr, r0
+ } else {
+ | push 0
+ }
+ | mov r0, EX->run_time_cache
+ | add r0, opline->extended_value
+ | push r0
+ | PUSH_ZVAL_ADDR val_addr, r0
+ |.endif
+
+ | EXT_CALL zend_jit_assign_obj_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_free(dasm_State **Dst, const zend_op *opline, uint32_t op1_info, int may_throw)
{
zend_jit_addr op1_addr = OP1_ADDR();