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--;
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
(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)) {
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 */
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) {
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");
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();
}
}
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;
}
/* 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))
| 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) {
| 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 {
|| }
|| }
|| 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
|| }
| 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();
}
}
} 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 ¬_found_exit_addr
} else {
| jbe >2 // NOT_FOUND
}
}
} 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 ¬_found_exit_addr
} else {
| jbe >2 // NOT_FOUND
}
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 ¬_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 ¬_found_exit_addr
} else {
| jmp >2 // NOT_FOUND
}
| 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 ¬_found_exit_addr
} else {
| jz >2 // NOT_FOUND
}
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();
| 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 ¬_found_exit_addr
} else {
| jz >2 // NOT_FOUND
}
| // 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 ¬_found_exit_addr
+ }
|2:
switch (type) {
case BP_VAR_R:
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();
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 */
{
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();
}
}
+ 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
}
}
| 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;
}
}
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)) {
}
} 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
}
}