goto jit_failure;
}
goto done;
+ case ZEND_FETCH_DIM_W:
+ case ZEND_FETCH_DIM_RW:
+// case ZEND_FETCH_DIM_UNSET:
+ case ZEND_FETCH_LIST_W:
+ if (PROFITABILITY_CHECKS && (!ssa->ops || !ssa->var_info)) {
+ break;
+ }
+ if (opline->op1_type != IS_CV) {
+ break;
+ }
+ if (!zend_jit_fetch_dim(&dasm_state, opline,
+ OP1_INFO(), OP1_REG_ADDR(), OP2_INFO(), RES_REG_ADDR(),
+ zend_may_throw_ex(opline, ssa_op, op_array, ssa, op1_info, op2_info))) {
+ goto jit_failure;
+ }
+ goto done;
case ZEND_ISSET_ISEMPTY_DIM_OBJ:
if ((opline->extended_value & ZEND_ISEMPTY)) {
// TODO: support for empty() ???
REGISTER_HELPER(zend_jit_fetch_dim_obj_is_helper);
REGISTER_HELPER(zend_jit_fetch_dim_rw_helper);
REGISTER_HELPER(zend_jit_fetch_dim_w_helper);
+ REGISTER_HELPER(zend_jit_fetch_dim_obj_rw_helper);
+ REGISTER_HELPER(zend_jit_fetch_dim_obj_w_helper);
+// REGISTER_HELPER(zend_jit_fetch_dim_obj_unset_helper);
REGISTER_HELPER(zend_jit_assign_dim_helper);
REGISTER_HELPER(zend_jit_assign_dim_op_helper);
REGISTER_HELPER(zend_jit_fast_assign_concat_helper);
/* For BC reasons we allow errors so that we can warn on leading numeric string */
if (IS_LONG == is_numeric_string_ex(Z_STRVAL_P(dim), Z_STRLEN_P(dim), &offset, NULL,
/* allow errors */ true, NULL, &trailing_data)) {
- if (UNEXPECTED(trailing_data) /*&& type != BP_VAR_UNSET*/) {
+ if (UNEXPECTED(trailing_data)
+ && EG(current_execute_data)->opline->opcode != ZEND_FETCH_DIM_UNSET) {
zend_error(E_WARNING, "Illegal string offset \"%s\"", Z_STRVAL_P(dim));
}
return offset;
case ZEND_FETCH_DIM_RW:
case ZEND_FETCH_DIM_FUNC_ARG:
case ZEND_FETCH_DIM_UNSET:
+ case ZEND_FETCH_LIST_W:
/* TODO: Encode the "reason" into opline->extended_value??? */
var = opline->result.var;
opline++;
while (opline < end) {
if (opline->op1_type == IS_VAR && opline->op1.var == var) {
switch (opline->opcode) {
+ case ZEND_FETCH_OBJ_W:
+ case ZEND_FETCH_OBJ_RW:
+ case ZEND_FETCH_OBJ_FUNC_ARG:
+ case ZEND_FETCH_OBJ_UNSET:
+ case ZEND_ASSIGN_OBJ:
case ZEND_ASSIGN_OBJ_OP:
+ case ZEND_ASSIGN_OBJ_REF:
msg = "Cannot use string offset as an object";
break;
+ case ZEND_FETCH_DIM_W:
+ case ZEND_FETCH_DIM_RW:
+ case ZEND_FETCH_DIM_FUNC_ARG:
+ case ZEND_FETCH_DIM_UNSET:
+ case ZEND_FETCH_LIST_W:
+ case ZEND_ASSIGN_DIM:
case ZEND_ASSIGN_DIM_OP:
msg = "Cannot use string offset as an array";
break;
case ZEND_POST_DEC:
msg = "Cannot increment/decrement string offsets";
break;
- case ZEND_FETCH_DIM_W:
- case ZEND_FETCH_DIM_RW:
- case ZEND_FETCH_DIM_FUNC_ARG:
- case ZEND_FETCH_DIM_UNSET:
- case ZEND_ASSIGN_DIM:
- msg = "Cannot use string offset as an array";
- break;
- case ZEND_FETCH_OBJ_W:
- case ZEND_FETCH_OBJ_RW:
- case ZEND_FETCH_OBJ_FUNC_ARG:
- case ZEND_FETCH_OBJ_UNSET:
- case ZEND_ASSIGN_OBJ:
- msg = "Cannot use string offset as an object";
- break;
case ZEND_ASSIGN_REF:
case ZEND_ADD_ARRAY_ELEMENT:
case ZEND_INIT_ARRAY:
case ZEND_SEND_FUNC_ARG:
msg = "Only variables can be passed by reference";
break;
+ case ZEND_FE_RESET_RW:
+ msg = "Cannot iterate on string offsets by reference";
+ break;
EMPTY_SWITCH_DEFAULT_CASE();
}
break;
}
}
+static zend_always_inline void ZEND_FASTCALL zend_jit_fetch_dim_obj_helper(zval *object_ptr, zval *dim, zval *result, int type)
+{
+ zval *retval;
+
+ if (EXPECTED(Z_TYPE_P(object_ptr) == IS_OBJECT)) {
+ retval = Z_OBJ_HT_P(object_ptr)->read_dimension(Z_OBJ_P(object_ptr), dim, type, result);
+ if (UNEXPECTED(retval == &EG(uninitialized_zval))) {
+ zend_class_entry *ce = Z_OBJCE_P(object_ptr);
+
+ ZVAL_NULL(result);
+ zend_error(E_NOTICE, "Indirect modification of overloaded element of %s has no effect", ZSTR_VAL(ce->name));
+ } else if (EXPECTED(retval && Z_TYPE_P(retval) != IS_UNDEF)) {
+ if (!Z_ISREF_P(retval)) {
+ if (result != retval) {
+ ZVAL_COPY(result, retval);
+ retval = result;
+ }
+ if (Z_TYPE_P(retval) != IS_OBJECT) {
+ zend_class_entry *ce = Z_OBJCE_P(object_ptr);
+ zend_error(E_NOTICE, "Indirect modification of overloaded element of %s has no effect", ZSTR_VAL(ce->name));
+ }
+ } else if (UNEXPECTED(Z_REFCOUNT_P(retval) == 1)) {
+ ZVAL_UNREF(retval);
+ }
+ if (result != retval) {
+ ZVAL_INDIRECT(result, retval);
+ }
+ } else {
+ ZEND_ASSERT(EG(exception) && "read_dimension() returned NULL without exception");
+ ZVAL_UNDEF(result);
+ }
+ } else if (EXPECTED(Z_TYPE_P(object_ptr) == IS_STRING)) {
+ if (!dim) {
+ zend_throw_error(NULL, "[] operator not supported for strings");
+ } else {
+ if (UNEXPECTED(Z_TYPE_P(dim) != IS_LONG)) {
+ zend_check_string_offset(dim/*, BP_VAR_RW*/);
+ }
+ if (!EG(exception)) {
+ zend_wrong_string_offset();
+ }
+ }
+ ZVAL_UNDEF(result);
+ } else {
+ if (type == BP_VAR_UNSET) {
+ zend_throw_error(NULL, "Cannot unset offset in a non-array variable");
+ ZVAL_UNDEF(result);
+ } else {
+ zend_throw_error(NULL, "Cannot use a scalar value as an array");
+ ZVAL_UNDEF(result);
+ }
+ }
+}
+
+static void ZEND_FASTCALL zend_jit_fetch_dim_obj_w_helper(zval *object_ptr, zval *dim, zval *result)
+{
+ zend_jit_fetch_dim_obj_helper(object_ptr, dim, result, BP_VAR_W);
+}
+
+static void ZEND_FASTCALL zend_jit_fetch_dim_obj_rw_helper(zval *object_ptr, zval *dim, zval *result)
+{
+ zend_jit_fetch_dim_obj_helper(object_ptr, dim, result, BP_VAR_RW);
+}
+
+//static void ZEND_FASTCALL zend_jit_fetch_dim_obj_unset_helper(zval *object_ptr, zval *dim, zval *result)
+//{
+// zend_jit_fetch_dim_obj_helper(object_ptr, dim, result, BP_VAR_UNSET);
+//}
+
static void ZEND_FASTCALL zend_jit_assign_dim_helper(zval *object_ptr, zval *dim, zval *value, zval *result)
{
if (EXPECTED(Z_TYPE_P(object_ptr) == IS_OBJECT)) {
}
}
break;
+ case ZEND_FETCH_DIM_W:
+ case ZEND_FETCH_DIM_RW:
+// case ZEND_FETCH_DIM_UNSET:
+ case ZEND_FETCH_LIST_W:
+ if (opline->op1_type != IS_CV) {
+ break;
+ }
+ ADD_OP1_TRACE_GUARD();
+ ADD_OP2_TRACE_GUARD();
+ break;
case ZEND_SEND_VAL_EX:
case ZEND_SEND_VAR_EX:
case ZEND_SEND_VAR_NO_REF_EX:
goto jit_failure;
}
goto done;
+ case ZEND_FETCH_DIM_W:
+ case ZEND_FETCH_DIM_RW:
+// case ZEND_FETCH_DIM_UNSET:
+ case ZEND_FETCH_LIST_W:
+ if (opline->op1_type != IS_CV) {
+ break;
+ }
+ op1_info = OP1_INFO();
+ op1_addr = OP1_REG_ADDR();
+ 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();
+ }
+ op2_info = OP2_INFO();
+ CHECK_OP2_TRACE_TYPE();
+ op1_def_info = OP1_DEF_INFO();
+ if (!zend_jit_fetch_dim(&dasm_state, opline,
+ op1_info, op1_addr, op2_info, RES_REG_ADDR(),
+ zend_may_throw_ex(opline, ssa_op, op_array, ssa, op1_info, op2_info))) {
+ goto jit_failure;
+ }
+ goto done;
case ZEND_ISSET_ISEMPTY_DIM_OBJ:
if ((opline->extended_value & ZEND_ISEMPTY)) {
// TODO: support for empty() ???
return 1;
}
+static int zend_jit_fetch_dim(dasm_State **Dst,
+ const zend_op *opline,
+ uint32_t op1_info,
+ zend_jit_addr op1_addr,
+ uint32_t op2_info,
+ zend_jit_addr res_addr,
+ int may_throw)
+{
+ zend_jit_addr op2_addr;
+
+ ZEND_ASSERT(opline->op1_type == IS_CV);
+
+ op2_addr = (opline->op2_type != IS_UNUSED) ? OP2_ADDR() : 0;
+
+ if (op1_info & MAY_BE_REF) {
+ | LOAD_ZVAL_ADDR FCARG1a, op1_addr
+ | IF_NOT_Z_TYPE FCARG1a, IS_REFERENCE, >1
+ | GET_Z_PTR FCARG2a, FCARG1a
+ | IF_NOT_TYPE byte [FCARG2a + offsetof(zend_reference, val) + offsetof(zval, u1.v.type)], IS_ARRAY, >2
+ | lea FCARG1a, [FCARG2a + offsetof(zend_reference, val)]
+ | jmp >3
+ |.cold_code
+ |2:
+ | SET_EX_OPLINE opline, r0
+ | EXT_CALL zend_jit_prepare_assign_dim_ref, r0
+ | test r0, r0
+ | mov FCARG1a, r0
+ | jne >1
+ | jmp ->exception_handler_undef
+ |.code
+ |1:
+ op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1a, 0);
+ }
+
+ if (op1_info & MAY_BE_ARRAY) {
+ if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF) - MAY_BE_ARRAY)) {
+ | IF_NOT_ZVAL_TYPE op1_addr, IS_ARRAY, >7
+ }
+ |3:
+ | SEPARATE_ARRAY op1_addr, op1_info, 1
+ }
+ if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE)) {
+ if (op1_info & MAY_BE_ARRAY) {
+ |.cold_code
+ |7:
+ }
+ if (op1_info & (MAY_BE_ANY-(MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_ARRAY))) {
+ | CMP_ZVAL_TYPE op1_addr, IS_FALSE
+ | jg >7
+ }
+ if ((op1_info & MAY_BE_UNDEF)
+ && opline->opcode == ZEND_FETCH_DIM_RW) {
+ if (op1_info & (MAY_BE_NULL|MAY_BE_FALSE)) {
+ | IF_NOT_ZVAL_TYPE op1_addr, IS_UNDEF, >1
+ }
+ | SET_EX_OPLINE opline, r0
+ | mov FCARG1a, opline->op1.var
+ | EXT_CALL zend_jit_undefined_op_helper, r0
+ |1:
+ }
+ | // ZVAL_ARR(container, zend_new_array(8));
+ if (Z_REG(op1_addr) != ZREG_FP) {
+ | mov T1, Ra(Z_REG(op1_addr)) // save
+ }
+ | EXT_CALL _zend_new_array_0, r0
+ if (Z_REG(op1_addr) != ZREG_FP) {
+ | mov Ra(Z_REG(op1_addr)), T1 // restore
+ }
+ | SET_ZVAL_LVAL op1_addr, r0
+ | SET_ZVAL_TYPE_INFO op1_addr, IS_ARRAY_EX
+ | mov FCARG1a, r0
+ if (op1_info & MAY_BE_ARRAY) {
+ | jmp >1
+ |.code
+ |1:
+ }
+ }
+
+ if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_ARRAY)) {
+ |6:
+ if (opline->op2_type == IS_UNUSED) {
+ | // var_ptr = zend_hash_next_index_insert(Z_ARRVAL_P(container), &EG(uninitialized_zval));
+ | LOAD_ADDR_ZTS FCARG2a, executor_globals, uninitialized_zval
+ | EXT_CALL zend_hash_next_index_insert, r0
+ | // if (UNEXPECTED(!var_ptr)) {
+ | test r0, r0
+ | jz >1
+ |.cold_code
+ |1:
+ | // zend_throw_error(NULL, "Cannot add element to the array as the next element is already occupied");
+ | CANNOT_ADD_ELEMENT opline
+ | SET_ZVAL_TYPE_INFO res_addr, IS_UNDEF
+ | //ZEND_VM_C_GOTO(assign_dim_op_ret_null);
+ | jmp >8
+ |.code
+ | SET_ZVAL_PTR res_addr, r0
+ | SET_ZVAL_TYPE_INFO res_addr, IS_INDIRECT
+ } else {
+ uint32_t type;
+
+ switch (opline->opcode) {
+ case ZEND_FETCH_DIM_W:
+ case ZEND_FETCH_LIST_W:
+ type = BP_VAR_W;
+ break;
+ case ZEND_FETCH_DIM_RW:
+ type = BP_VAR_RW;
+ break;
+ case ZEND_FETCH_DIM_UNSET:
+ type = BP_VAR_UNSET;
+ break;
+ default:
+ ZEND_UNREACHABLE();
+ }
+
+ if (!zend_jit_fetch_dimension_address_inner(Dst, opline, type, op1_info, op2_info, NULL, NULL, NULL)) {
+ return 0;
+ }
+
+ |8:
+ | SET_ZVAL_PTR res_addr, r0
+ | SET_ZVAL_TYPE_INFO res_addr, IS_INDIRECT
+
+ if (type == BP_VAR_RW || (op2_info & ((MAY_BE_ANY|MAY_BE_UNDEF) - (MAY_BE_LONG|MAY_BE_STRING)))) {
+ |.cold_code
+ |9:
+ | SET_ZVAL_TYPE_INFO res_addr, IS_NULL
+ | jmp >8
+ |.code
+ }
+ }
+ }
+
+ if (op1_info & (MAY_BE_ANY-(MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_ARRAY))) {
+ if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_ARRAY)) {
+ |.cold_code
+ |7:
+ }
+
+ | SET_EX_OPLINE opline, r0
+ if (Z_REG(op1_addr) != ZREG_FCARG1a || Z_OFFSET(op1_addr) != 0) {
+ | LOAD_ZVAL_ADDR FCARG1a, op1_addr
+ }
+ if (opline->op2_type == IS_UNUSED) {
+ | xor FCARG2a, FCARG2a
+ } else if (opline->op2_type == IS_CONST && Z_EXTRA_P(RT_CONSTANT(opline, opline->op2)) == ZEND_EXTRA_VALUE) {
+ ZEND_ASSERT(Z_MODE(op2_addr) == IS_CONST_ZVAL);
+ | LOAD_ADDR FCARG2a, (Z_ZV(op2_addr) + 1)
+ } else {
+ | LOAD_ZVAL_ADDR FCARG2a, op2_addr
+ }
+ |.if X64
+ | LOAD_ZVAL_ADDR CARG3, res_addr
+ |.else
+ | sub r4, 12
+ | PUSH_ZVAL_ADDR res_addr, r0
+ |.endif
+ switch (opline->opcode) {
+ case ZEND_FETCH_DIM_W:
+ case ZEND_FETCH_LIST_W:
+ | EXT_CALL zend_jit_fetch_dim_obj_w_helper, r0
+ break;
+ case ZEND_FETCH_DIM_RW:
+ | EXT_CALL zend_jit_fetch_dim_obj_rw_helper, r0
+ break;
+// case ZEND_FETCH_DIM_UNSET:
+// | EXT_CALL zend_jit_fetch_dim_obj_unset_helper, r0
+// break;
+ default:
+ ZEND_UNREACHABLE();
+ }
+ |.if not(X64)
+ | add r4, 12
+ |.endif
+
+ if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_ARRAY)) {
+ | jmp >8 // END
+ |.code
+ }
+ }
+
+ |8:
+ | FREE_OP opline->op2_type, opline->op2, op2_info, 0, opline
+
+ if (may_throw) {
+ if (!zend_jit_check_exception(Dst)) {
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
static int zend_jit_isset_isempty_dim(dasm_State **Dst,
const zend_op *opline,
uint32_t op1_info,