typedef struct _zend_tssa {
zend_ssa ssa;
const zend_op **tssa_opcodes;
+ int used_stack;
} zend_tssa;
static const zend_op _nop_opcode = {0};
zend_jit_trace_stack *stack;
uint32_t build_flags = ZEND_SSA_RC_INFERENCE | ZEND_SSA_USE_CV_RESULTS;
uint32_t optimization_level = 0;
- int call_level, level, num_op_arrays;
+ int call_level, level, num_op_arrays, used_stack, max_used_stack;
size_t frame_size, stack_top, stack_size, stack_bottom;
zend_jit_op_array_trace_extension *jit_extension;
zend_ssa *ssa;
tssa->cfg.blocks[0].successors_count = 0;
tssa->cfg.blocks[0].predecessors_count = 0;
}
+ ((zend_tssa*)tssa)->used_stack = -1;
if (JIT_G(opt_level) < ZEND_JIT_LEVEL_INLINE) {
return tssa;
top = zend_jit_trace_call_frame(frame, op_array);
TRACE_FRAME_INIT(frame, op_array, 0, 0);
TRACE_FRAME_SET_RETURN_SSA_VAR(frame, -1);
+ frame->used_stack = 0;
for (i = 0; i < op_array->last_var + op_array->T; i++) {
SET_STACK_TYPE(frame->stack, i, IS_UNKNOWN, 1);
}
memset(&return_value_info, 0, sizeof(return_value_info));
+ if (trace_buffer->stop == ZEND_JIT_TRACE_STOP_LOOP) {
+ max_used_stack = used_stack = 0;
+ } else {
+ max_used_stack = used_stack = -1;
+ }
+
p = trace_buffer + ZEND_JIT_TRACE_START_REC_SIZE;
idx = 0;
level = 0;
ADD_OP2_TRACE_GUARD();
}
break;
+ case ZEND_SEND_ARRAY:
+ case ZEND_SEND_UNPACK:
+ case ZEND_CHECK_UNDEF_ARGS:
+ case ZEND_INCLUDE_OR_EVAL:
+ max_used_stack = used_stack = -1;
default:
break;
}
/* Trace missed INIT_FCALL opcode */
call = top;
TRACE_FRAME_INIT(call, op_array, 0, 0);
+ call->used_stack = 0;
top = zend_jit_trace_call_frame(top, op_array);
for (i = 0; i < op_array->last_var + op_array->T; i++) {
SET_STACK_TYPE(call->stack, i, IS_UNKNOWN, 1);
top = frame;
if (frame->prev) {
+ if (used_stack > 0) {
+ used_stack -= frame->used_stack;
+ }
frame = frame->prev;
ZEND_ASSERT(&frame->func->op_array == op_array);
} else {
+ max_used_stack = used_stack = -1;
frame = zend_jit_trace_ret_frame(frame, op_array);
TRACE_FRAME_INIT(frame, op_array, 0, 0);
TRACE_FRAME_SET_RETURN_SSA_VAR(frame, -1);
+ frame->used_stack = 0;
for (i = 0; i < op_array->last_var + op_array->T; i++) {
SET_STACK_TYPE(frame->stack, i, IS_UNKNOWN, 1);
}
call = top;
TRACE_FRAME_INIT(call, p->func, 0, 0);
call->prev = frame->call;
+ call->used_stack = 0;
frame->call = call;
top = zend_jit_trace_call_frame(top, p->op_array);
if (p->func && p->func->type == ZEND_USER_FUNCTION) {
SET_STACK_INFO(call->stack, i, -1);
}
}
+ if (used_stack >= 0
+ && !(p->info & ZEND_JIT_TRACE_FAKE_INIT_CALL)) {
+ if (p->func == NULL || (p-1)->op != ZEND_JIT_TRACE_VM) {
+ max_used_stack = used_stack = -1;
+ } else {
+ const zend_op *opline = (p-1)->opline;
+
+ switch (opline->opcode) {
+ case ZEND_INIT_FCALL:
+ case ZEND_INIT_FCALL_BY_NAME:
+ case ZEND_INIT_NS_FCALL_BY_NAME:
+ case ZEND_INIT_METHOD_CALL:
+ case ZEND_INIT_DYNAMIC_CALL:
+ //case ZEND_INIT_STATIC_METHOD_CALL:
+ //case ZEND_INIT_USER_CALL:
+ //case ZEND_NEW:
+ frame->used_stack = zend_vm_calc_used_stack(opline->extended_value, (zend_function*)p->func);
+ used_stack += frame->used_stack;
+ if (used_stack > max_used_stack) {
+ max_used_stack = used_stack;
+ }
+ break;
+ default:
+ max_used_stack = used_stack = -1;
+ }
+ }
+ }
} else if (p->op == ZEND_JIT_TRACE_DO_ICALL) {
call = frame->call;
if (call) {
}
}
+ ((zend_tssa*)tssa)->used_stack = max_used_stack;
+
if (trace_buffer->stop == ZEND_JIT_TRACE_STOP_LOOP
|| trace_buffer->stop == ZEND_JIT_TRACE_STOP_RECURSIVE_CALL
|| trace_buffer->stop == ZEND_JIT_TRACE_STOP_RECURSIVE_RET) {
zend_uchar res_type = IS_UNKNOWN;
const zend_op *opline, *orig_opline;
const zend_ssa_op *ssa_op, *orig_ssa_op;
+ int used_stack;
JIT_G(current_trace) = trace_buffer;
}
ssa_opcodes = ((zend_tssa*)ssa)->tssa_opcodes;
+ used_stack = ((zend_tssa*)ssa)->used_stack;
/* Register allocation */
if ((JIT_G(opt_flags) & (ZEND_JIT_REG_ALLOC_LOCAL|ZEND_JIT_REG_ALLOC_GLOBAL))
int parent_vars_count = 0;
zend_jit_trace_stack *parent_stack = NULL;
+ if (used_stack > 0) {
+ if (!zend_jit_stack_check(&dasm_state, opline, used_stack)) {
+ goto jit_failure;
+ }
+ }
+
if (parent_trace) {
parent_vars_count = MIN(zend_jit_traces[parent_trace].exit_info[exit_num].stack_size,
op_array->last_var + op_array->T);
case ZEND_INIT_FCALL:
case ZEND_INIT_FCALL_BY_NAME:
case ZEND_INIT_NS_FCALL_BY_NAME:
- if (!zend_jit_init_fcall(&dasm_state, opline, op_array_ssa->cfg.map ? op_array_ssa->cfg.map[opline - op_array->opcodes] : -1, op_array, ssa, ssa_op, frame->call_level, p + 1)) {
+ if (!zend_jit_init_fcall(&dasm_state, opline, op_array_ssa->cfg.map ? op_array_ssa->cfg.map[opline - op_array->opcodes] : -1, op_array, ssa, ssa_op, frame->call_level, p + 1, used_stack < 0)) {
goto jit_failure;
}
goto done;
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)) {
+ p + 1, used_stack < 0)) {
goto jit_failure;
}
goto done;
}
op2_info = OP2_INFO();
CHECK_OP2_TRACE_TYPE();
- if (!zend_jit_init_closure_call(&dasm_state, opline, op_array_ssa->cfg.map ? op_array_ssa->cfg.map[opline - op_array->opcodes] : -1, op_array, ssa, ssa_op, frame->call_level, p + 1)) {
+ if (!zend_jit_init_closure_call(&dasm_state, opline, op_array_ssa->cfg.map ? op_array_ssa->cfg.map[opline - op_array->opcodes] : -1, op_array, ssa, ssa_op, frame->call_level, p + 1, used_stack < 0)) {
goto jit_failure;
}
goto done;
zif_handler orig_internal_handler;
} zend_closure;
-static int zend_jit_push_call_frame(dasm_State **Dst, const zend_op *opline, zend_function *func, zend_bool is_closure, zend_bool use_this)
+static int zend_jit_stack_check(dasm_State **Dst, const zend_op *opline, uint32_t used_stack)
+{
+ 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;
+ }
+
+ | // Check Stack Overflow
+ | MEM_OP2_2_ZTS mov, r1, aword, executor_globals, vm_stack_end, r0
+ | MEM_OP2_2_ZTS sub, r1, aword, executor_globals, vm_stack_top, r0
+ | cmp r1, used_stack
+ | jb &exit_addr
+
+ return 1;
+}
+
+static int zend_jit_push_call_frame(dasm_State **Dst, const zend_op *opline, zend_function *func, zend_bool is_closure, zend_bool use_this, zend_bool stack_check)
{
uint32_t used_stack;
| // if (UNEXPECTED(used_stack > (size_t)(((char*)EG(vm_stack_end)) - (char*)call))) {
| MEM_OP2_2_ZTS mov, RX, aword, executor_globals, vm_stack_top, RX
- | // Check Stack Overflow
- | MEM_OP2_2_ZTS mov, r2, aword, executor_globals, vm_stack_end, r2
- | sub r2, RX
- if (func) {
- | cmp r2, used_stack
- } else {
- | cmp r2, FCARG1a
- }
-
- 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;
- }
- | jb &exit_addr
- } else {
- | jb >1
- | // EG(vm_stack_top) = (zval*)((char*)call + used_stack);
- |.cold_code
- |1:
+ if (stack_check) {
+ | // Check Stack Overflow
+ | MEM_OP2_2_ZTS mov, r2, aword, executor_globals, vm_stack_end, r2
+ | sub r2, RX
if (func) {
- | mov FCARG1d, used_stack
+ | cmp r2, used_stack
+ } else {
+ | cmp r2, FCARG1a
}
+
+ 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;
+ }
+
+ | jb &exit_addr
+ } else {
+ | jb >1
+ | // EG(vm_stack_top) = (zval*)((char*)call + used_stack);
+ |.cold_code
+ |1:
+ if (func) {
+ | mov FCARG1d, used_stack
+ }
#ifdef _WIN32
- if (0) {
+ if (0) {
#else
- if (func && func->type == ZEND_INTERNAL_FUNCTION) {
+ if (func && func->type == ZEND_INTERNAL_FUNCTION) {
#endif
- | SET_EX_OPLINE opline, r0
- | EXT_CALL zend_jit_int_extend_stack_helper, r0
- } else {
- if (!is_closure) {
- | mov FCARG2a, r0
+ | SET_EX_OPLINE opline, r0
+ | EXT_CALL zend_jit_int_extend_stack_helper, r0
} else {
- | lea FCARG2a, aword [r0 + offsetof(zend_closure, func)]
+ if (!is_closure) {
+ | mov FCARG2a, r0
+ } else {
+ | lea FCARG2a, aword [r0 + offsetof(zend_closure, func)]
+ }
+ | SET_EX_OPLINE opline, r0
+ | EXT_CALL zend_jit_extend_stack_helper, r0
}
- | SET_EX_OPLINE opline, r0
- | EXT_CALL zend_jit_extend_stack_helper, r0
+ | mov RX, r0
+ | jmp >1
+ |.code
}
- | mov RX, r0
- | jmp >1
- |.code
}
if (func) {
return 1;
}
-static int zend_jit_init_fcall(dasm_State **Dst, const zend_op *opline, uint32_t b, const zend_op_array *op_array, zend_ssa *ssa, const zend_ssa_op *ssa_op, int call_level, zend_jit_trace_rec *trace)
+static int zend_jit_init_fcall(dasm_State **Dst, const zend_op *opline, uint32_t b, const zend_op_array *op_array, zend_ssa *ssa, const zend_ssa_op *ssa_op, int call_level, zend_jit_trace_rec *trace, zend_bool stack_check)
{
zend_func_info *info = ZEND_FUNC_INFO(op_array);
zend_call_info *call_info = NULL;
|3:
}
- if (!zend_jit_push_call_frame(Dst, opline, func, 0, 0)) {
+ if (!zend_jit_push_call_frame(Dst, opline, func, 0, 0, stack_check)) {
return 0;
}
zend_bool ce_is_instanceof,
zend_bool use_this,
zend_class_entry *trace_ce,
- zend_jit_trace_rec *trace)
+ zend_jit_trace_rec *trace,
+ zend_bool stack_check)
{
zend_func_info *info = ZEND_FUNC_INFO(op_array);
zend_call_info *call_info = NULL;
}
if (!func || (func->common.fn_flags & ZEND_ACC_STATIC) == 0) {
- if (!zend_jit_push_call_frame(Dst, opline, func, 0, use_this)) {
+ if (!zend_jit_push_call_frame(Dst, opline, func, 0, use_this, stack_check)) {
return 0;
}
}
zend_ssa *ssa,
const zend_ssa_op *ssa_op,
int call_level,
- zend_jit_trace_rec *trace)
+ zend_jit_trace_rec *trace,
+ zend_bool stack_check)
{
zend_function *func = NULL;
zend_jit_addr op2_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->op2.var);
}
}
- if (!zend_jit_push_call_frame(Dst, opline, func, 1, 0)) {
+ if (!zend_jit_push_call_frame(Dst, opline, func, 1, 0, stack_check)) {
return 0;
}