Skip to content

Commit 50af4de

Browse files
committed
Improve tracing JIT for FETCH_THIS + FETCH_OBJ_*
1 parent a1c6a7a commit 50af4de

File tree

5 files changed

+128
-28
lines changed

5 files changed

+128
-28
lines changed

ext/opcache/Optimizer/zend_ssa.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ typedef struct _zend_ssa_var_info {
127127
unsigned int is_instanceof : 1; /* 0 - class == "ce", 1 - may be child of "ce" */
128128
unsigned int recursive : 1;
129129
unsigned int use_as_double : 1;
130+
unsigned int delayed_fetch_this : 1;
130131
} zend_ssa_var_info;
131132

132133
typedef struct _zend_ssa {

ext/opcache/jit/zend_jit.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2791,7 +2791,7 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op
27912791
}
27922792
}
27932793
if (!zend_jit_fetch_obj(&dasm_state, opline, op_array,
2794-
op1_info, op1_addr, 0, ce, ce_is_instanceof,
2794+
op1_info, op1_addr, 0, ce, ce_is_instanceof, 0,
27952795
zend_may_throw(opline, ssa_op, op_array, ssa))) {
27962796
goto jit_failure;
27972797
}
@@ -2844,7 +2844,7 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op
28442844
}
28452845
goto done;
28462846
case ZEND_FETCH_THIS:
2847-
if (!zend_jit_fetch_this(&dasm_state, opline, op_array)) {
2847+
if (!zend_jit_fetch_this(&dasm_state, opline, op_array, 0)) {
28482848
goto jit_failure;
28492849
}
28502850
goto done;

ext/opcache/jit/zend_jit_trace.c

Lines changed: 103 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2612,6 +2612,51 @@ static void zend_jit_trace_setup_ret_counter(const zend_op *opline, size_t offse
26122612
}
26132613
}
26142614

2615+
static zend_bool zend_jit_may_delay_fetch_this(zend_ssa *ssa, const zend_op **ssa_opcodes, int var)
2616+
{
2617+
int i;
2618+
int use = ssa->vars[var].use_chain;
2619+
const zend_op *opline;
2620+
2621+
if (use < 0
2622+
|| ssa->vars[var].phi_use_chain
2623+
|| ssa->ops[use].op1_use != var
2624+
|| ssa->ops[use].op1_use_chain != -1) {
2625+
return 0;
2626+
}
2627+
2628+
opline = ssa_opcodes[use];
2629+
if (opline->opcode == ZEND_FETCH_OBJ_FUNC_ARG) {
2630+
if (!JIT_G(current_frame)
2631+
|| !JIT_G(current_frame)->call
2632+
|| !JIT_G(current_frame)->call->func
2633+
|| !TRACE_FRAME_IS_LAST_SEND_BY_VAL(JIT_G(current_frame)->call)) {
2634+
return 0;
2635+
}
2636+
} else if (opline->opcode != ZEND_FETCH_OBJ_R
2637+
&& opline->opcode != ZEND_FETCH_OBJ_IS
2638+
&& opline->opcode != ZEND_FETCH_OBJ_W) {
2639+
return 0;
2640+
}
2641+
2642+
if (opline->op2_type != IS_CONST
2643+
|| Z_TYPE_P(RT_CONSTANT(opline, opline->op2)) != IS_STRING
2644+
|| Z_STRVAL_P(RT_CONSTANT(opline, opline->op2))[0] == '\0') {
2645+
return 0;
2646+
}
2647+
2648+
for (i = ssa->vars[var].definition; i < use; i++) {
2649+
if (ssa_opcodes[i]->opcode == ZEND_DO_UCALL
2650+
|| ssa_opcodes[i]->opcode == ZEND_DO_FCALL_BY_NAME
2651+
|| ssa_opcodes[i]->opcode == ZEND_DO_FCALL
2652+
|| ssa_opcodes[i]->opcode == ZEND_INCLUDE_OR_EVAL) {
2653+
return 0;
2654+
}
2655+
}
2656+
2657+
return 1;
2658+
}
2659+
26152660
static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t parent_trace, uint32_t exit_num)
26162661
{
26172662
const void *handler = NULL;
@@ -2622,6 +2667,7 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par
26222667
void *checkpoint;
26232668
const zend_op_array *op_array;
26242669
zend_ssa *ssa, *op_array_ssa;
2670+
const zend_op **ssa_opcodes;
26252671
zend_jit_trace_rec *p;
26262672
zend_jit_op_array_trace_extension *jit_extension;
26272673
int num_op_arrays = 0;
@@ -2634,6 +2680,7 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par
26342680
zend_jit_addr op1_addr, op1_def_addr, op2_addr, op2_def_addr, res_addr;
26352681
zend_class_entry *ce;
26362682
zend_bool ce_is_instanceof;
2683+
zend_bool delayed_fetch_this = 0;
26372684
uint32_t i;
26382685
zend_jit_trace_stack_frame *frame, *top, *call;
26392686
zend_jit_trace_stack *stack;
@@ -2652,6 +2699,8 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par
26522699
goto jit_cleanup;
26532700
}
26542701

2702+
ssa_opcodes = ((zend_tssa*)ssa)->tssa_opcodes;
2703+
26552704
/* Register allocation */
26562705
if ((JIT_G(opt_flags) & (ZEND_JIT_REG_ALLOC_LOCAL|ZEND_JIT_REG_ALLOC_GLOBAL))
26572706
&& 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
27682817
if (!zend_jit_store_var(&dasm_state, ssa->var_info[i].type, i, STACK_REG(parent_stack, i))) {
27692818
goto jit_failure;
27702819
}
2820+
} else if (STACK_REG(parent_stack, i) == ZREG_THIS) {
2821+
if (!zend_jit_load_this(&dasm_state, EX_NUM_TO_VAR(i))) {
2822+
goto jit_failure;
2823+
}
27712824
} else {
27722825
SET_STACK_REG(stack, i, ZREG_NONE);
27732826
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
37323785
case ZEND_FETCH_OBJ_R:
37333786
case ZEND_FETCH_OBJ_IS:
37343787
case ZEND_FETCH_OBJ_W:
3788+
delayed_fetch_this = 0;
37353789
if (opline->op2_type != IS_CONST
37363790
|| Z_TYPE_P(RT_CONSTANT(opline, opline->op2)) != IS_STRING
37373791
|| 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
37783832
}
37793833
}
37803834
}
3835+
if (ssa_op->op1_use >= 0) {
3836+
delayed_fetch_this = ssa->var_info[ssa_op->op1_use].delayed_fetch_this;
3837+
}
37813838
}
37823839
if (!zend_jit_fetch_obj(&dasm_state, opline, op_array,
37833840
op1_info, op1_addr, op1_indirect, ce, ce_is_instanceof,
3841+
delayed_fetch_this,
37843842
zend_may_throw(opline, ssa_op, op_array, ssa))) {
37853843
goto jit_failure;
37863844
}
@@ -3861,7 +3919,14 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par
38613919
}
38623920
goto done;
38633921
case ZEND_FETCH_THIS:
3864-
if (!zend_jit_fetch_this(&dasm_state, opline, op_array)) {
3922+
delayed_fetch_this = 0;
3923+
if (ssa_op->result_def >= 0) {
3924+
if (zend_jit_may_delay_fetch_this(ssa, ssa_opcodes, ssa_op->result_def)) {
3925+
ssa->var_info[ssa_op->result_def].delayed_fetch_this = 1;
3926+
delayed_fetch_this = 1;
3927+
}
3928+
}
3929+
if (!zend_jit_fetch_this(&dasm_state, opline, op_array, delayed_fetch_this)) {
38653930
goto jit_failure;
38663931
}
38673932
goto done;
@@ -3942,6 +4007,11 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par
39424007
zend_jit_trace_clenup_stack(stack, opline, ssa_op, ssa, ra);
39434008
}
39444009

4010+
if ((opline->op1_type & (IS_VAR|IS_TMP_VAR))
4011+
&& STACK_REG(stack, EX_VAR_TO_NUM(opline->op1.var)) == ZREG_THIS) {
4012+
SET_STACK_REG(stack, EX_VAR_TO_NUM(opline->op1.var), ZREG_NONE);
4013+
}
4014+
39454015
if (ssa_op) {
39464016
/* Keep information about known types on abstract stack */
39474017
if (ssa_op->result_def >= 0) {
@@ -3975,7 +4045,10 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par
39754045
SET_RES_STACK_VAR_TYPE(type);
39764046
if (type != IS_UNKNOWN) {
39774047
ssa->var_info[ssa_op->result_def].type &= ~MAY_BE_GUARD;
3978-
if (ra && ra[ssa_op->result_def]) {
4048+
if (opline->opcode == ZEND_FETCH_THIS
4049+
&& delayed_fetch_this) {
4050+
SET_STACK_REG(stack, EX_VAR_TO_NUM(opline->result.var), ZREG_THIS);
4051+
} else if (ra && ra[ssa_op->result_def]) {
39794052
SET_STACK_REG(stack, EX_VAR_TO_NUM(opline->result.var), ra[ssa_op->result_def]->reg);
39804053
}
39814054
}
@@ -4323,21 +4396,23 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par
43234396
}
43244397
} else if (p->stop == ZEND_JIT_TRACE_STOP_LINK
43254398
|| p->stop == ZEND_JIT_TRACE_STOP_INTERPRETER) {
4326-
if (ra) {
4327-
/* Generate code for trace deoptimization */
4328-
int i;
4329-
4330-
for (i = 0; i < op_array->last_var + op_array->T; i++) {
4331-
if (STACK_REG(stack, i) != ZREG_NONE) {
4332-
// TODO: optimize out useless stores ????
4333-
if (STACK_REG(stack, i) < ZREG_NUM) {
4334-
if (!zend_jit_store_var(&dasm_state, 1 << STACK_TYPE(stack, i), i, STACK_REG(stack, i))) {
4335-
goto jit_failure;
4336-
}
4337-
} else {
4338-
if (!zend_jit_store_const(&dasm_state, i, STACK_REG(stack, i))) {
4339-
goto jit_failure;
4340-
}
4399+
/* Generate code for trace deoptimization */
4400+
int i;
4401+
4402+
for (i = 0; i < op_array->last_var + op_array->T; i++) {
4403+
if (STACK_REG(stack, i) != ZREG_NONE) {
4404+
// TODO: optimize out useless stores ????
4405+
if (STACK_REG(stack, i) < ZREG_NUM) {
4406+
if (!zend_jit_store_var(&dasm_state, 1 << STACK_TYPE(stack, i), i, STACK_REG(stack, i))) {
4407+
goto jit_failure;
4408+
}
4409+
} else if (STACK_REG(stack, i) == ZREG_THIS) {
4410+
if (!zend_jit_load_this(&dasm_state, EX_NUM_TO_VAR(i))) {
4411+
goto jit_failure;
4412+
}
4413+
} else {
4414+
if (!zend_jit_store_const(&dasm_state, i, STACK_REG(stack, i))) {
4415+
goto jit_failure;
43414416
}
43424417
}
43434418
}
@@ -4484,6 +4559,10 @@ static const void *zend_jit_trace_exit_to_vm(uint32_t trace_num, uint32_t exit_n
44844559
if (!zend_jit_store_var(&dasm_state, 1 << STACK_TYPE(stack, i), i, STACK_REG(stack, i))) {
44854560
goto jit_failure;
44864561
}
4562+
} else if (STACK_REG(stack, i) == ZREG_THIS) {
4563+
if (!zend_jit_load_this(&dasm_state, EX_NUM_TO_VAR(i))) {
4564+
goto jit_failure;
4565+
}
44874566
} else {
44884567
if (!zend_jit_store_const(&dasm_state, i, STACK_REG(stack, i))) {
44894568
goto jit_failure;
@@ -4904,6 +4983,8 @@ static void zend_jit_dump_exit_info(zend_jit_trace_info *t)
49044983
if (STACK_REG(stack, j) != ZREG_NONE) {
49054984
if (STACK_REG(stack, j) < ZREG_NUM) {
49064985
fprintf(stderr, "(%s)", zend_reg_name[STACK_REG(stack, j)]);
4986+
} else if (STACK_REG(stack, j) == ZREG_THIS) {
4987+
fprintf(stderr, "(this)");
49074988
} else {
49084989
fprintf(stderr, "(const_%d)", STACK_REG(stack, j) - ZREG_NUM);
49094990
}
@@ -5357,6 +5438,11 @@ int ZEND_FASTCALL zend_jit_trace_exit(uint32_t exit_num, zend_jit_registers_buf
53575438
ZEND_UNREACHABLE();
53585439
}
53595440
ZVAL_DOUBLE(EX_VAR_NUM(i), val);
5441+
} else if (STACK_REG(stack, i) == ZREG_THIS) {
5442+
zend_object *obj = Z_OBJ(EX(This));
5443+
5444+
GC_ADDREF(obj);
5445+
ZVAL_OBJ(EX_VAR_NUM(i), obj);
53605446
} else {
53615447
ZEND_UNREACHABLE();
53625448
}

ext/opcache/jit/zend_jit_x86.dasc

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10787,7 +10787,7 @@ static zend_bool zend_may_be_dynamic_property(zend_class_entry *ce, zend_string
1078710787
return 0;
1078810788
}
1078910789

10790-
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)
10790+
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)
1079110791
{
1079210792
zval *member;
1079310793
uint32_t offset;
@@ -10804,7 +10804,7 @@ static int zend_jit_fetch_obj(dasm_State **Dst, const zend_op *opline, const zen
1080410804
ZEND_ASSERT(Z_TYPE_P(member) == IS_STRING && Z_STRVAL_P(member)[0] != '\0');
1080510805
offset = zend_get_known_property_offset(ce, Z_STR_P(member), opline->op1_type == IS_UNUSED, op_array->filename, &prop_info);
1080610806

10807-
if (opline->op1_type == IS_UNUSED) {
10807+
if (opline->op1_type == IS_UNUSED || use_this) {
1080810808
| GET_ZVAL_PTR FCARG1a, this_addr
1080910809
} else {
1081010810
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
1102411024

1102511025
|.code;
1102611026
|9: // END
11027-
if (!op1_indirect) {
11027+
if (opline->op1_type != IS_UNUSED && !use_this && !op1_indirect) {
1102811028
if (opline->op1_type == IS_VAR
1102911029
&& opline->opcode == ZEND_FETCH_OBJ_W) {
1103011030
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
1117411174
return 1;
1117511175
}
1117611176

11177-
static int zend_jit_fetch_this(dasm_State **Dst, const zend_op *opline, const zend_op_array *op_array)
11177+
static int zend_jit_load_this(dasm_State **Dst, uint32_t var)
1117811178
{
11179-
zend_jit_addr res_addr = RES_ADDR();
11179+
zend_jit_addr var_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, var);
11180+
11181+
| mov r0, aword EX->This.value.ptr
11182+
| SET_ZVAL_PTR var_addr, r0
11183+
| SET_ZVAL_TYPE_INFO var_addr, IS_OBJECT_EX
11184+
| GC_ADDREF r0
11185+
11186+
return 1;
11187+
}
1118011188

11189+
static int zend_jit_fetch_this(dasm_State **Dst, const zend_op *opline, const zend_op_array *op_array, zend_bool check_only)
11190+
{
1118111191
if (!op_array->scope || (op_array->fn_flags & ZEND_ACC_STATIC)) {
1118211192
if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) {
1118311193
if (!JIT_G(current_frame) ||
@@ -11205,10 +11215,11 @@ static int zend_jit_fetch_this(dasm_State **Dst, const zend_op *opline, const ze
1120511215
}
1120611216
}
1120711217

11208-
| mov r0, aword EX->This.value.ptr
11209-
| SET_ZVAL_PTR res_addr, r0
11210-
| SET_ZVAL_TYPE_INFO res_addr, IS_OBJECT_EX
11211-
| GC_ADDREF r0
11218+
if (!check_only) {
11219+
if (!zend_jit_load_this(Dst, opline->result.var)) {
11220+
return 0;
11221+
}
11222+
}
1121211223

1121311224
return 1;
1121411225
}

ext/opcache/jit/zend_jit_x86.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,8 @@ typedef enum _zend_reg {
6464

6565
ZREG_NUM,
6666

67+
ZREG_THIS, /* used for delayed FETCH_THIS deoptimization */
68+
6769
/* pseudo constants used by deoptimizer */
6870
ZREG_LONG_MIN_MINUS_1,
6971
ZREG_LONG_MIN,

0 commit comments

Comments
 (0)