int parent_vars_count,
zend_ssa *ssa,
zend_jit_trace_stack *stack,
- zend_lifetime_interval **ra)
+ zend_lifetime_interval **ra,
+ zend_bool polymorphic_side_trace)
{
int i;
zend_bool has_constants = 0;
if (reg < ZREG_NUM) {
/* pass */
} else if (reg == ZREG_THIS) {
- if (!zend_jit_load_this(Dst, EX_NUM_TO_VAR(i))) {
+ if (polymorphic_side_trace) {
+ ssa->var_info[i].delayed_fetch_this = 1;
+ } else if (!zend_jit_load_this(Dst, EX_NUM_TO_VAR(i))) {
return 0;
}
} else {
zend_bool ce_is_instanceof;
zend_bool delayed_fetch_this = 0;
zend_bool avoid_refcounting = 0;
+ zend_bool polymorphic_side_trace =
+ parent_trace &&
+ (zend_jit_traces[parent_trace].exit_info[exit_num].flags & ZEND_JIT_EXIT_METHOD_CALL);
uint32_t i;
zend_jit_trace_stack_frame *frame, *top, *call;
zend_jit_trace_stack *stack;
if (!zend_jit_trace_deoptimization(&dasm_state,
zend_jit_traces[parent_trace].exit_info[exit_num].flags,
zend_jit_traces[parent_trace].exit_info[exit_num].opline,
- parent_stack, parent_vars_count, ssa, stack, ra)) {
+ parent_stack, parent_vars_count, ssa, stack, ra,
+ polymorphic_side_trace)) {
goto jit_failure;
}
}
} else {
op1_info = OP1_INFO();
op1_addr = OP1_REG_ADDR();
- if (orig_op1_type != IS_UNKNOWN
+ if (polymorphic_side_trace) {
+ op1_info = MAY_BE_OBJECT;
+ op1_addr = 0;
+ } else 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)) {
op_array_ssa->cfg.map ? op_array_ssa->cfg.map[opline - op_array->opcodes] : -1,
op_array, ssa, ssa_op, frame->call_level,
op1_info, op1_addr, ce, ce_is_instanceof, delayed_fetch_this, op1_ce,
- p + 1, used_stack < 0)) {
+ p + 1, used_stack < 0, polymorphic_side_trace)) {
goto jit_failure;
}
goto done;
}
done:
+ polymorphic_side_trace = 0;
switch (opline->opcode) {
case ZEND_DO_FCALL:
case ZEND_DO_ICALL:
} else if (p->stop == ZEND_JIT_TRACE_STOP_LINK
|| p->stop == ZEND_JIT_TRACE_STOP_INTERPRETER) {
if (!zend_jit_trace_deoptimization(&dasm_state, 0, NULL,
- stack, op_array->last_var + op_array->T, NULL, NULL, NULL)) {
+ stack, op_array->last_var + op_array->T, NULL, NULL, NULL, 0)) {
goto jit_failure;
}
if (p->stop == ZEND_JIT_TRACE_STOP_LINK) {
if (!zend_jit_trace_deoptimization(&dasm_state,
zend_jit_traces[trace_num].exit_info[exit_num].flags,
zend_jit_traces[trace_num].exit_info[exit_num].opline,
- stack, stack_size, NULL, NULL, NULL)) {
+ stack, stack_size, NULL, NULL, NULL, 0)) {
goto jit_failure;
}
| mov r1, aword EX:r1->func
| .if X64
|| if (!IS_SIGNED_32BIT(opcodes)) {
- | mov64 r0, ((ptrdiff_t)opcodes)
- | cmp aword [r1 + offsetof(zend_op_array, opcodes)], r0
+ | mov64 r2, ((ptrdiff_t)opcodes)
+ | cmp aword [r1 + offsetof(zend_op_array, opcodes)], r2
|| } else {
| cmp aword [r1 + offsetof(zend_op_array, opcodes)], opcodes
|| }
} else {
| .if X64
|| if (!IS_SIGNED_32BIT(func)) {
- | mov64 r0, ((ptrdiff_t)func)
- | cmp aword EX:r1->func, r0
+ | mov64 r2, ((ptrdiff_t)func)
+ | cmp aword EX:r1->func, r2
|| } else {
| cmp aword EX:r1->func, func
|| }
zend_bool use_this,
zend_class_entry *trace_ce,
zend_jit_trace_rec *trace,
- zend_bool stack_check)
+ zend_bool stack_check,
+ zend_bool polymorphic_side_trace)
{
zend_func_info *info = ZEND_FUNC_INFO(op_array);
zend_call_info *call_info = NULL;
function_name = RT_CONSTANT(opline, opline->op2);
- if (opline->op1_type == IS_UNUSED || use_this) {
- zend_jit_addr this_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, offsetof(zend_execute_data, This));
+ if (info) {
+ call_info = info->callee_info;
+ while (call_info && call_info->caller_init_opline != opline) {
+ call_info = call_info->next_callee;
+ }
+ if (call_info && call_info->callee_func) {
+ func = call_info->callee_func;
+ }
+ }
- | GET_ZVAL_PTR FCARG1a, this_addr
+ if (polymorphic_side_trace) {
+ /* function is passed in r0 from parent_trace */
} else {
- if (op1_info & MAY_BE_REF) {
- if (opline->op1_type == IS_CV) {
- if (Z_REG(op1_addr) != ZREG_FCARG1a || Z_OFFSET(op1_addr) != 0) {
+ if (opline->op1_type == IS_UNUSED || use_this) {
+ zend_jit_addr this_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, offsetof(zend_execute_data, This));
+
+ | GET_ZVAL_PTR FCARG1a, this_addr
+ } else {
+ if (op1_info & MAY_BE_REF) {
+ if (opline->op1_type == IS_CV) {
+ 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);
+ } else {
+ /* Hack: Convert reference to regular value to simplify JIT code */
+ ZEND_ASSERT(Z_REG(op1_addr) == ZREG_FP);
+ | IF_NOT_ZVAL_TYPE op1_addr, IS_REFERENCE, >1
| LOAD_ZVAL_ADDR FCARG1a, op1_addr
+ | EXT_CALL zend_jit_unref_helper, r0
+ |1:
}
- | ZVAL_DEREF FCARG1a, op1_info
- op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1a, 0);
- } else {
- /* Hack: Convert reference to regular value to simplify JIT code */
- ZEND_ASSERT(Z_REG(op1_addr) == ZREG_FP);
- | IF_NOT_ZVAL_TYPE op1_addr, IS_REFERENCE, >1
- | LOAD_ZVAL_ADDR FCARG1a, op1_addr
- | EXT_CALL zend_jit_unref_helper, r0
- |1:
}
- }
- 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 (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:
- if (Z_REG(op1_addr) != ZREG_FCARG1a || Z_OFFSET(op1_addr) != 0) {
- | LOAD_ZVAL_ADDR FCARG1a, op1_addr
- }
- | SET_EX_OPLINE opline, r0
- if ((opline->op1_type & (IS_VAR|IS_TMP_VAR)) && !use_this) {
- | EXT_CALL zend_jit_invalid_method_call_tmp, r0
+ if (!exit_addr) {
+ return 0;
+ }
+ | IF_NOT_ZVAL_TYPE op1_addr, IS_OBJECT, &exit_addr
} else {
- | EXT_CALL zend_jit_invalid_method_call, r0
+ | IF_NOT_ZVAL_TYPE op1_addr, IS_OBJECT, >1
+ |.cold_code
+ |1:
+ if (Z_REG(op1_addr) != ZREG_FCARG1a || Z_OFFSET(op1_addr) != 0) {
+ | LOAD_ZVAL_ADDR FCARG1a, op1_addr
+ }
+ | SET_EX_OPLINE opline, r0
+ if ((opline->op1_type & (IS_VAR|IS_TMP_VAR)) && !use_this) {
+ | EXT_CALL zend_jit_invalid_method_call_tmp, r0
+ } else {
+ | EXT_CALL zend_jit_invalid_method_call, r0
+ }
+ | jmp ->exception_handler
+ |.code
}
- | jmp ->exception_handler
- |.code
}
+ | GET_ZVAL_PTR FCARG1a, op1_addr
}
- | GET_ZVAL_PTR FCARG1a, op1_addr
- }
- if (delayed_call_chain) {
- if (!zend_jit_save_call_chain(Dst, delayed_call_level)) {
- return 0;
+ if (delayed_call_chain) {
+ if (!zend_jit_save_call_chain(Dst, delayed_call_level)) {
+ return 0;
+ }
}
- }
- if (info) {
- call_info = info->callee_info;
- while (call_info && call_info->caller_init_opline != opline) {
- call_info = call_info->next_callee;
- }
- if (call_info && call_info->callee_func) {
- func = call_info->callee_func;
- }
- }
+ | mov aword T1, FCARG1a // save
- | mov aword T1, FCARG1a // save
+ if (func) {
+ | // fbc = CACHED_PTR(opline->result.num + sizeof(void*));
+ | mov r0, EX->run_time_cache
+ | mov r0, aword [r0 + opline->result.num + sizeof(void*)]
+ | test r0, r0
+ | jz >1
+ } else {
+ | // if (CACHED_PTR(opline->result.num) == obj->ce)) {
+ | mov r0, EX->run_time_cache
+ | mov r2, aword [r0 + opline->result.num]
+ | cmp r2, [FCARG1a + offsetof(zend_object, ce)]
+ | jnz >1
+ | // fbc = CACHED_PTR(opline->result.num + sizeof(void*));
+ | mov r0, aword [r0 + opline->result.num + sizeof(void*)]
+ }
- if (func) {
- | // fbc = CACHED_PTR(opline->result.num + sizeof(void*));
- | mov r0, EX->run_time_cache
- | mov r0, aword [r0 + opline->result.num + sizeof(void*)]
+ |.cold_code
+ |1:
+ | LOAD_ADDR FCARG2a, function_name
+ |.if X64
+ | lea CARG3, aword T1
+ |.else
+ | lea r0, aword T1
+ | sub r4, 12
+ | push r0
+ |.endif
+ | SET_EX_OPLINE opline, r0
+ if ((opline->op1_type & (IS_VAR|IS_TMP_VAR)) && !use_this) {
+ | EXT_CALL zend_jit_find_method_tmp_helper, r0
+ } else {
+ | EXT_CALL zend_jit_find_method_helper, r0
+ }
+ |.if not(X64)
+ | add r4, 12
+ |.endif
| test r0, r0
- | jz >1
- } else {
- | // if (CACHED_PTR(opline->result.num) == obj->ce)) {
- | mov r0, EX->run_time_cache
- | mov r2, aword [r0 + opline->result.num]
- | cmp r2, [FCARG1a + offsetof(zend_object, ce)]
- | jnz >1
- | // fbc = CACHED_PTR(opline->result.num + sizeof(void*));
- | mov r0, aword [r0 + opline->result.num + sizeof(void*)]
- }
-
- |.cold_code
- |1:
- | LOAD_ADDR FCARG2a, function_name
- |.if X64
- | lea CARG3, aword T1
- |.else
- | lea r0, aword T1
- | sub r4, 12
- | push r0
- |.endif
- | SET_EX_OPLINE opline, r0
- if ((opline->op1_type & (IS_VAR|IS_TMP_VAR)) && !use_this) {
- | EXT_CALL zend_jit_find_method_tmp_helper, r0
- } else {
- | EXT_CALL zend_jit_find_method_helper, r0
+ | jnz >2
+ | jmp ->exception_handler
+ |.code
+ |2:
}
- |.if not(X64)
- | add r4, 12
- |.endif
- | test r0, r0
- | jnz >2
- | jmp ->exception_handler
- |.code
- |2:
if (!func
&& trace