Skip to content

Commit 161ee11

Browse files
committed
Tracing JIT support for delayed call chain
1 parent 9198faa commit 161ee11

File tree

4 files changed

+112
-16
lines changed

4 files changed

+112
-16
lines changed

ext/opcache/jit/zend_jit.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ static const void *zend_jit_loop_counter_handler = NULL;
112112
static int zend_may_overflow(const zend_op *opline, const zend_op_array *op_array, zend_ssa *ssa);
113113
static void ZEND_FASTCALL zend_runtime_jit(void);
114114

115+
static int zend_jit_trace_op_len(const zend_op *opline);
115116
static int zend_jit_trace_may_exit(const zend_op_array *op_array, const zend_op *opline, zend_jit_trace_rec *trace);
116117
static uint32_t zend_jit_trace_get_exit_point(const zend_op *from_opline, const zend_op *to_opline, zend_jit_trace_rec *trace, uint32_t flags);
117118
static const void *zend_jit_trace_get_exit_addr(uint32_t n);
@@ -2460,7 +2461,7 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op
24602461
case ZEND_INIT_FCALL:
24612462
case ZEND_INIT_FCALL_BY_NAME:
24622463
case ZEND_INIT_NS_FCALL_BY_NAME:
2463-
if (!zend_jit_init_fcall(&dasm_state, opline, b, op_array, ssa, call_level, NULL)) {
2464+
if (!zend_jit_init_fcall(&dasm_state, opline, b, op_array, ssa, ssa_op, call_level, NULL)) {
24642465
goto jit_failure;
24652466
}
24662467
goto done;

ext/opcache/jit/zend_jit_internal.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,7 @@ typedef enum _zend_jit_trace_stop {
225225
#define ZEND_JIT_EXIT_JITED (1<<0)
226226
#define ZEND_JIT_EXIT_BLACKLISTED (1<<1)
227227
#define ZEND_JIT_EXIT_TO_VM (1<<2) /* exit to VM without attempt to create a side trace */
228+
#define ZEND_JIT_EXIT_RESTORE_CALL (1<<3) /* deoptimizer should restore EX(call) chain */
228229

229230
typedef union _zend_op_trace_info {
230231
zend_op dummy; /* the size of this structure must be the same as zend_op */

ext/opcache/jit/zend_jit_trace.c

Lines changed: 35 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,10 @@ static uint32_t zend_jit_trace_get_exit_point(const zend_op *from_opline, const
136136
uint32_t stack_size;
137137
zend_jit_trace_stack *stack = NULL;
138138

139+
if (delayed_call_chain) {
140+
assert(to_opline != NULL); /* CALL and IP share the same register */
141+
flags |= ZEND_JIT_EXIT_RESTORE_CALL;
142+
}
139143
if (JIT_G(current_frame)) {
140144
op_array = &JIT_G(current_frame)->func->op_array;
141145
stack_size = op_array->last_var + op_array->T;
@@ -2582,16 +2586,24 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par
25822586
}
25832587
}
25842588

2585-
// TODO: Merge two loops implementing parallel move ???
2586-
for (i = 0; i < parent_vars_count; i++) {
2587-
if (STACK_REG(parent_stack, i) != ZREG_NONE) {
2588-
if (ra && ra[i] && ra[i]->reg == STACK_REG(parent_stack, i)) {
2589-
/* register already loaded by parent trace */
2590-
SET_STACK_REG(stack, i, ra[i]->reg);
2591-
} else if (!zend_jit_store_var(&dasm_state, ssa->var_info[i].type, i, STACK_REG(parent_stack, i))) {
2592-
goto jit_failure;
2589+
if (parent_trace) {
2590+
/* Deoptimization */
2591+
2592+
// TODO: Merge this loop with the following LOAD loop to implement parallel move ???
2593+
for (i = 0; i < parent_vars_count; i++) {
2594+
if (STACK_REG(parent_stack, i) != ZREG_NONE) {
2595+
if (ra && ra[i] && ra[i]->reg == STACK_REG(parent_stack, i)) {
2596+
/* register already loaded by parent trace */
2597+
SET_STACK_REG(stack, i, ra[i]->reg);
2598+
} else if (!zend_jit_store_var(&dasm_state, ssa->var_info[i].type, i, STACK_REG(parent_stack, i))) {
2599+
goto jit_failure;
2600+
}
25932601
}
25942602
}
2603+
2604+
if (zend_jit_traces[parent_trace].exit_info[exit_num].flags & ZEND_JIT_EXIT_RESTORE_CALL) {
2605+
zend_jit_save_call_chain(&dasm_state, -1);
2606+
}
25952607
}
25962608

25972609
if (ra
@@ -3069,7 +3081,7 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par
30693081
case ZEND_INIT_FCALL:
30703082
case ZEND_INIT_FCALL_BY_NAME:
30713083
case ZEND_INIT_NS_FCALL_BY_NAME:
3072-
if (!zend_jit_init_fcall(&dasm_state, opline, op_array_ssa->cfg.map ? op_array_ssa->cfg.map[opline - op_array->opcodes] : -1, op_array, op_array_ssa, frame->call_level, p + 1)) {
3084+
if (!zend_jit_init_fcall(&dasm_state, opline, op_array_ssa->cfg.map ? op_array_ssa->cfg.map[opline - op_array->opcodes] : -1, op_array, ssa, ssa_op, frame->call_level, p + 1)) {
30733085
goto jit_failure;
30743086
}
30753087
goto done;
@@ -4094,10 +4106,11 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par
40944106
static int zend_jit_trace_exit_needs_deoptimization(uint32_t trace_num, uint32_t exit_num)
40954107
{
40964108
const zend_op *opline = zend_jit_traces[trace_num].exit_info[exit_num].opline;
4109+
uint32_t flags = zend_jit_traces[trace_num].exit_info[exit_num].flags;
40974110
uint32_t stack_size;
40984111
zend_jit_trace_stack *stack;
40994112

4100-
if (opline) {
4113+
if (opline || (flags & ZEND_JIT_EXIT_RESTORE_CALL)) {
41014114
return 1;
41024115
}
41034116

@@ -4133,6 +4146,9 @@ static const void *zend_jit_trace_exit_to_vm(uint32_t trace_num, uint32_t exit_n
41334146
zend_jit_align_func(&dasm_state);
41344147

41354148
/* Deoptimization */
4149+
if (zend_jit_traces[trace_num].exit_info[exit_num].flags & ZEND_JIT_EXIT_RESTORE_CALL) {
4150+
zend_jit_save_call_chain(&dasm_state, -1);
4151+
}
41364152
stack_size = zend_jit_traces[trace_num].exit_info[exit_num].stack_size;
41374153
stack = zend_jit_traces[trace_num].stack_map + zend_jit_traces[trace_num].exit_info[exit_num].stack_offset;
41384154
for (i = 0; i < stack_size; i++) {
@@ -4535,6 +4551,9 @@ static void zend_jit_dump_exit_info(zend_jit_trace_info *t)
45354551
if (t->exit_info[i].flags & ZEND_JIT_EXIT_TO_VM) {
45364552
fprintf(stderr, "/VM");
45374553
}
4554+
if (t->exit_info[i].flags & ZEND_JIT_EXIT_RESTORE_CALL) {
4555+
fprintf(stderr, "/CALL");
4556+
}
45384557
for (j = 0; j < stack_size; j++) {
45394558
zend_uchar type = STACK_TYPE(stack, j);
45404559
if (type != IS_UNKNOWN) {
@@ -4956,6 +4975,12 @@ int ZEND_FASTCALL zend_jit_trace_exit(uint32_t exit_num, zend_jit_registers_buf
49564975
uint32_t stack_size = t->exit_info[exit_num].stack_size;
49574976
zend_jit_trace_stack *stack = t->stack_map + t->exit_info[exit_num].stack_offset;
49584977

4978+
if (t->exit_info[exit_num].flags & ZEND_JIT_EXIT_RESTORE_CALL) {
4979+
zend_execute_data *call = (zend_execute_data *)regs->r[ZREG_RX];
4980+
call->prev_execute_data = EX(call);
4981+
EX(call) = call;
4982+
}
4983+
49594984
for (i = 0; i < stack_size; i++) {
49604985
if (STACK_REG(stack, i) != ZREG_NONE) {
49614986
if (STACK_TYPE(stack, i) == IS_LONG) {

ext/opcache/jit/zend_jit_x86.dasc

Lines changed: 74 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2328,6 +2328,7 @@ static int zend_jit_trace_exit_stub(dasm_State **Dst)
23282328
| // Save CPU registers
23292329
|.if X64
23302330
| sub r4, 16*8+16*8-8 /* CPU regs + SSE regs */
2331+
| mov aword [r4+15*8], r15
23312332
| mov aword [r4+11*8], r11
23322333
| mov aword [r4+10*8], r10
23332334
| mov aword [r4+9*8], r9
@@ -2357,6 +2358,7 @@ static int zend_jit_trace_exit_stub(dasm_State **Dst)
23572358
| movsd qword [r4+16*8+0*8], xmm0
23582359
|.else
23592360
| sub r4, 8*4+8*8-4 /* CPU regs + SSE regs */
2361+
| mov aword [r4+7*4], edi
23602362
| mov aword [r4+2*4], edx
23612363
| mov aword [r4+1*4], ecx
23622364
| mov aword [r4+0*4], eax
@@ -7673,18 +7675,83 @@ static int zend_jit_push_call_frame(dasm_State **Dst, const zend_op *opline, con
76737675
return 1;
76747676
}
76757677

7676-
static int zend_jit_needs_call_chain(zend_call_info *call_info, uint32_t b, const zend_op_array *op_array, zend_ssa *ssa, const zend_op *opline)
7678+
static int zend_jit_needs_call_chain(zend_call_info *call_info, uint32_t b, const zend_op_array *op_array, zend_ssa *ssa, const zend_ssa_op *ssa_op, const zend_op *opline, zend_jit_trace_rec *trace)
76777679
{
76787680
int skip;
76797681

7682+
if (trace) {
7683+
zend_jit_trace_rec *p = trace;
7684+
7685+
ssa_op++;
7686+
while (1) {
7687+
if (p->op == ZEND_JIT_TRACE_VM) {
7688+
switch (p->opline->opcode) {
7689+
case ZEND_SEND_ARRAY:
7690+
case ZEND_SEND_USER:
7691+
case ZEND_SEND_UNPACK:
7692+
case ZEND_INIT_FCALL:
7693+
case ZEND_INIT_METHOD_CALL:
7694+
case ZEND_INIT_STATIC_METHOD_CALL:
7695+
case ZEND_INIT_FCALL_BY_NAME:
7696+
case ZEND_INIT_NS_FCALL_BY_NAME:
7697+
case ZEND_INIT_DYNAMIC_CALL:
7698+
case ZEND_NEW:
7699+
case ZEND_INIT_USER_CALL:
7700+
case ZEND_FAST_CALL:
7701+
case ZEND_JMP:
7702+
case ZEND_JMPZNZ:
7703+
case ZEND_JMPZ:
7704+
case ZEND_JMPNZ:
7705+
case ZEND_JMPZ_EX:
7706+
case ZEND_JMPNZ_EX:
7707+
case ZEND_FE_RESET_R:
7708+
case ZEND_FE_RESET_RW:
7709+
case ZEND_JMP_SET:
7710+
case ZEND_COALESCE:
7711+
case ZEND_ASSERT_CHECK:
7712+
case ZEND_CATCH:
7713+
case ZEND_DECLARE_ANON_CLASS:
7714+
case ZEND_FE_FETCH_R:
7715+
case ZEND_FE_FETCH_RW:
7716+
return 1;
7717+
case ZEND_DO_ICALL:
7718+
case ZEND_DO_UCALL:
7719+
case ZEND_DO_FCALL_BY_NAME:
7720+
case ZEND_DO_FCALL:
7721+
return 0;
7722+
case ZEND_SEND_VAL:
7723+
case ZEND_SEND_VAR:
7724+
case ZEND_SEND_VAL_EX:
7725+
case ZEND_SEND_VAR_EX:
7726+
case ZEND_SEND_FUNC_ARG:
7727+
case ZEND_SEND_REF:
7728+
case ZEND_SEND_VAR_NO_REF:
7729+
case ZEND_SEND_VAR_NO_REF_EX:
7730+
/* skip */
7731+
break;
7732+
default:
7733+
if (zend_may_throw(opline, ssa_op, op_array, ssa)) {
7734+
return 1;
7735+
}
7736+
}
7737+
ssa_op += zend_jit_trace_op_len(opline);
7738+
} else if (p->op == ZEND_JIT_TRACE_ENTER ||
7739+
p->op == ZEND_JIT_TRACE_BACK ||
7740+
p->op == ZEND_JIT_TRACE_END) {
7741+
return 1;
7742+
}
7743+
p++;
7744+
}
7745+
}
7746+
76807747
if (!call_info) {
76817748
const zend_op *end = op_array->opcodes + op_array->last;
76827749

76837750
opline++;
7751+
ssa_op++;
76847752
skip = 1;
76857753
while (opline != end) {
76867754
if (!skip) {
7687-
zend_ssa_op *ssa_op = &ssa->ops[opline - op_array->opcodes];
76887755
if (zend_may_throw(opline, ssa_op, op_array, ssa)) {
76897756
return 1;
76907757
}
@@ -7740,6 +7807,7 @@ static int zend_jit_needs_call_chain(zend_call_info *call_info, uint32_t b, cons
77407807
return 0;
77417808
}
77427809
opline++;
7810+
ssa_op++;
77437811
}
77447812

77457813
return 1;
@@ -7752,6 +7820,7 @@ static int zend_jit_needs_call_chain(zend_call_info *call_info, uint32_t b, cons
77527820
}
77537821

77547822
opline++;
7823+
ssa_op++;
77557824
skip = 1;
77567825
while (opline != end) {
77577826
if (skip) {
@@ -7772,12 +7841,12 @@ static int zend_jit_needs_call_chain(zend_call_info *call_info, uint32_t b, cons
77727841
return 1;
77737842
}
77747843
} else {
7775-
zend_ssa_op *ssa_op = &ssa->ops[opline - op_array->opcodes];
77767844
if (zend_may_throw(opline, ssa_op, op_array, ssa)) {
77777845
return 1;
77787846
}
77797847
}
77807848
opline++;
7849+
ssa_op++;
77817850
}
77827851

77837852
return 0;
@@ -7847,7 +7916,7 @@ static int zend_jit_init_fcall_guard(dasm_State **Dst, const zend_op *opline, co
78477916
return 1;
78487917
}
78497918

7850-
static int zend_jit_init_fcall(dasm_State **Dst, const zend_op *opline, uint32_t b, const zend_op_array *op_array, zend_ssa *ssa, int call_level, zend_jit_trace_rec *trace)
7919+
static int zend_jit_init_fcall(dasm_State **Dst, const zend_op *opline, uint32_t b, const zend_op_array *op_array, zend_ssa *ssa, const zend_ssa_op *ssa_op, int call_level, zend_jit_trace_rec *trace)
78517920
{
78527921
zend_func_info *info = ZEND_FUNC_INFO(op_array);
78537922
zend_call_info *call_info = NULL;
@@ -7984,7 +8053,7 @@ static int zend_jit_init_fcall(dasm_State **Dst, const zend_op *opline, uint32_t
79848053
return 0;
79858054
}
79868055

7987-
if (trace || zend_jit_needs_call_chain(call_info, b, op_array, ssa, opline)) {
8056+
if (zend_jit_needs_call_chain(call_info, b, op_array, ssa, ssa_op, opline, trace)) {
79888057
if (!zend_jit_save_call_chain(Dst, call_level)) {
79898058
return 0;
79908059
}

0 commit comments

Comments
 (0)