Skip to content

Commit ddc4915

Browse files
authored
Improve JIT TRACE coverage (#16171)
Now it's possible that PHP tracing JIT loses some parts of the "hot" code. In case we have a root LOOP trace with an inlined call of some function, and we get a SIDE exit inside that function - we recorded a side trace, but finished it a the RETURN of the inlined function. As result the opcodes betwee RETURN from SIDE trace and LOOP exit were not covered by tracer and were executed in interpreter. This patch introduces a "ret_depth" argument that prevents stopping tracing on RETURN of such SIDE trace.
1 parent 3952a8f commit ddc4915

File tree

3 files changed

+55
-6
lines changed

3 files changed

+55
-6
lines changed

ext/opcache/jit/zend_jit_internal.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -647,7 +647,12 @@ ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_jit_ret_trace_helper(ZEND_OPCODE_HAND
647647
ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_jit_loop_trace_helper(ZEND_OPCODE_HANDLER_ARGS);
648648

649649
int ZEND_FASTCALL zend_jit_trace_hot_root(zend_execute_data *execute_data, const zend_op *opline);
650-
zend_jit_trace_stop ZEND_FASTCALL zend_jit_trace_execute(zend_execute_data *execute_data, const zend_op *opline, zend_jit_trace_rec *trace_buffer, uint8_t start, uint32_t is_megamorphc);
650+
zend_jit_trace_stop ZEND_FASTCALL zend_jit_trace_execute(zend_execute_data *execute_data,
651+
const zend_op *opline,
652+
zend_jit_trace_rec *trace_buffer,
653+
uint8_t start,
654+
uint32_t is_megamorphc,
655+
int ret_depth);
651656

652657
static zend_always_inline const zend_op* zend_jit_trace_get_exit_opline(zend_jit_trace_rec *trace, const zend_op *opline, bool *exit_if_true)
653658
{

ext/opcache/jit/zend_jit_trace.c

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8066,7 +8066,7 @@ int ZEND_FASTCALL zend_jit_trace_hot_root(zend_execute_data *execute_data, const
80668066

80678067
JIT_G(tracing) = 1;
80688068
stop = zend_jit_trace_execute(execute_data, opline, trace_buffer,
8069-
ZEND_OP_TRACE_INFO(opline, offset)->trace_flags & ZEND_JIT_TRACE_START_MASK, 0);
8069+
ZEND_OP_TRACE_INFO(opline, offset)->trace_flags & ZEND_JIT_TRACE_START_MASK, 0, 0);
80708070
JIT_G(tracing) = 0;
80718071

80728072
if (stop & ZEND_JIT_TRACE_HALT) {
@@ -8390,6 +8390,8 @@ int ZEND_FASTCALL zend_jit_trace_hot_side(zend_execute_data *execute_data, uint3
83908390
zend_jit_trace_rec trace_buffer[ZEND_JIT_TRACE_MAX_LENGTH];
83918391
uint32_t is_megamorphic = 0;
83928392
uint32_t polymorphism = 0;
8393+
uint32_t root;
8394+
int ret_depth = 0;
83938395

83948396
trace_num = ZEND_JIT_TRACE_NUM;
83958397

@@ -8414,7 +8416,8 @@ int ZEND_FASTCALL zend_jit_trace_hot_side(zend_execute_data *execute_data, uint3
84148416
goto abort;
84158417
}
84168418

8417-
if (zend_jit_traces[zend_jit_traces[parent_num].root].child_count >= JIT_G(max_side_traces)) {
8419+
root = zend_jit_traces[parent_num].root;
8420+
if (zend_jit_traces[root].child_count >= JIT_G(max_side_traces)) {
84188421
stop = ZEND_JIT_TRACE_STOP_TOO_MANY_CHILDREN;
84198422
goto abort;
84208423
}
@@ -8434,8 +8437,29 @@ int ZEND_FASTCALL zend_jit_trace_hot_side(zend_execute_data *execute_data, uint3
84348437
}
84358438
}
84368439

8440+
/* Check if this is a side trace of a root LOOP trace */
8441+
if ((zend_jit_traces[root].flags & ZEND_JIT_TRACE_LOOP)
8442+
&& zend_jit_traces[root].op_array != &EX(func)->op_array) {
8443+
const zend_op_array *op_array = zend_jit_traces[root].op_array;
8444+
const zend_op *opline = zend_jit_traces[root].opline;
8445+
zend_jit_op_array_trace_extension *jit_extension =
8446+
(zend_jit_op_array_trace_extension*)ZEND_FUNC_INFO(op_array);
8447+
8448+
if (jit_extension->trace_info[opline - op_array->opcodes].trace_flags & ZEND_JIT_TRACE_START_LOOP) {
8449+
zend_execute_data *ex = execute_data;
8450+
int n = 0;
8451+
do {
8452+
ex = ex->prev_execute_data;
8453+
n++;
8454+
} while (ex && zend_jit_traces[root].op_array != &ex->func->op_array);
8455+
if (ex && n <= ZEND_JIT_TRACE_MAX_RET_DEPTH) {
8456+
ret_depth = n;
8457+
}
8458+
}
8459+
}
8460+
84378461
JIT_G(tracing) = 1;
8438-
stop = zend_jit_trace_execute(execute_data, EX(opline), trace_buffer, ZEND_JIT_TRACE_START_SIDE, is_megamorphic);
8462+
stop = zend_jit_trace_execute(execute_data, EX(opline), trace_buffer, ZEND_JIT_TRACE_START_SIDE, is_megamorphic, ret_depth);
84398463
JIT_G(tracing) = 0;
84408464

84418465
if (stop & ZEND_JIT_TRACE_HALT) {

ext/opcache/jit/zend_jit_vm_helpers.c

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -575,7 +575,7 @@ static int zend_jit_trace_subtrace(zend_jit_trace_rec *trace_buffer, int start,
575575
* +--------+----------+----------+----------++----------+----------+----------+
576576
* | RETURN |INNER_LOOP| | rec-ret || LINK | | LINK |
577577
* +--------+----------+----------+----------++----------+----------+----------+
578-
* | SIDE | unroll | | return || LINK | LINK | LINK |
578+
* | SIDE | unroll | | side-ret || LINK | LINK | LINK |
579579
* +--------+----------+----------+----------++----------+----------+----------+
580580
*
581581
* loop: LOOP if "cycle" and level == 0, otherwise INNER_LOOP
@@ -586,10 +586,16 @@ static int zend_jit_trace_subtrace(zend_jit_trace_rec *trace_buffer, int start,
586586
* loop-ret: LOOP_EXIT if level == 0, otherwise continue (wait for loop)
587587
* return: RETURN if level == 0
588588
* rec_ret: RECURSIVE_RET if "cycle" and ret_level > N, otherwise continue
589+
* side_ret: RETURN if level == 0 && ret_level == ret_depth, otherwise continue
589590
*
590591
*/
591592

592-
zend_jit_trace_stop ZEND_FASTCALL zend_jit_trace_execute(zend_execute_data *ex, const zend_op *op, zend_jit_trace_rec *trace_buffer, uint8_t start, uint32_t is_megamorphic)
593+
zend_jit_trace_stop ZEND_FASTCALL zend_jit_trace_execute(zend_execute_data *ex,
594+
const zend_op *op,
595+
zend_jit_trace_rec *trace_buffer,
596+
uint8_t start,
597+
uint32_t is_megamorphic,
598+
int ret_depth)
593599

594600
{
595601
#ifdef HAVE_GCC_GLOBAL_REGS
@@ -1060,6 +1066,20 @@ zend_jit_trace_stop ZEND_FASTCALL zend_jit_trace_execute(zend_execute_data *ex,
10601066
ZEND_JIT_TRACE_STOP_RECURSION_EXIT) {
10611067
stop = ZEND_JIT_TRACE_STOP_RECURSION_EXIT;
10621068
break;
1069+
} else if ((start & ZEND_JIT_TRACE_START_SIDE)
1070+
&& ret_level < ret_depth) {
1071+
TRACE_RECORD(ZEND_JIT_TRACE_BACK, 0, op_array);
1072+
ret_level++;
1073+
last_loop_opline = NULL;
1074+
1075+
if (prev_call) {
1076+
int ret = zend_jit_trace_record_fake_init_call(prev_call, trace_buffer, idx, 0);
1077+
if (ret < 0) {
1078+
stop = ZEND_JIT_TRACE_STOP_BAD_FUNC;
1079+
break;
1080+
}
1081+
idx = ret;
1082+
}
10631083
} else {
10641084
stop = ZEND_JIT_TRACE_STOP_RETURN;
10651085
break;

0 commit comments

Comments
 (0)