@@ -4538,11 +4538,11 @@ static void jit_observer_fcall_begin(zend_jit_ctx *jit, ir_ref rx, ir_ref observ
4538
4538
ir_CALL_2(IR_VOID, ir_CONST_FC_FUNC(zend_observer_fcall_begin_prechecked), rx, observer_handler);
4539
4539
}
4540
4540
4541
- static void jit_observer_fcall_end(zend_jit_ctx *jit, ir_ref rx, zend_jit_addr res_addr ) {
4541
+ static void jit_observer_fcall_end(zend_jit_ctx *jit, ir_ref rx, ir_ref res_ref ) {
4542
4542
ir_ref has_end_observer = ir_IF(ir_EQ(rx, ir_LOAD_A(jit_EG(current_observed_frame))));
4543
4543
ir_IF_TRUE(has_end_observer);
4544
4544
ir_CALL_2(IR_VOID, ir_CONST_FC_FUNC(zend_observer_fcall_end_prechecked),
4545
- rx, jit_ZVAL_ADDR(jit, res_addr) );
4545
+ rx, res_ref );
4546
4546
ir_MERGE_WITH_EMPTY_FALSE(has_end_observer);
4547
4547
}
4548
4548
@@ -10155,8 +10155,9 @@ static int zend_jit_do_fcall(zend_jit_ctx *jit, const zend_op *opline, const zen
10155
10155
zend_jit_reset_last_valid_opline(jit);
10156
10156
10157
10157
// JIT: (zend_execute_internal ? zend_execute_internal : fbc->internal_function.handler)(call, ret);
10158
+ ir_ref res_ref = jit_ZVAL_ADDR(jit, res_addr);
10158
10159
if (zend_execute_internal) {
10159
- ir_CALL_2(IR_VOID, ir_CONST_FUNC(zend_execute_internal), rx, jit_ZVAL_ADDR(jit, res_addr) );
10160
+ ir_CALL_2(IR_VOID, ir_CONST_FUNC(zend_execute_internal), rx, res_ref );
10160
10161
} else {
10161
10162
if (func) {
10162
10163
func_ptr = ir_CONST_FC_FUNC(func->internal_function.handler);
@@ -10166,11 +10167,11 @@ static int zend_jit_do_fcall(zend_jit_ctx *jit, const zend_op *opline, const zen
10166
10167
func_ptr = ir_CAST_FC_FUNC(func_ptr);
10167
10168
#endif
10168
10169
}
10169
- ir_CALL_2(IR_VOID, func_ptr, rx, jit_ZVAL_ADDR(jit, res_addr) );
10170
+ ir_CALL_2(IR_VOID, func_ptr, rx, res_ref );
10170
10171
}
10171
10172
10172
10173
if (may_have_observer) {
10173
- jit_observer_fcall_end(jit, rx, res_addr );
10174
+ jit_observer_fcall_end(jit, rx, res_ref );
10174
10175
}
10175
10176
10176
10177
// JIT: EG(current_execute_data) = execute_data;
@@ -10882,9 +10883,7 @@ static int zend_jit_return(zend_jit_ctx *jit, const zend_op *opline, const zend_
10882
10883
}
10883
10884
op1_addr = dst;
10884
10885
}
10885
- ir_CALL_2(IR_VOID, ir_CONST_FC_FUNC(zend_observer_fcall_end),
10886
- jit_FP(jit),
10887
- jit_ZVAL_ADDR(jit, op1_addr));
10886
+ jit_observer_fcall_end(jit, jit_FP(jit), jit_ZVAL_ADDR(jit, op1_addr));
10888
10887
}
10889
10888
10890
10889
// JIT: if (!EX(return_value))
@@ -17035,38 +17034,38 @@ static bool zend_jit_may_be_in_reg(const zend_op_array *op_array, zend_ssa *ssa,
17035
17034
return 1;
17036
17035
}
17037
17036
17038
- struct jit_frameless_observer_data {
17039
- ir_ref if_unobserved;
17040
- ir_ref reused_jit_ip;
17041
- };
17042
-
17043
- static struct jit_frameless_observer_data jit_frameless_observer_begin(zend_jit_ctx *jit, int checked_stack, const zend_op *opline, ir_ref op1_ref, uint32_t op1_info, ir_ref op2_ref, uint32_t op2_info, ir_ref op1_data_ref, uint32_t op1_data_info) {
17044
- struct jit_frameless_observer_data data;
17037
+ static ir_ref jit_frameless_observer(zend_jit_ctx *jit, int checked_stack, const zend_op *opline, ir_ref op1_ref, uint32_t op1_info, ir_ref op2_ref, uint32_t op2_info, ir_ref op1_data_ref, uint32_t op1_data_info, ir_ref res_ref) {
17045
17038
// JIT: zend_observer_handler_is_unobserved(ZEND_OBSERVER_DATA(fbc))
17046
17039
ir_ref observer_handler;
17047
17040
zend_function *fbc = ZEND_FLF_FUNC(opline);
17048
17041
// Not need for runtime cache or generator checks here, we just need if_unobserved
17049
- data. if_unobserved = jit_observer_fcall_is_unobserved_start(jit, fbc, &observer_handler, IR_UNUSED, IR_UNUSED).if_unobserved;
17042
+ ir_ref if_unobserved = jit_observer_fcall_is_unobserved_start(jit, fbc, &observer_handler, IR_UNUSED, IR_UNUSED).if_unobserved;
17050
17043
17051
- // push args to a valid call frame, without copying
17044
+ // push args to a valid call frame
17052
17045
bool track_last_valid_opline = jit->track_last_valid_opline;
17046
+ bool use_last_valid_opline = jit->use_last_valid_opline;
17053
17047
const zend_op *last_valid_opline = jit->last_valid_opline;
17054
17048
bool reuse_ip = jit->reuse_ip;
17055
- if (reuse_ip) {
17056
- data.reused_jit_ip = jit_IP(jit);
17049
+ // zend_jit_save_call_chain, but preserve delayed_call_level. We cannot override delayed_call_level without also saving in unobserved path.
17050
+ if (jit->delayed_call_level) {
17051
+ ir_ref call = jit_CALL(jit_FP(jit), call);
17052
+ ir_STORE(jit_CALL(jit_IP(jit), prev_execute_data), jit->delayed_call_level == 1 ? IR_NULL : ir_LOAD_A(call));
17053
+ ir_STORE(call, jit_IP(jit));
17057
17054
}
17058
17055
zend_jit_push_call_frame(jit, opline, NULL, fbc, 0, 0, checked_stack, ir_CONST_ADDR(fbc), IR_NULL);
17059
- // yes, we'll temporarily reuse the IP, but we don't want to handle this via the reuse_ip mechanism for simiplicity - we just reset IP to original in end
17056
+ // yes, we'll temporarily reuse the IP, but we must not handle this via the reuse_ip mechanism - we just reset IP to original in end
17057
+ // the primary issue here is that this code is just executed conditionally while the reuse mechanisms are meant to be global
17060
17058
jit->track_last_valid_opline = track_last_valid_opline;
17059
+ jit->use_last_valid_opline = use_last_valid_opline;
17061
17060
jit->last_valid_opline = last_valid_opline;
17062
17061
jit->reuse_ip = reuse_ip;
17063
17062
17064
17063
// push all args
17065
17064
uint32_t call_num_args = ZEND_FLF_NUM_ARGS(opline->opcode);
17066
17065
switch (call_num_args) {
17067
- case 3: jit_ZVAL_COPY(jit, ZEND_ADDR_MEM_ZVAL(ZREG_RX, EX_NUM_TO_VAR(3 )), MAY_BE_ANY & ~MAY_BE_REF, ZEND_ADDR_REF_ZVAL(op1_data_ref), op1_data_info, 0 ); ZEND_FALLTHROUGH;
17068
- case 2: jit_ZVAL_COPY(jit, ZEND_ADDR_MEM_ZVAL(ZREG_RX, EX_NUM_TO_VAR(2 )), MAY_BE_ANY & ~MAY_BE_REF, ZEND_ADDR_REF_ZVAL(op2_ref), op2_info, 0 ); ZEND_FALLTHROUGH;
17069
- case 1: jit_ZVAL_COPY(jit, ZEND_ADDR_MEM_ZVAL(ZREG_RX, EX_NUM_TO_VAR(1 )), MAY_BE_ANY & ~MAY_BE_REF, ZEND_ADDR_REF_ZVAL(op1_ref), op1_info, 0 );
17066
+ case 3: jit_ZVAL_COPY(jit, ZEND_ADDR_MEM_ZVAL(ZREG_RX, EX_NUM_TO_VAR(2 )), MAY_BE_ANY & ~MAY_BE_REF, ZEND_ADDR_REF_ZVAL(op1_data_ref), op1_data_info, 1 ); ZEND_FALLTHROUGH;
17067
+ case 2: jit_ZVAL_COPY(jit, ZEND_ADDR_MEM_ZVAL(ZREG_RX, EX_NUM_TO_VAR(1 )), MAY_BE_ANY & ~MAY_BE_REF, ZEND_ADDR_REF_ZVAL(op2_ref), op2_info, 1 ); ZEND_FALLTHROUGH;
17068
+ case 1: jit_ZVAL_COPY(jit, ZEND_ADDR_MEM_ZVAL(ZREG_RX, EX_NUM_TO_VAR(0 )), MAY_BE_ANY & ~MAY_BE_REF, ZEND_ADDR_REF_ZVAL(op1_ref), op1_info, 1 );
17070
17069
}
17071
17070
17072
17071
// Make call frame externally visible
@@ -17076,15 +17075,22 @@ static struct jit_frameless_observer_data jit_frameless_observer_begin(zend_jit_
17076
17075
17077
17076
jit_observer_fcall_begin(jit, rx, observer_handler);
17078
17077
17079
- return data;
17080
- }
17078
+ // JIT: fbc->internal_function.handler(new_frame, return_value)
17079
+ ir_CALL_2(IR_VOID, ir_CONST_FC_FUNC(fbc->internal_function.handler), rx, res_ref);
17081
17080
17082
- static ir_ref jit_frameless_observer_end(zend_jit_ctx *jit, struct jit_frameless_observer_data *data, const zend_op *opline, zend_jit_addr res_addr) {
17083
- ir_ref rx = jit_IP(jit);
17084
- jit_observer_fcall_end(jit, rx, res_addr);
17081
+ jit_observer_fcall_end(jit, rx, res_ref);
17085
17082
17086
17083
ir_STORE(jit_EG(current_execute_data), ir_LOAD_A(jit_CALL(rx, prev_execute_data)));
17087
17084
17085
+ for (int i = 0; i < call_num_args; ++i) {
17086
+ if (zend_jit_needs_arg_dtor(fbc, i, NULL)) {
17087
+ uint32_t offset = EX_NUM_TO_VAR(i);
17088
+ zend_jit_addr var_addr = ZEND_ADDR_MEM_ZVAL(ZREG_RX, offset);
17089
+
17090
+ jit_ZVAL_PTR_DTOR(jit, var_addr, MAY_BE_ANY|MAY_BE_RC1|MAY_BE_RCN, 0, opline);
17091
+ }
17092
+ }
17093
+
17088
17094
ir_ref allocated_path = IR_UNUSED;
17089
17095
17090
17096
// free the call frame
@@ -17110,14 +17116,18 @@ static ir_ref jit_frameless_observer_end(zend_jit_ctx *jit, struct jit_frameless
17110
17116
ir_MERGE_WITH(allocated_path);
17111
17117
}
17112
17118
17113
- if (jit->reuse_ip) {
17114
- jit_STORE_IP(jit, data->reused_jit_ip);
17119
+ if (reuse_ip) {
17120
+ jit_STORE_IP(jit, ir_LOAD_A(jit_CALL(jit_FP(jit), call)));
17121
+ if (jit->delayed_call_level) { // restore if saved
17122
+ ir_STORE(jit_CALL(jit_FP(jit), call), ir_LOAD_A(jit_CALL(jit_IP(jit), prev_execute_data)));
17123
+ }
17115
17124
} else {
17116
- jit_SET_EX_OPLINE(jit, opline);
17125
+ // Note: conditional (if_unobserved) opline, zend_jit_set_last_valid_opline() may only be called if the opline is actually unconditionally updated
17126
+ jit_STORE_IP(jit, ir_CONST_ADDR(jit->last_valid_opline ? jit->last_valid_opline : opline));
17117
17127
}
17118
17128
17119
17129
ir_ref skip = ir_END();
17120
- ir_IF_TRUE(data-> if_unobserved);
17130
+ ir_IF_TRUE(if_unobserved);
17121
17131
return skip;
17122
17132
}
17123
17133
@@ -17132,9 +17142,7 @@ static void jit_frameless_icall0(zend_jit_ctx *jit, int checked_stack, const zen
17132
17142
17133
17143
ir_ref skip_observer = IR_UNUSED;
17134
17144
if (ZEND_OBSERVER_ENABLED) {
17135
- struct jit_frameless_observer_data observer_data = jit_frameless_observer_begin(jit, checked_stack, opline, IR_UNUSED, 0, IR_UNUSED, 0, IR_UNUSED, 0);
17136
- ir_CALL_1(IR_VOID, ir_CONST_ADDR((size_t)function), res_ref);
17137
- skip_observer = jit_frameless_observer_end(jit, &observer_data, opline, res_addr);
17145
+ skip_observer = jit_frameless_observer(jit, checked_stack, opline, IR_UNUSED, 0, IR_UNUSED, 0, IR_UNUSED, 0, res_ref);
17138
17146
}
17139
17147
ir_CALL_1(IR_VOID, ir_CONST_ADDR((size_t)function), res_ref);
17140
17148
if (skip_observer != IR_UNUSED) {
@@ -17168,16 +17176,15 @@ static void jit_frameless_icall1(zend_jit_ctx *jit, int checked_stack, const zen
17168
17176
17169
17177
ir_ref skip_observer = IR_UNUSED;
17170
17178
if (ZEND_OBSERVER_ENABLED) {
17171
- struct jit_frameless_observer_data observer_data = jit_frameless_observer_begin(jit, checked_stack, opline, op1_ref, op1_info, IR_UNUSED, 0, IR_UNUSED, 0);
17172
- ir_CALL_2(IR_VOID, ir_CONST_ADDR((size_t)function), res_ref, op1_ref);
17173
- skip_observer = jit_frameless_observer_end(jit, &observer_data, opline, res_addr);
17179
+ skip_observer = jit_frameless_observer(jit, checked_stack, opline, op1_ref, op1_info, IR_UNUSED, 0, IR_UNUSED, 0, res_ref);
17174
17180
}
17175
17181
ir_CALL_2(IR_VOID, ir_CONST_ADDR((size_t)function), res_ref, op1_ref);
17176
17182
if (skip_observer != IR_UNUSED) {
17177
17183
ir_MERGE_WITH(skip_observer);
17178
17184
}
17179
17185
17180
17186
jit_FREE_OP(jit, opline->op1_type, opline->op1, op1_info, NULL);
17187
+
17181
17188
zend_jit_check_exception(jit);
17182
17189
}
17183
17190
@@ -17217,9 +17224,7 @@ static void jit_frameless_icall2(zend_jit_ctx *jit, int checked_stack, const zen
17217
17224
17218
17225
ir_ref skip_observer = IR_UNUSED;
17219
17226
if (ZEND_OBSERVER_ENABLED) {
17220
- struct jit_frameless_observer_data observer_data = jit_frameless_observer_begin(jit, checked_stack, opline, op1_ref, op1_info, op2_ref, op2_info, IR_UNUSED, 0);
17221
- ir_CALL_3(IR_VOID, ir_CONST_ADDR((size_t)function), res_ref, op1_ref, op2_ref);
17222
- skip_observer = jit_frameless_observer_end(jit, &observer_data, opline, res_addr);
17227
+ skip_observer = jit_frameless_observer(jit, checked_stack, opline, op1_ref, op1_info, op2_ref, op2_info, IR_UNUSED, 0, res_ref);
17223
17228
}
17224
17229
ir_CALL_3(IR_VOID, ir_CONST_ADDR((size_t)function), res_ref, op1_ref, op2_ref);
17225
17230
if (skip_observer != IR_UNUSED) {
@@ -17233,6 +17238,7 @@ static void jit_frameless_icall2(zend_jit_ctx *jit, int checked_stack, const zen
17233
17238
jit_set_Z_TYPE_INFO(jit, op1_addr, IS_UNDEF);
17234
17239
}
17235
17240
jit_FREE_OP(jit, opline->op2_type, opline->op2, op2_info, NULL);
17241
+
17236
17242
zend_jit_check_exception(jit);
17237
17243
}
17238
17244
@@ -17284,9 +17290,7 @@ static void jit_frameless_icall3(zend_jit_ctx *jit, int checked_stack, const zen
17284
17290
17285
17291
ir_ref skip_observer = IR_UNUSED;
17286
17292
if (ZEND_OBSERVER_ENABLED) {
17287
- struct jit_frameless_observer_data observer_data = jit_frameless_observer_begin(jit, checked_stack, opline, op1_ref, op1_info, op2_ref, op2_info, op3_ref, op1_data_info);
17288
- ir_CALL_4(IR_VOID, ir_CONST_ADDR((size_t)function), res_ref, op1_ref, op2_ref, op3_ref);
17289
- skip_observer = jit_frameless_observer_end(jit, &observer_data, opline, res_addr);
17293
+ skip_observer = jit_frameless_observer(jit, checked_stack, opline, op1_ref, op1_info, op2_ref, op2_info, op3_ref, op1_data_info, res_ref);
17290
17294
}
17291
17295
ir_CALL_4(IR_VOID, ir_CONST_ADDR((size_t)function), res_ref, op1_ref, op2_ref, op3_ref);
17292
17296
if (skip_observer != IR_UNUSED) {
@@ -17309,6 +17313,7 @@ static void jit_frameless_icall3(zend_jit_ctx *jit, int checked_stack, const zen
17309
17313
jit_set_Z_TYPE_INFO(jit, op2_addr, IS_UNDEF);
17310
17314
}
17311
17315
jit_FREE_OP(jit, (opline+1)->op1_type, (opline+1)->op1, op1_data_info, NULL);
17316
+
17312
17317
zend_jit_check_exception(jit);
17313
17318
}
17314
17319
0 commit comments