From 1c56b40a5ce45f598d718cf34e8a265d44f1d9d4 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Tue, 1 Sep 2020 16:26:29 +0300 Subject: [PATCH] Eliminate some EX_CALL_INFO() checks --- ext/opcache/jit/zend_jit.c | 5 +- ext/opcache/jit/zend_jit_internal.h | 26 +++--- ext/opcache/jit/zend_jit_trace.c | 14 ++- ext/opcache/jit/zend_jit_x86.dasc | 130 ++++++++++++++++------------ 4 files changed, 108 insertions(+), 67 deletions(-) diff --git a/ext/opcache/jit/zend_jit.c b/ext/opcache/jit/zend_jit.c index 5254a39518..a89b21f875 100644 --- a/ext/opcache/jit/zend_jit.c +++ b/ext/opcache/jit/zend_jit.c @@ -2740,9 +2740,10 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op } } } - if (!zend_jit_leave_func(&dasm_state, op_array, NULL, NULL, 1)) { + if (!zend_jit_leave_func(&dasm_state, op_array, NULL, NULL, + (ssa->cfg.flags & ZEND_FUNC_INDIRECT_VAR_ACCESS) != 0, 1)) { goto jit_failure; - } + } } goto done; case ZEND_BOOL: diff --git a/ext/opcache/jit/zend_jit_internal.h b/ext/opcache/jit/zend_jit_internal.h index f471321991..1f4b36cb62 100644 --- a/ext/opcache/jit/zend_jit_internal.h +++ b/ext/opcache/jit/zend_jit_internal.h @@ -379,17 +379,18 @@ struct _zend_jit_trace_stack_frame { zend_jit_trace_stack stack[1]; }; -#define TRACE_FRAME_SHIFT_NUM_ARGS 16 -#define TRACE_FRAME_MAX_NUM_ARGS 32767 +#define TRACE_FRAME_SHIFT_NUM_ARGS 16 +#define TRACE_FRAME_MAX_NUM_ARGS 32767 -#define TRACE_FRAME_MASK_NUM_ARGS 0xffff0000 -#define TRACE_FRAME_MASK_NESTED 0x00000001 -#define TRACE_FRAME_MASK_LAST_SEND_BY_REF 0x00000002 -#define TRACE_FRAME_MASK_LAST_SEND_BY_VAL 0x00000004 -#define TRACE_FRAME_MASK_RETURN_VALUE_USED 0x00000008 -#define TRACE_FRAME_MASK_RETURN_VALUE_UNUSED 0x00000010 -#define TRACE_FRAME_MASK_THIS_CHECKED 0x00000020 -#define TRACE_FRAME_MASK_UNKNOWN_RETURN 0x00000040 +#define TRACE_FRAME_MASK_NUM_ARGS 0xffff0000 +#define TRACE_FRAME_MASK_NESTED 0x00000001 +#define TRACE_FRAME_MASK_LAST_SEND_BY_REF 0x00000002 +#define TRACE_FRAME_MASK_LAST_SEND_BY_VAL 0x00000004 +#define TRACE_FRAME_MASK_RETURN_VALUE_USED 0x00000008 +#define TRACE_FRAME_MASK_RETURN_VALUE_UNUSED 0x00000010 +#define TRACE_FRAME_MASK_THIS_CHECKED 0x00000020 +#define TRACE_FRAME_MASK_UNKNOWN_RETURN 0x00000040 +#define TRACE_FRAME_MASK_NO_NEED_RELEASE_THIS 0x00000080 #define TRACE_FRAME_INIT(frame, _func, _flags, num_args) do { \ @@ -421,6 +422,8 @@ struct _zend_jit_trace_stack_frame { ((frame)->_info & TRACE_FRAME_MASK_THIS_CHECKED) #define TRACE_FRAME_IS_UNKNOWN_RETURN(frame) \ ((frame)->_info & TRACE_FRAME_MASK_UNKNOWN_RETURN) +#define TRACE_FRAME_NO_NEED_REKEASE_THIS(frame) \ + ((frame)->_info & TRACE_FRAME_MASK_NO_NEED_RELEASE_THIS) #define TRACE_FRAME_SET_RETURN_SSA_VAR(frame, var) do { \ (frame)->_info = var; \ @@ -444,6 +447,9 @@ struct _zend_jit_trace_stack_frame { #define TRACE_FRAME_SET_THIS_CHECKED(frame) do { \ (frame)->_info |= TRACE_FRAME_MASK_THIS_CHECKED; \ } while (0) +#define TRACE_FRAME_SET_NO_NEED_RELEASE_THIS(frame) do { \ + (frame)->_info |= TRACE_FRAME_MASK_NO_NEED_RELEASE_THIS; \ + } while (0) ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_jit_func_trace_helper(ZEND_OPCODE_HANDLER_ARGS); ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_jit_ret_trace_helper(ZEND_OPCODE_HANDLER_ARGS); diff --git a/ext/opcache/jit/zend_jit_trace.c b/ext/opcache/jit/zend_jit_trace.c index d8ee688f0f..4b88a9d03e 100644 --- a/ext/opcache/jit/zend_jit_trace.c +++ b/ext/opcache/jit/zend_jit_trace.c @@ -4058,7 +4058,8 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par } } } - if (!zend_jit_leave_func(&dasm_state, op_array, p + 1, &zend_jit_traces[ZEND_JIT_TRACE_NUM], may_throw)) { + if (!zend_jit_leave_func(&dasm_state, op_array, p + 1, &zend_jit_traces[ZEND_JIT_TRACE_NUM], + (op_array_ssa->cfg.flags & ZEND_FUNC_INDIRECT_VAR_ACCESS) != 0, may_throw)) { goto jit_failure; } } @@ -4876,6 +4877,17 @@ done: if (!(p->info & ZEND_JIT_TRACE_FAKE_INIT_CALL)) { TRACE_FRAME_SET_LAST_SEND_BY_VAL(call); } + if (init_opline + && init_opline->opcode != ZEND_NEW + && (init_opline->opcode != ZEND_INIT_METHOD_CALL + || init_opline->op1_type == IS_UNDEF) + && (init_opline->opcode != ZEND_INIT_USER_CALL + || init_opline->op2_type == IS_CONST) /* no closure */ + && (init_opline->opcode != ZEND_INIT_DYNAMIC_CALL + || init_opline->op2_type == IS_CONST) /* no closure */ + ) { + TRACE_FRAME_SET_NO_NEED_RELEASE_THIS(call); + } frame->call = call; top = zend_jit_trace_call_frame(top, p->op_array); if (p->func) { diff --git a/ext/opcache/jit/zend_jit_x86.dasc b/ext/opcache/jit/zend_jit_x86.dasc index e6a46ad09f..16a5922344 100644 --- a/ext/opcache/jit/zend_jit_x86.dasc +++ b/ext/opcache/jit/zend_jit_x86.dasc @@ -10194,66 +10194,88 @@ static int zend_jit_free_op(dasm_State **Dst, const zend_op *opline, uint32_t in return 1; } -static int zend_jit_leave_func(dasm_State **Dst, const zend_op_array *op_array, zend_jit_trace_rec *trace, zend_jit_trace_info *trace_info, int may_throw) -{ - /* ZEND_CALL_FAKE_CLOSURE handled on slow path to eliminate check for ZEND_CALL_CLOSURE on fast path */ - | mov FCARG1d, dword [FP + offsetof(zend_execute_data, This.u1.type_info)] - | test FCARG1d, (ZEND_CALL_TOP|ZEND_CALL_HAS_SYMBOL_TABLE|ZEND_CALL_FREE_EXTRA_ARGS|ZEND_CALL_ALLOCATED|ZEND_CALL_HAS_EXTRA_NAMED_PARAMS|ZEND_CALL_FAKE_CLOSURE) - if (trace && trace->op != ZEND_JIT_TRACE_END) { - | jnz >1 - |.cold_code - |1: - if (!GCC_GLOBAL_REGS) { - | mov FCARG2a, FP - } - | EXT_CALL zend_jit_leave_func_helper, r0 +static int zend_jit_leave_func(dasm_State **Dst, const zend_op_array *op_array, zend_jit_trace_rec *trace, zend_jit_trace_info *trace_info, int indirect_var_access, int may_throw) +{ + zend_bool may_be_top_frame = + JIT_G(trigger) != ZEND_JIT_ON_HOT_TRACE || + !JIT_G(current_frame) || + !TRACE_FRAME_IS_NESTED(JIT_G(current_frame)); + zend_bool may_need_call_helper = + indirect_var_access || /* may have symbol table */ + !op_array->function_name || /* may have symbol table */ + may_be_top_frame || + (op_array->fn_flags & ZEND_ACC_VARIADIC) || /* may have extra named args */ + JIT_G(trigger) != ZEND_JIT_ON_HOT_TRACE || + !JIT_G(current_frame) || + TRACE_FRAME_NUM_ARGS(JIT_G(current_frame)) == -1 || /* unknown number of args */ + (uint32_t)TRACE_FRAME_NUM_ARGS(JIT_G(current_frame)) > op_array->num_args; /* extra args */ + zend_bool may_need_release_this = + !(op_array->fn_flags & ZEND_ACC_CLOSURE) && + op_array->scope && + !(op_array->fn_flags & ZEND_ACC_STATIC) && + (JIT_G(trigger) != ZEND_JIT_ON_HOT_TRACE || + !JIT_G(current_frame) || + !TRACE_FRAME_NO_NEED_REKEASE_THIS(JIT_G(current_frame))); + + if (may_need_call_helper || may_need_release_this) { + | mov FCARG1d, dword [FP + offsetof(zend_execute_data, This.u1.type_info)] + } + if (may_need_call_helper) { + /* ZEND_CALL_FAKE_CLOSURE handled on slow path to eliminate check for ZEND_CALL_CLOSURE on fast path */ + | test FCARG1d, (ZEND_CALL_TOP|ZEND_CALL_HAS_SYMBOL_TABLE|ZEND_CALL_FREE_EXTRA_ARGS|ZEND_CALL_ALLOCATED|ZEND_CALL_HAS_EXTRA_NAMED_PARAMS|ZEND_CALL_FAKE_CLOSURE) + if (trace && trace->op != ZEND_JIT_TRACE_END) { + | jnz >1 + |.cold_code + |1: + if (!GCC_GLOBAL_REGS) { + | mov FCARG2a, FP + } + | EXT_CALL zend_jit_leave_func_helper, r0 + + if (may_be_top_frame) { + // TODO: try to avoid this check ??? + if (zend_jit_vm_kind == ZEND_VM_KIND_HYBRID) { + | cmp IP, zend_jit_halt_op + | je ->trace_halt + } else if (GCC_GLOBAL_REGS) { + | test IP, IP + | je ->trace_halt + } else { + | test eax, eax + | jl ->trace_halt + } + } - if (JIT_G(trigger) != ZEND_JIT_ON_HOT_TRACE || - !JIT_G(current_frame) || - !TRACE_FRAME_IS_NESTED(JIT_G(current_frame))) { - // TODO: try to avoid this check ??? - if (zend_jit_vm_kind == ZEND_VM_KIND_HYBRID) { - | cmp IP, zend_jit_halt_op - | je ->trace_halt - } else if (GCC_GLOBAL_REGS) { - | test IP, IP - | je ->trace_halt - } else { - | test eax, eax - | jl ->trace_halt + if (!GCC_GLOBAL_REGS) { + | // execute_data = EG(current_execute_data) + | MEM_OP2_2_ZTS mov, FP, aword, executor_globals, current_execute_data, r0 } + | jmp >8 + |.code + } else { + | jnz ->leave_function_handler } + } - if (!GCC_GLOBAL_REGS) { - | // execute_data = EG(current_execute_data) - | MEM_OP2_2_ZTS mov, FP, aword, executor_globals, current_execute_data, r0 - } - | jmp >8 - |.code - } else { - | jnz ->leave_function_handler - } - - if ((op_array->scope && !(op_array->fn_flags & ZEND_ACC_STATIC)) || - (op_array->fn_flags & ZEND_ACC_CLOSURE)) { - if (op_array->fn_flags & ZEND_ACC_CLOSURE) { - | // OBJ_RELEASE(ZEND_CLOSURE_OBJECT(EX(func))); - | mov FCARG1a, EX->func - | sub FCARG1a, sizeof(zend_object) - | OBJ_RELEASE ZREG_FCARG1a, >4 - } else if (op_array->scope && !(op_array->fn_flags & ZEND_ACC_STATIC)) { - | // if (call_info & ZEND_CALL_RELEASE_THIS) - | test FCARG1d, ZEND_CALL_RELEASE_THIS - | je >4 - | // zend_object *object = Z_OBJ(execute_data->This); - | mov FCARG1a, EX->This.value.obj - | // OBJ_RELEASE(object); - | OBJ_RELEASE ZREG_FCARG1a, >4 - // TODO: avoid EG(excption) check for $this->foo() calls - may_throw = 1; - } + if (op_array->fn_flags & ZEND_ACC_CLOSURE) { + | // OBJ_RELEASE(ZEND_CLOSURE_OBJECT(EX(func))); + | mov FCARG1a, EX->func + | sub FCARG1a, sizeof(zend_object) + | OBJ_RELEASE ZREG_FCARG1a, >4 |4: + } else if (may_need_release_this) { + | // if (call_info & ZEND_CALL_RELEASE_THIS) + | test FCARG1d, ZEND_CALL_RELEASE_THIS + | je >4 + | // zend_object *object = Z_OBJ(execute_data->This); + | mov FCARG1a, EX->This.value.obj + | // OBJ_RELEASE(object); + | OBJ_RELEASE ZREG_FCARG1a, >4 + |4: + // TODO: avoid EG(excption) check for $this->foo() calls + may_throw = 1; } + | // EG(vm_stack_top) = (zval*)execute_data; | MEM_OP2_1_ZTS mov, aword, executor_globals, vm_stack_top, FP, r0 | // execute_data = EX(prev_execute_data); -- 2.40.0