From 50af4de0fefedd2363d542d816d8394b14aa6915 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Thu, 25 Jun 2020 17:33:53 +0300 Subject: [PATCH] Improve tracing JIT for FETCH_THIS + FETCH_OBJ_* --- ext/opcache/Optimizer/zend_ssa.h | 1 + ext/opcache/jit/zend_jit.c | 4 +- ext/opcache/jit/zend_jit_trace.c | 120 +++++++++++++++++++++++++----- ext/opcache/jit/zend_jit_x86.dasc | 29 +++++--- ext/opcache/jit/zend_jit_x86.h | 2 + 5 files changed, 128 insertions(+), 28 deletions(-) diff --git a/ext/opcache/Optimizer/zend_ssa.h b/ext/opcache/Optimizer/zend_ssa.h index 5439e1e12c..7d065ac42a 100644 --- a/ext/opcache/Optimizer/zend_ssa.h +++ b/ext/opcache/Optimizer/zend_ssa.h @@ -127,6 +127,7 @@ typedef struct _zend_ssa_var_info { unsigned int is_instanceof : 1; /* 0 - class == "ce", 1 - may be child of "ce" */ unsigned int recursive : 1; unsigned int use_as_double : 1; + unsigned int delayed_fetch_this : 1; } zend_ssa_var_info; typedef struct _zend_ssa { diff --git a/ext/opcache/jit/zend_jit.c b/ext/opcache/jit/zend_jit.c index 53f47b7fac..09c4691034 100644 --- a/ext/opcache/jit/zend_jit.c +++ b/ext/opcache/jit/zend_jit.c @@ -2791,7 +2791,7 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op } } if (!zend_jit_fetch_obj(&dasm_state, opline, op_array, - op1_info, op1_addr, 0, ce, ce_is_instanceof, + op1_info, op1_addr, 0, ce, ce_is_instanceof, 0, zend_may_throw(opline, ssa_op, op_array, ssa))) { goto jit_failure; } @@ -2844,7 +2844,7 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op } goto done; case ZEND_FETCH_THIS: - if (!zend_jit_fetch_this(&dasm_state, opline, op_array)) { + if (!zend_jit_fetch_this(&dasm_state, opline, op_array, 0)) { goto jit_failure; } goto done; diff --git a/ext/opcache/jit/zend_jit_trace.c b/ext/opcache/jit/zend_jit_trace.c index a2f0784ade..bae7799717 100644 --- a/ext/opcache/jit/zend_jit_trace.c +++ b/ext/opcache/jit/zend_jit_trace.c @@ -2612,6 +2612,51 @@ static void zend_jit_trace_setup_ret_counter(const zend_op *opline, size_t offse } } +static zend_bool zend_jit_may_delay_fetch_this(zend_ssa *ssa, const zend_op **ssa_opcodes, int var) +{ + int i; + int use = ssa->vars[var].use_chain; + const zend_op *opline; + + if (use < 0 + || ssa->vars[var].phi_use_chain + || ssa->ops[use].op1_use != var + || ssa->ops[use].op1_use_chain != -1) { + return 0; + } + + opline = ssa_opcodes[use]; + if (opline->opcode == ZEND_FETCH_OBJ_FUNC_ARG) { + if (!JIT_G(current_frame) + || !JIT_G(current_frame)->call + || !JIT_G(current_frame)->call->func + || !TRACE_FRAME_IS_LAST_SEND_BY_VAL(JIT_G(current_frame)->call)) { + return 0; + } + } else if (opline->opcode != ZEND_FETCH_OBJ_R + && opline->opcode != ZEND_FETCH_OBJ_IS + && opline->opcode != ZEND_FETCH_OBJ_W) { + return 0; + } + + if (opline->op2_type != IS_CONST + || Z_TYPE_P(RT_CONSTANT(opline, opline->op2)) != IS_STRING + || Z_STRVAL_P(RT_CONSTANT(opline, opline->op2))[0] == '\0') { + return 0; + } + + for (i = ssa->vars[var].definition; i < use; i++) { + if (ssa_opcodes[i]->opcode == ZEND_DO_UCALL + || ssa_opcodes[i]->opcode == ZEND_DO_FCALL_BY_NAME + || ssa_opcodes[i]->opcode == ZEND_DO_FCALL + || ssa_opcodes[i]->opcode == ZEND_INCLUDE_OR_EVAL) { + return 0; + } + } + + return 1; +} + static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t parent_trace, uint32_t exit_num) { const void *handler = NULL; @@ -2622,6 +2667,7 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par void *checkpoint; const zend_op_array *op_array; zend_ssa *ssa, *op_array_ssa; + const zend_op **ssa_opcodes; zend_jit_trace_rec *p; zend_jit_op_array_trace_extension *jit_extension; int num_op_arrays = 0; @@ -2634,6 +2680,7 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par zend_jit_addr op1_addr, op1_def_addr, op2_addr, op2_def_addr, res_addr; zend_class_entry *ce; zend_bool ce_is_instanceof; + zend_bool delayed_fetch_this = 0; uint32_t i; zend_jit_trace_stack_frame *frame, *top, *call; zend_jit_trace_stack *stack; @@ -2652,6 +2699,8 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par goto jit_cleanup; } + ssa_opcodes = ((zend_tssa*)ssa)->tssa_opcodes; + /* Register allocation */ if ((JIT_G(opt_flags) & (ZEND_JIT_REG_ALLOC_LOCAL|ZEND_JIT_REG_ALLOC_GLOBAL)) && JIT_G(opt_level) >= ZEND_JIT_LEVEL_INLINE) { @@ -2768,6 +2817,10 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par if (!zend_jit_store_var(&dasm_state, ssa->var_info[i].type, i, STACK_REG(parent_stack, i))) { goto jit_failure; } + } else if (STACK_REG(parent_stack, i) == ZREG_THIS) { + if (!zend_jit_load_this(&dasm_state, EX_NUM_TO_VAR(i))) { + goto jit_failure; + } } else { SET_STACK_REG(stack, i, ZREG_NONE); if (!zend_jit_store_const(&dasm_state, i, STACK_REG(parent_stack, i))) { @@ -3732,6 +3785,7 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par case ZEND_FETCH_OBJ_R: case ZEND_FETCH_OBJ_IS: case ZEND_FETCH_OBJ_W: + delayed_fetch_this = 0; if (opline->op2_type != IS_CONST || Z_TYPE_P(RT_CONSTANT(opline, opline->op2)) != IS_STRING || Z_STRVAL_P(RT_CONSTANT(opline, opline->op2))[0] == '\0') { @@ -3778,9 +3832,13 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par } } } + if (ssa_op->op1_use >= 0) { + delayed_fetch_this = ssa->var_info[ssa_op->op1_use].delayed_fetch_this; + } } if (!zend_jit_fetch_obj(&dasm_state, opline, op_array, op1_info, op1_addr, op1_indirect, ce, ce_is_instanceof, + delayed_fetch_this, zend_may_throw(opline, ssa_op, op_array, ssa))) { goto jit_failure; } @@ -3861,7 +3919,14 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par } goto done; case ZEND_FETCH_THIS: - if (!zend_jit_fetch_this(&dasm_state, opline, op_array)) { + delayed_fetch_this = 0; + if (ssa_op->result_def >= 0) { + if (zend_jit_may_delay_fetch_this(ssa, ssa_opcodes, ssa_op->result_def)) { + ssa->var_info[ssa_op->result_def].delayed_fetch_this = 1; + delayed_fetch_this = 1; + } + } + if (!zend_jit_fetch_this(&dasm_state, opline, op_array, delayed_fetch_this)) { goto jit_failure; } goto done; @@ -3942,6 +4007,11 @@ done: zend_jit_trace_clenup_stack(stack, opline, ssa_op, ssa, ra); } + if ((opline->op1_type & (IS_VAR|IS_TMP_VAR)) + && STACK_REG(stack, EX_VAR_TO_NUM(opline->op1.var)) == ZREG_THIS) { + SET_STACK_REG(stack, EX_VAR_TO_NUM(opline->op1.var), ZREG_NONE); + } + if (ssa_op) { /* Keep information about known types on abstract stack */ if (ssa_op->result_def >= 0) { @@ -3975,7 +4045,10 @@ done: SET_RES_STACK_VAR_TYPE(type); if (type != IS_UNKNOWN) { ssa->var_info[ssa_op->result_def].type &= ~MAY_BE_GUARD; - if (ra && ra[ssa_op->result_def]) { + if (opline->opcode == ZEND_FETCH_THIS + && delayed_fetch_this) { + SET_STACK_REG(stack, EX_VAR_TO_NUM(opline->result.var), ZREG_THIS); + } else if (ra && ra[ssa_op->result_def]) { SET_STACK_REG(stack, EX_VAR_TO_NUM(opline->result.var), ra[ssa_op->result_def]->reg); } } @@ -4323,21 +4396,23 @@ done: } } else if (p->stop == ZEND_JIT_TRACE_STOP_LINK || p->stop == ZEND_JIT_TRACE_STOP_INTERPRETER) { - if (ra) { - /* Generate code for trace deoptimization */ - int i; - - for (i = 0; i < op_array->last_var + op_array->T; i++) { - if (STACK_REG(stack, i) != ZREG_NONE) { - // TODO: optimize out useless stores ???? - if (STACK_REG(stack, i) < ZREG_NUM) { - if (!zend_jit_store_var(&dasm_state, 1 << STACK_TYPE(stack, i), i, STACK_REG(stack, i))) { - goto jit_failure; - } - } else { - if (!zend_jit_store_const(&dasm_state, i, STACK_REG(stack, i))) { - goto jit_failure; - } + /* Generate code for trace deoptimization */ + int i; + + for (i = 0; i < op_array->last_var + op_array->T; i++) { + if (STACK_REG(stack, i) != ZREG_NONE) { + // TODO: optimize out useless stores ???? + if (STACK_REG(stack, i) < ZREG_NUM) { + if (!zend_jit_store_var(&dasm_state, 1 << STACK_TYPE(stack, i), i, STACK_REG(stack, i))) { + goto jit_failure; + } + } else if (STACK_REG(stack, i) == ZREG_THIS) { + if (!zend_jit_load_this(&dasm_state, EX_NUM_TO_VAR(i))) { + goto jit_failure; + } + } else { + if (!zend_jit_store_const(&dasm_state, i, STACK_REG(stack, i))) { + goto jit_failure; } } } @@ -4484,6 +4559,10 @@ static const void *zend_jit_trace_exit_to_vm(uint32_t trace_num, uint32_t exit_n if (!zend_jit_store_var(&dasm_state, 1 << STACK_TYPE(stack, i), i, STACK_REG(stack, i))) { goto jit_failure; } + } else if (STACK_REG(stack, i) == ZREG_THIS) { + if (!zend_jit_load_this(&dasm_state, EX_NUM_TO_VAR(i))) { + goto jit_failure; + } } else { if (!zend_jit_store_const(&dasm_state, i, STACK_REG(stack, i))) { goto jit_failure; @@ -4904,6 +4983,8 @@ static void zend_jit_dump_exit_info(zend_jit_trace_info *t) 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); } @@ -5357,6 +5438,11 @@ int ZEND_FASTCALL zend_jit_trace_exit(uint32_t exit_num, zend_jit_registers_buf ZEND_UNREACHABLE(); } ZVAL_DOUBLE(EX_VAR_NUM(i), val); + } else if (STACK_REG(stack, i) == ZREG_THIS) { + zend_object *obj = Z_OBJ(EX(This)); + + GC_ADDREF(obj); + ZVAL_OBJ(EX_VAR_NUM(i), obj); } else { ZEND_UNREACHABLE(); } diff --git a/ext/opcache/jit/zend_jit_x86.dasc b/ext/opcache/jit/zend_jit_x86.dasc index f57d2ab26f..c5cf2b6660 100644 --- a/ext/opcache/jit/zend_jit_x86.dasc +++ b/ext/opcache/jit/zend_jit_x86.dasc @@ -10787,7 +10787,7 @@ static zend_bool zend_may_be_dynamic_property(zend_class_entry *ce, zend_string return 0; } -static int zend_jit_fetch_obj(dasm_State **Dst, const zend_op *opline, const zend_op_array *op_array, uint32_t op1_info, zend_jit_addr op1_addr, zend_bool op1_indirect, zend_class_entry *ce, zend_bool ce_is_instanceof, int may_throw) +static int zend_jit_fetch_obj(dasm_State **Dst, const zend_op *opline, const zend_op_array *op_array, uint32_t op1_info, zend_jit_addr op1_addr, zend_bool op1_indirect, zend_class_entry *ce, zend_bool ce_is_instanceof, zend_bool use_this, int may_throw) { zval *member; uint32_t offset; @@ -10804,7 +10804,7 @@ static int zend_jit_fetch_obj(dasm_State **Dst, const zend_op *opline, const zen ZEND_ASSERT(Z_TYPE_P(member) == IS_STRING && Z_STRVAL_P(member)[0] != '\0'); offset = zend_get_known_property_offset(ce, Z_STR_P(member), opline->op1_type == IS_UNUSED, op_array->filename, &prop_info); - if (opline->op1_type == IS_UNUSED) { + if (opline->op1_type == IS_UNUSED || use_this) { | GET_ZVAL_PTR FCARG1a, this_addr } else { if (opline->op1_type == IS_VAR @@ -11024,7 +11024,7 @@ static int zend_jit_fetch_obj(dasm_State **Dst, const zend_op *opline, const zen |.code; |9: // END - if (!op1_indirect) { + if (opline->op1_type != IS_UNUSED && !use_this && !op1_indirect) { if (opline->op1_type == IS_VAR && opline->opcode == ZEND_FETCH_OBJ_W) { zend_jit_addr orig_op1_addr = OP1_ADDR(); @@ -11174,10 +11174,20 @@ static int zend_jit_strlen(dasm_State **Dst, const zend_op *opline, const zend_o return 1; } -static int zend_jit_fetch_this(dasm_State **Dst, const zend_op *opline, const zend_op_array *op_array) +static int zend_jit_load_this(dasm_State **Dst, uint32_t var) { - zend_jit_addr res_addr = RES_ADDR(); + zend_jit_addr var_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, var); + + | mov r0, aword EX->This.value.ptr + | SET_ZVAL_PTR var_addr, r0 + | SET_ZVAL_TYPE_INFO var_addr, IS_OBJECT_EX + | GC_ADDREF r0 + + return 1; +} +static int zend_jit_fetch_this(dasm_State **Dst, const zend_op *opline, const zend_op_array *op_array, zend_bool check_only) +{ if (!op_array->scope || (op_array->fn_flags & ZEND_ACC_STATIC)) { if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) { if (!JIT_G(current_frame) || @@ -11205,10 +11215,11 @@ static int zend_jit_fetch_this(dasm_State **Dst, const zend_op *opline, const ze } } - | mov r0, aword EX->This.value.ptr - | SET_ZVAL_PTR res_addr, r0 - | SET_ZVAL_TYPE_INFO res_addr, IS_OBJECT_EX - | GC_ADDREF r0 + if (!check_only) { + if (!zend_jit_load_this(Dst, opline->result.var)) { + return 0; + } + } return 1; } diff --git a/ext/opcache/jit/zend_jit_x86.h b/ext/opcache/jit/zend_jit_x86.h index 3368820d44..0ebf7a2235 100644 --- a/ext/opcache/jit/zend_jit_x86.h +++ b/ext/opcache/jit/zend_jit_x86.h @@ -64,6 +64,8 @@ typedef enum _zend_reg { ZREG_NUM, + ZREG_THIS, /* used for delayed FETCH_THIS deoptimization */ + /* pseudo constants used by deoptimizer */ ZREG_LONG_MIN_MINUS_1, ZREG_LONG_MIN, -- 2.40.0