]> granicus.if.org Git - php/commitdiff
Improve tracing JIT for FETCH_THIS + FETCH_OBJ_*
authorDmitry Stogov <dmitry@zend.com>
Thu, 25 Jun 2020 14:33:53 +0000 (17:33 +0300)
committerDmitry Stogov <dmitry@zend.com>
Thu, 25 Jun 2020 14:33:53 +0000 (17:33 +0300)
ext/opcache/Optimizer/zend_ssa.h
ext/opcache/jit/zend_jit.c
ext/opcache/jit/zend_jit_trace.c
ext/opcache/jit/zend_jit_x86.dasc
ext/opcache/jit/zend_jit_x86.h

index 5439e1e12ccc70c67cad68e3568fe14edc72d9b4..7d065ac42ab4257055036c11e642d5e02c603f59 100644 (file)
@@ -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 {
index 53f47b7fac38d2c9d0a94a6ae725f191f97752dc..09c4691034cf686338ef29533cc76e286dcd6cac 100644 (file)
@@ -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;
index a2f0784ade0f65f7d7e8b47b05efa725337f31f7..bae77997178a9832646fee994c0ee10295ab9fd6 100644 (file)
@@ -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();
                        }
index f57d2ab26f0b083e50d7d0517206f3927aac3189..c5cf2b66602e5bf469d5962720670257618e4b65 100644 (file)
@@ -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;
 }
index 3368820d446e9b90ce7ab961dffef60ff0d4da93..0ebf7a2235c85fea22733a3de72632042da7bb54 100644 (file)
@@ -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,