summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDmitry Stogov <dmitry@zend.com>2020-07-15 17:28:46 +0300
committerDmitry Stogov <dmitry@zend.com>2020-07-15 17:28:46 +0300
commitf74e9a4dd35a92627f3195d94ce8952e46b5d479 (patch)
treec59559239265e9bd989e99a1c32ac27c46ae27fb
parent1c0ee68b830554fd381c72a74d3276095a69945c (diff)
downloadphp-git-f74e9a4dd35a92627f3195d94ce8952e46b5d479.tar.gz
Check type guard on result of FETCH_DIM_R/IS instructions
-rw-r--r--ext/opcache/jit/zend_jit_internal.h2
-rw-r--r--ext/opcache/jit/zend_jit_trace.c98
-rw-r--r--ext/opcache/jit/zend_jit_x86.dasc112
-rw-r--r--ext/opcache/jit/zend_jit_x86.h2
4 files changed, 190 insertions, 24 deletions
diff --git a/ext/opcache/jit/zend_jit_internal.h b/ext/opcache/jit/zend_jit_internal.h
index 6c47547034..7dd8786fde 100644
--- a/ext/opcache/jit/zend_jit_internal.h
+++ b/ext/opcache/jit/zend_jit_internal.h
@@ -208,6 +208,8 @@ typedef enum _zend_jit_trace_stop {
#define ZEND_JIT_EXIT_TO_VM (1<<2) /* exit to VM without attempt to create a side trace */
#define ZEND_JIT_EXIT_RESTORE_CALL (1<<3) /* deoptimizer should restore EX(call) chain */
#define ZEND_JIT_EXIT_POLYMORPHISM (1<<4) /* exit because of polymorphic call */
+#define ZEND_JIT_EXIT_FREE_OP1 (1<<5)
+#define ZEND_JIT_EXIT_FREE_OP2 (1<<6)
typedef union _zend_op_trace_info {
zend_op dummy; /* the size of this structure must be the same as zend_op */
diff --git a/ext/opcache/jit/zend_jit_trace.c b/ext/opcache/jit/zend_jit_trace.c
index 655bcbc2b2..a5bc5f34af 100644
--- a/ext/opcache/jit/zend_jit_trace.c
+++ b/ext/opcache/jit/zend_jit_trace.c
@@ -146,7 +146,8 @@ static uint32_t zend_jit_trace_get_exit_point(const zend_op *from_opline, const
if (stack_size) {
stack = JIT_G(current_frame)->stack;
do {
- if (STACK_TYPE(stack, stack_size-1) != IS_UNKNOWN) {
+ if (STACK_TYPE(stack, stack_size-1) != IS_UNKNOWN
+ || STACK_REG(stack, stack_size-1) != ZREG_NONE) {
break;
}
stack_size--;
@@ -2832,17 +2833,37 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par
goto jit_failure;
}
} else {
- SET_STACK_REG(stack, i, ZREG_NONE);
+ if (STACK_REG(parent_stack, i) == ZREG_ZVAL_COPY_R0) {
+ SET_STACK_TYPE(stack, i, IS_UNKNOWN);
+ } else {
+ SET_STACK_REG(stack, i, ZREG_NONE);
+ }
if (!zend_jit_store_const(&dasm_state, i, STACK_REG(parent_stack, i))) {
goto jit_failure;
}
}
}
}
-
if (zend_jit_traces[parent_trace].exit_info[exit_num].flags & ZEND_JIT_EXIT_RESTORE_CALL) {
zend_jit_save_call_chain(&dasm_state, -1);
}
+ if (zend_jit_traces[parent_trace].exit_info[exit_num].flags & ZEND_JIT_EXIT_FREE_OP2) {
+ const zend_op *op = zend_jit_traces[parent_trace].exit_info[exit_num].opline - 1;
+ if (!zend_jit_free_op(&dasm_state, op, -1, op->op2.var)) {
+ goto jit_failure;
+ }
+ }
+ if (zend_jit_traces[parent_trace].exit_info[exit_num].flags & ZEND_JIT_EXIT_FREE_OP1) {
+ const zend_op *op = zend_jit_traces[parent_trace].exit_info[exit_num].opline - 1;
+ if (!zend_jit_free_op(&dasm_state, op, -1, op->op1.var)) {
+ goto jit_failure;
+ }
+ }
+ if (zend_jit_traces[parent_trace].exit_info[exit_num].flags & (ZEND_JIT_EXIT_FREE_OP1|ZEND_JIT_EXIT_FREE_OP2)) {
+ if (!zend_jit_check_exception(&dasm_state)) {
+ goto jit_failure;
+ }
+ }
}
if (ra
@@ -3750,6 +3771,11 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par
(op2_info & (MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_ARRAY_OF_OBJECT|MAY_BE_ARRAY_OF_RESOURCE|MAY_BE_ARRAY_OF_ARRAY)) != 0)))) {
goto jit_failure;
}
+ if ((res_info & MAY_BE_GUARD)
+ && JIT_G(current_frame)
+ && (op1_info & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_ARRAY) {
+ ssa->var_info[ssa_op->result_def].type &= ~MAY_BE_GUARD;
+ }
goto done;
case ZEND_ISSET_ISEMPTY_DIM_OBJ:
if ((opline->extended_value & ZEND_ISEMPTY)) {
@@ -4598,6 +4624,21 @@ static const void *zend_jit_trace_exit_to_vm(uint32_t trace_num, uint32_t exit_n
opline = zend_jit_traces[trace_num].exit_info[exit_num].opline;
if (opline) {
+ if (zend_jit_traces[trace_num].exit_info[exit_num].flags & ZEND_JIT_EXIT_FREE_OP2) {
+ if (!zend_jit_free_op(&dasm_state, (opline-1), -1, (opline-1)->op2.var)) {
+ goto jit_failure;
+ }
+ }
+ if (zend_jit_traces[trace_num].exit_info[exit_num].flags & ZEND_JIT_EXIT_FREE_OP1) {
+ if (!zend_jit_free_op(&dasm_state, (opline-1), -1, (opline-1)->op1.var)) {
+ goto jit_failure;
+ }
+ }
+ if (zend_jit_traces[trace_num].exit_info[exit_num].flags & (ZEND_JIT_EXIT_FREE_OP1|ZEND_JIT_EXIT_FREE_OP2)) {
+ if (!zend_jit_check_exception(&dasm_state)) {
+ goto jit_failure;
+ }
+ }
zend_jit_set_ip(&dasm_state, opline);
if (opline == zend_jit_traces[zend_jit_traces[trace_num].root].opline) {
/* prevent endless loop */
@@ -4995,6 +5036,12 @@ static void zend_jit_dump_exit_info(zend_jit_trace_info *t)
if (t->exit_info[i].flags & ZEND_JIT_EXIT_POLYMORPHISM) {
fprintf(stderr, "/POLY");
}
+ if (t->exit_info[i].flags & ZEND_JIT_EXIT_FREE_OP1) {
+ fprintf(stderr, "/FREE_OP1");
+ }
+ if (t->exit_info[i].flags & ZEND_JIT_EXIT_FREE_OP2) {
+ fprintf(stderr, "/FREE_OP2");
+ }
for (j = 0; j < stack_size; j++) {
zend_uchar type = STACK_TYPE(stack, j);
if (type != IS_UNKNOWN) {
@@ -5005,16 +5052,18 @@ static void zend_jit_dump_exit_info(zend_jit_trace_info *t)
fprintf(stderr, "undef");
} else {
fprintf(stderr, "%s", zend_get_type_by_const(type));
- if (STACK_REG(stack, j) != ZREG_NONE) {
- if (STACK_REG(stack, j) < ZREG_NUM) {
- fprintf(stderr, "(%s)", zend_reg_name[STACK_REG(stack, j)]);
- } else if (STACK_REG(stack, j) == ZREG_THIS) {
- fprintf(stderr, "(this)");
- } else {
- fprintf(stderr, "(const_%d)", STACK_REG(stack, j) - ZREG_NUM);
- }
+ }
+ if (STACK_REG(stack, j) != ZREG_NONE) {
+ if (STACK_REG(stack, j) < ZREG_NUM) {
+ fprintf(stderr, "(%s)", zend_reg_name[STACK_REG(stack, j)]);
+ } else if (STACK_REG(stack, j) == ZREG_THIS) {
+ fprintf(stderr, "(this)");
+ } else {
+ fprintf(stderr, "(const_%d)", STACK_REG(stack, j) - ZREG_NUM);
}
}
+ } else if (STACK_REG(stack, j) == ZREG_ZVAL_COPY_R0) {
+ fprintf(stderr, " zval_copy(%s)", zend_reg_name[0]);
}
}
fprintf(stderr, "\n");
@@ -5477,6 +5526,12 @@ int ZEND_FASTCALL zend_jit_trace_exit(uint32_t exit_num, zend_jit_registers_buf
GC_ADDREF(obj);
ZVAL_OBJ(EX_VAR_NUM(i), obj);
+ } else if (STACK_REG(stack, i) == ZREG_NULL) {
+ ZVAL_NULL(EX_VAR_NUM(i));
+ } else if (STACK_REG(stack, i) == ZREG_ZVAL_COPY_R0) {
+ zval *val = (zval*)regs->r[0];
+
+ ZVAL_COPY(EX_VAR_NUM(i), val);
} else {
ZEND_UNREACHABLE();
}
@@ -5484,7 +5539,28 @@ int ZEND_FASTCALL zend_jit_trace_exit(uint32_t exit_num, zend_jit_registers_buf
}
opline = t->exit_info[exit_num].opline;
+
if (opline) {
+ if (t->exit_info[exit_num].flags & ZEND_JIT_EXIT_FREE_OP2) {
+ ZEND_ASSERT((opline-1)->opcode == ZEND_FETCH_DIM_R
+ || (opline-1)->opcode == ZEND_FETCH_DIM_IS
+ || (opline-1)->opcode == ZEND_FETCH_DIM_FUNC_ARG);
+ EX(opline) = opline-1;
+ zval_ptr_dtor_nogc(EX_VAR((opline-1)->op2.var));
+ }
+ if (t->exit_info[exit_num].flags & ZEND_JIT_EXIT_FREE_OP1) {
+ ZEND_ASSERT((opline-1)->opcode == ZEND_FETCH_DIM_R
+ || (opline-1)->opcode == ZEND_FETCH_DIM_IS
+ || (opline-1)->opcode == ZEND_FETCH_DIM_FUNC_ARG);
+ EX(opline) = opline-1;
+ zval_ptr_dtor_nogc(EX_VAR((opline-1)->op1.var));
+ }
+ if (t->exit_info[exit_num].flags & (ZEND_JIT_EXIT_FREE_OP1|ZEND_JIT_EXIT_FREE_OP2)) {
+ if (EG(exception)) {
+ return 1;
+ }
+ }
+
/* Set VM opline to continue interpretation */
EX(opline) = opline;
}
diff --git a/ext/opcache/jit/zend_jit_x86.dasc b/ext/opcache/jit/zend_jit_x86.dasc
index 2bc4da07a4..9508cd3fa7 100644
--- a/ext/opcache/jit/zend_jit_x86.dasc
+++ b/ext/opcache/jit/zend_jit_x86.dasc
@@ -1024,7 +1024,7 @@ static void* dasm_labels[zend_lb_MAX];
/* the same as above, but "src" may overlap with "tmp_reg1" */
|.macro ZVAL_COPY_VALUE, dst_addr, dst_info, src_addr, src_info, tmp_reg1, tmp_reg2
|| if (src_info & (MAY_BE_ANY-(MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE))) {
-|| if ((src_info & MAY_BE_ANY) == MAY_BE_LONG) {
+|| if ((src_info & (MAY_BE_ANY|MAY_BE_GUARD)) == MAY_BE_LONG) {
|| if (Z_MODE(src_addr) == IS_REG) {
|| if (Z_MODE(dst_addr) != IS_REG || Z_REG(dst_addr) != Z_REG(src_addr)) {
| SET_ZVAL_LVAL dst_addr, Ra(Z_REG(src_addr))
@@ -1035,7 +1035,7 @@ static void* dasm_labels[zend_lb_MAX];
| GET_ZVAL_LVAL tmp_reg2, src_addr
| SET_ZVAL_LVAL dst_addr, Ra(tmp_reg2)
|| }
-|| } else if ((src_info & MAY_BE_ANY) == MAY_BE_DOUBLE) {
+|| } else if ((src_info & (MAY_BE_ANY|MAY_BE_GUARD)) == MAY_BE_DOUBLE) {
|| if (Z_MODE(src_addr) == IS_REG) {
| SSE_SET_ZVAL_DVAL dst_addr, Z_REG(src_addr)
|| } else if (Z_MODE(dst_addr) == IS_REG) {
@@ -1044,7 +1044,7 @@ static void* dasm_labels[zend_lb_MAX];
| SSE_GET_ZVAL_DVAL ZREG_XMM0, src_addr
| SSE_SET_ZVAL_DVAL dst_addr, ZREG_XMM0
|| }
-|| } else if (!(src_info & MAY_BE_DOUBLE)) {
+|| } else if (!(src_info & (MAY_BE_DOUBLE|MAY_BE_GUARD))) {
| GET_ZVAL_PTR Ra(tmp_reg2), src_addr
| SET_ZVAL_PTR dst_addr, Ra(tmp_reg2)
|| } else {
@@ -1067,9 +1067,10 @@ static void* dasm_labels[zend_lb_MAX];
|| }
|| }
|| if ((src_info & (MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG|MAY_BE_DOUBLE)) &&
+|| !(src_info & MAY_BE_GUARD) &&
|| has_concrete_type(src_info & MAY_BE_ANY)) {
|| if (Z_MODE(dst_addr) == IS_MEM_ZVAL) {
-|| if ((dst_info & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_GUARD)) != (src_info & (MAY_BE_ANY|MAY_BE_UNDEF))) {
+|| if ((dst_info & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_GUARD)) != (src_info & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_GUARD))) {
|| zend_uchar type = concrete_type(src_info);
| SET_ZVAL_TYPE_INFO dst_addr, type
|| }
@@ -3558,6 +3559,13 @@ static int zend_jit_store_const(dasm_State **Dst, int var, zend_reg reg)
| SET_ZVAL_W2 dst, 0x41e00000
|.endif
| SET_ZVAL_TYPE_INFO dst, IS_DOUBLE
+ } else if (reg == ZREG_NULL) {
+ | SET_ZVAL_TYPE_INFO dst, IS_NULL
+ } else if (reg == ZREG_ZVAL_COPY_R0) {
+ zend_jit_addr val_addr = ZEND_ADDR_MEM_ZVAL(ZREG_R0, 0);
+
+ | ZVAL_COPY_VALUE dst, -1, val_addr, -1, ZREG_R1, ZREG_R2
+ | TRY_ADDREF -1, ch, r2
} else {
ZEND_UNREACHABLE();
}
@@ -4849,6 +4857,8 @@ static int zend_jit_fetch_dimension_address_inner(dasm_State **Dst, const zend_o
}
} else if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE && (type == BP_VAR_R || type == BP_VAR_RW)) {
| jbe &exit_addr
+ } else if (type == BP_VAR_IS && not_found_exit_addr) {
+ | jbe &not_found_exit_addr
} else {
| jbe >2 // NOT_FOUND
}
@@ -4886,6 +4896,8 @@ static int zend_jit_fetch_dimension_address_inner(dasm_State **Dst, const zend_o
}
} else if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE && (type == BP_VAR_R || type == BP_VAR_RW)) {
| jbe &exit_addr
+ } else if (type == BP_VAR_IS && not_found_exit_addr) {
+ | jbe &not_found_exit_addr
} else {
| jbe >2 // NOT_FOUND
}
@@ -4933,12 +4945,16 @@ static int zend_jit_fetch_dimension_address_inner(dasm_State **Dst, const zend_o
if (val >= 0 && val < HT_MAX_SIZE) {
if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE && type == BP_VAR_R) {
| jmp &exit_addr
+ } else if (type == BP_VAR_IS && not_found_exit_addr) {
+ | jmp &not_found_exit_addr
} else {
| jmp >2 // NOT_FOUND
}
}
} else if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE && type == BP_VAR_R) {
| jmp &exit_addr
+ } else if (type == BP_VAR_IS && not_found_exit_addr) {
+ | jmp &not_found_exit_addr
} else {
| jmp >2 // NOT_FOUND
}
@@ -4952,6 +4968,8 @@ static int zend_jit_fetch_dimension_address_inner(dasm_State **Dst, const zend_o
| test r0, r0
if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE && type == BP_VAR_R) {
| jz &exit_addr
+ } else if (type == BP_VAR_IS && not_found_exit_addr) {
+ | jz &not_found_exit_addr
} else {
| jz >2 // NOT_FOUND
}
@@ -4968,9 +4986,11 @@ static int zend_jit_fetch_dimension_address_inner(dasm_State **Dst, const zend_o
break;
case BP_VAR_IS:
case BP_VAR_UNSET:
- | // retval = &EG(uninitialized_zval);
- | SET_ZVAL_TYPE_INFO res_addr, IS_NULL
- | jmp >9
+ if (!not_found_exit_addr) {
+ | // retval = &EG(uninitialized_zval);
+ | SET_ZVAL_TYPE_INFO res_addr, IS_NULL
+ | jmp >9
+ }
break;
default:
ZEND_UNREACHABLE();
@@ -5078,6 +5098,8 @@ static int zend_jit_fetch_dimension_address_inner(dasm_State **Dst, const zend_o
| test r0, r0
if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE && type == BP_VAR_R) {
| jz &exit_addr
+ } else if (type == BP_VAR_IS && not_found_exit_addr) {
+ | jz &not_found_exit_addr
} else {
| jz >2 // NOT_FOUND
}
@@ -5088,6 +5110,9 @@ static int zend_jit_fetch_dimension_address_inner(dasm_State **Dst, const zend_o
| // retval = Z_INDIRECT_P(retval);
| GET_Z_PTR r0, r0
| IF_NOT_Z_TYPE r0, IS_UNDEF, >8
+ if (type == BP_VAR_IS && not_found_exit_addr) {
+ | jmp &not_found_exit_addr
+ }
|2:
switch (type) {
case BP_VAR_R:
@@ -5101,9 +5126,11 @@ static int zend_jit_fetch_dimension_address_inner(dasm_State **Dst, const zend_o
break;
case BP_VAR_IS:
case BP_VAR_UNSET:
- | // retval = &EG(uninitialized_zval);
- | SET_ZVAL_TYPE_INFO res_addr, IS_NULL
- | jmp >9
+ if (!not_found_exit_addr) {
+ | // retval = &EG(uninitialized_zval);
+ | SET_ZVAL_TYPE_INFO res_addr, IS_NULL
+ | jmp >9
+ }
break;
default:
ZEND_UNREACHABLE();
@@ -9851,6 +9878,14 @@ static int zend_jit_free_cv(dasm_State **Dst, const zend_op *opline, const zend_
return 1;
}
+static int zend_jit_free_op(dasm_State **Dst, const zend_op *opline, /*const zend_op_array *op_array, */uint32_t info, uint32_t var_offset)
+{
+ if (info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF)) {
+ | ZVAL_PTR_DTOR ZEND_ADDR_MEM_ZVAL(ZREG_FP, var_offset), info, 0, 1, opline
+ }
+ return 1;
+}
+
static int zend_jit_leave_func(dasm_State **Dst, const zend_op *opline, const zend_op_array *op_array, zend_jit_trace_rec *trace, zend_jit_trace_info *trace_info)
{
/* ZEND_CALL_FAKE_CLOSURE handled on slow path to eliminate check for ZEND_CALL_CLOSURE on fast path */
@@ -10203,6 +10238,8 @@ static int zend_jit_fetch_dim_read(dasm_State **Dst, const zend_op *opline, cons
{
zend_jit_addr orig_op1_addr, op2_addr, res_addr;
const void *exit_addr = NULL;
+ const void *not_found_exit_addr = NULL;
+ const void *res_exit_addr = NULL;
orig_op1_addr = OP1_ADDR();
op2_addr = OP2_ADDR();
@@ -10218,6 +10255,45 @@ static int zend_jit_fetch_dim_read(dasm_State **Dst, const zend_op *opline, cons
}
}
+ if ((res_info & MAY_BE_GUARD)
+ && JIT_G(current_frame)
+ && (op1_info & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_ARRAY) {
+ uint32_t flags = 0;
+ uint32_t old_info;
+ zend_jit_trace_stack *stack = JIT_G(current_frame)->stack;
+ int32_t exit_point;
+
+ if (opline->op1_type & (IS_VAR|IS_TMP_VAR)) {
+ flags = ZEND_JIT_EXIT_FREE_OP1;
+ }
+ if ((opline->op2_type & (IS_VAR|IS_TMP_VAR))
+ && (op2_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
+ flags = ZEND_JIT_EXIT_FREE_OP2;
+ }
+ old_info = STACK_INFO(stack, EX_VAR_TO_NUM(opline->result.var));
+ SET_STACK_TYPE(stack, EX_VAR_TO_NUM(opline->result.var), IS_UNKNOWN);
+ SET_STACK_REG(stack, EX_VAR_TO_NUM(opline->result.var), ZREG_ZVAL_COPY_R0);
+ exit_point = zend_jit_trace_get_exit_point(opline, opline+1, NULL, flags);
+ SET_STACK_INFO(stack, EX_VAR_TO_NUM(opline->result.var), old_info);
+ res_exit_addr = zend_jit_trace_get_exit_addr(exit_point);
+ if (!res_exit_addr) {
+ return 0;
+ }
+ if (opline->opcode == ZEND_FETCH_DIM_IS
+ && !(res_info & MAY_BE_NULL)) {
+ old_info = STACK_INFO(stack, EX_VAR_TO_NUM(opline->result.var));
+ SET_STACK_TYPE(stack, EX_VAR_TO_NUM(opline->result.var), IS_NULL);
+ SET_STACK_REG(stack, EX_VAR_TO_NUM(opline->result.var), ZREG_NULL);
+ exit_point = zend_jit_trace_get_exit_point(opline, opline+1, NULL, flags);
+ SET_STACK_INFO(stack, EX_VAR_TO_NUM(opline->result.var), old_info);
+ not_found_exit_addr = zend_jit_trace_get_exit_addr(exit_point);
+ if (!not_found_exit_addr) {
+ return 0;
+ }
+ }
+ res_info &= ~MAY_BE_GUARD;
+ }
+
if (op1_info & MAY_BE_REF) {
| LOAD_ZVAL_ADDR FCARG1a, op1_addr
| ZVAL_DEREF FCARG1a, op1_info
@@ -10233,7 +10309,7 @@ static int zend_jit_fetch_dim_read(dasm_State **Dst, const zend_op *opline, cons
}
}
| GET_ZVAL_LVAL ZREG_FCARG1a, op1_addr
- if (!zend_jit_fetch_dimension_address_inner(Dst, opline, (opline->opcode != ZEND_FETCH_DIM_IS) ? BP_VAR_R : BP_VAR_IS, op1_info, op2_info, 8, 9, NULL, NULL)) {
+ if (!zend_jit_fetch_dimension_address_inner(Dst, opline, (opline->opcode != ZEND_FETCH_DIM_IS) ? BP_VAR_R : BP_VAR_IS, op1_info, op2_info, 8, 9, NULL, not_found_exit_addr)) {
return 0;
}
}
@@ -10365,7 +10441,17 @@ static int zend_jit_fetch_dim_read(dasm_State **Dst, const zend_op *opline, cons
zend_jit_addr val_addr = ZEND_ADDR_MEM_ZVAL(ZREG_R0, 0);
|8:
- if (op1_info & MAY_BE_ARRAY_OF_REF) {
+ if (res_exit_addr) {
+ zend_uchar type = concrete_type(res_info);
+
+ if (op1_info & MAY_BE_ARRAY_OF_REF) {
+ | ZVAL_DEREF r0, MAY_BE_REF
+ }
+ | IF_NOT_ZVAL_TYPE val_addr, type, &res_exit_addr
+ | // ZVAL_COPY
+ | ZVAL_COPY_VALUE res_addr, -1, val_addr, res_info, ZREG_R1, ZREG_R2
+ | TRY_ADDREF res_info, ch, r2
+ } else if (op1_info & MAY_BE_ARRAY_OF_REF) {
| // ZVAL_COPY_DEREF
| GET_ZVAL_TYPE_INFO Rd(ZREG_R2), val_addr
if (!zend_jit_zval_copy_deref(Dst, res_addr, val_addr, ZREG_R2)) {
@@ -10373,7 +10459,7 @@ static int zend_jit_fetch_dim_read(dasm_State **Dst, const zend_op *opline, cons
}
} else {
| // ZVAL_COPY
- | ZVAL_COPY_VALUE res_addr, -1, val_addr, MAY_BE_ANY, ZREG_R1, ZREG_R2
+ | ZVAL_COPY_VALUE res_addr, -1, val_addr, res_info, ZREG_R1, ZREG_R2
| TRY_ADDREF res_info, ch, r2
}
}
diff --git a/ext/opcache/jit/zend_jit_x86.h b/ext/opcache/jit/zend_jit_x86.h
index 0ebf7a2235..10cd77a13a 100644
--- a/ext/opcache/jit/zend_jit_x86.h
+++ b/ext/opcache/jit/zend_jit_x86.h
@@ -71,6 +71,8 @@ typedef enum _zend_reg {
ZREG_LONG_MIN,
ZREG_LONG_MAX,
ZREG_LONG_MAX_PLUS_1,
+ ZREG_NULL,
+ ZREG_ZVAL_COPY_R0,
} zend_reg;
typedef struct _zend_jit_registers_buf {