Skip to content

Commit 5f1fb1a

Browse files
committed
JIT for INIT_METHOD_CALL
1 parent effa3b0 commit 5f1fb1a

File tree

5 files changed

+474
-18
lines changed

5 files changed

+474
-18
lines changed

ext/opcache/jit/zend_jit.c

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3176,6 +3176,41 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op
31763176
goto jit_failure;
31773177
}
31783178
goto done;
3179+
case ZEND_INIT_METHOD_CALL:
3180+
if (opline->op2_type != IS_CONST
3181+
|| Z_TYPE_P(RT_CONSTANT(opline, opline->op2)) != IS_STRING) {
3182+
break;
3183+
}
3184+
ce = NULL;
3185+
ce_is_instanceof = 0;
3186+
if (opline->op1_type == IS_UNUSED) {
3187+
op1_info = MAY_BE_OBJECT|MAY_BE_RC1|MAY_BE_RCN;
3188+
op1_addr = 0;
3189+
ce = op_array->scope;
3190+
ce_is_instanceof = (ce->ce_flags & ZEND_ACC_FINAL) != 0;
3191+
} else {
3192+
op1_info = OP1_INFO();
3193+
if (!(op1_info & MAY_BE_OBJECT)) {
3194+
break;
3195+
}
3196+
op1_addr = OP1_REG_ADDR();
3197+
if (ssa->var_info && ssa->ops) {
3198+
zend_ssa_op *ssa_op = &ssa->ops[opline - op_array->opcodes];
3199+
if (ssa_op->op1_use >= 0) {
3200+
zend_ssa_var_info *op1_ssa = ssa->var_info + ssa_op->op1_use;
3201+
if (op1_ssa->ce && !op1_ssa->ce->create_object) {
3202+
ce = op1_ssa->ce;
3203+
ce_is_instanceof = op1_ssa->is_instanceof;
3204+
}
3205+
}
3206+
}
3207+
}
3208+
if (!zend_jit_init_method_call(&dasm_state, opline, b, op_array, ssa, ssa_op, call_level,
3209+
op1_info, op1_addr, ce, ce_is_instanceof, 0, NULL,
3210+
NULL)) {
3211+
goto jit_failure;
3212+
}
3213+
goto done;
31793214
default:
31803215
break;
31813216
}

ext/opcache/jit/zend_jit_disasm_x86.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -398,6 +398,11 @@ static int zend_jit_disasm_init(void)
398398
REGISTER_HELPER(zend_jit_init_func_run_time_cache_helper);
399399
REGISTER_HELPER(zend_jit_find_func_helper);
400400
REGISTER_HELPER(zend_jit_find_ns_func_helper);
401+
REGISTER_HELPER(zend_jit_find_method_helper);
402+
REGISTER_HELPER(zend_jit_push_static_metod_call_frame);
403+
REGISTER_HELPER(zend_jit_push_static_metod_call_frame_tmp);
404+
REGISTER_HELPER(zend_jit_invalid_method_call);
405+
REGISTER_HELPER(zend_jit_unref_helper);
401406
REGISTER_HELPER(zend_jit_extend_stack_helper);
402407
REGISTER_HELPER(zend_jit_int_extend_stack_helper);
403408
REGISTER_HELPER(zend_jit_leave_nested_func_helper);

ext/opcache/jit/zend_jit_helpers.c

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,109 @@ static zend_function* ZEND_FASTCALL zend_jit_find_ns_func_helper(zval *func_name
9090
return fbc;
9191
}
9292

93+
static ZEND_COLD void ZEND_FASTCALL zend_jit_invalid_method_call(zval *object)
94+
{
95+
zend_execute_data *execute_data = EG(current_execute_data);
96+
const zend_op *opline = EX(opline);
97+
zval *function_name = function_name = RT_CONSTANT(opline, opline->op2);;
98+
99+
if (Z_TYPE_P(object) == IS_UNDEF && opline->op1_type == IS_CV) {
100+
zend_string *cv = EX(func)->op_array.vars[EX_VAR_TO_NUM(opline->op1.var)];
101+
102+
zend_error(E_WARNING, "Undefined variable $%s", ZSTR_VAL(cv));
103+
if (UNEXPECTED(EG(exception) != NULL)) {
104+
return;
105+
}
106+
object = &EG(uninitialized_zval);
107+
}
108+
zend_throw_error(NULL, "Call to a member function %s() on %s",
109+
Z_STRVAL_P(function_name), zend_zval_type_name(object));
110+
if (opline->op1_type & (IS_VAR|IS_TMP_VAR)) {
111+
zval_ptr_dtor_nogc(EX_VAR(opline->op1.var));
112+
}
113+
}
114+
115+
static zend_never_inline ZEND_COLD void ZEND_FASTCALL zend_undefined_method(const zend_class_entry *ce, const zend_string *method)
116+
{
117+
zend_throw_error(NULL, "Call to undefined method %s::%s()", ZSTR_VAL(ce->name), ZSTR_VAL(method));
118+
}
119+
120+
static void ZEND_FASTCALL zend_jit_unref_helper(zval *zv)
121+
{
122+
zend_reference *ref;
123+
124+
ZEND_ASSERT(Z_ISREF_P(zv));
125+
ref = Z_REF_P(zv);
126+
ZVAL_COPY_VALUE(zv, &ref->val);
127+
if (GC_DELREF(ref) == 0) {
128+
efree_size(ref, sizeof(zend_reference));
129+
} else {
130+
Z_TRY_ADDREF_P(zv);
131+
}
132+
}
133+
134+
static zend_function* ZEND_FASTCALL zend_jit_find_method_helper(zend_object *obj, zval *function_name, zend_object **obj_ptr)
135+
{
136+
zend_execute_data *execute_data = EG(current_execute_data);
137+
const zend_op *opline = EX(opline);
138+
zend_class_entry *called_scope = obj->ce;
139+
zend_object *orig_obj = obj;
140+
zend_function *fbc;
141+
142+
fbc = obj->handlers->get_method(&obj, Z_STR_P(function_name), function_name + 1);
143+
if (UNEXPECTED(fbc == NULL)) {
144+
if (EXPECTED(!EG(exception))) {
145+
zend_undefined_method(called_scope, Z_STR_P(function_name));
146+
}
147+
if ((opline->op1_type & (IS_VAR|IS_TMP_VAR)) && GC_DELREF(orig_obj) == 0) {
148+
zend_objects_store_del(orig_obj);
149+
}
150+
return NULL;
151+
}
152+
153+
if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&fbc->op_array))) {
154+
zend_init_func_run_time_cache(&fbc->op_array);
155+
}
156+
157+
if (UNEXPECTED(obj != orig_obj)) {
158+
if (opline->op1_type & (IS_VAR|IS_TMP_VAR)) {
159+
GC_ADDREF(obj);
160+
if (GC_DELREF(orig_obj) == 0) {
161+
zend_objects_store_del(orig_obj);
162+
}
163+
}
164+
*obj_ptr = obj;
165+
return fbc;
166+
}
167+
168+
if (EXPECTED(!(fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_TRAMPOLINE|ZEND_ACC_NEVER_CACHE)))) {
169+
CACHE_POLYMORPHIC_PTR(opline->result.num, called_scope, fbc);
170+
}
171+
172+
return fbc;
173+
}
174+
175+
static zend_execute_data* ZEND_FASTCALL zend_jit_push_static_metod_call_frame(zend_object *obj, zend_function *fbc, uint32_t num_args)
176+
{
177+
zend_class_entry *scope = obj->ce;
178+
179+
return zend_vm_stack_push_call_frame(ZEND_CALL_NESTED_FUNCTION, fbc, num_args, scope);
180+
}
181+
182+
static zend_execute_data* ZEND_FASTCALL zend_jit_push_static_metod_call_frame_tmp(zend_object *obj, zend_function *fbc, uint32_t num_args)
183+
{
184+
zend_class_entry *scope = obj->ce;
185+
186+
if (GC_DELREF(obj) == 0) {
187+
zend_objects_store_del(obj);
188+
if (UNEXPECTED(EG(exception))) {
189+
return NULL;
190+
}
191+
}
192+
193+
return zend_vm_stack_push_call_frame(ZEND_CALL_NESTED_FUNCTION, fbc, num_args, scope);
194+
}
195+
93196
static zend_execute_data* ZEND_FASTCALL zend_jit_extend_stack_helper(uint32_t used_stack, zend_function *fbc)
94197
{
95198
zend_execute_data *call = (zend_execute_data*)zend_vm_stack_extend(used_stack);

ext/opcache/jit/zend_jit_trace.c

Lines changed: 67 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1703,6 +1703,13 @@ static zend_ssa *zend_jit_trace_build_tssa(zend_jit_trace_rec *trace_buffer, uin
17031703
}
17041704
ADD_OP1_TRACE_GUARD();
17051705
break;
1706+
case ZEND_INIT_METHOD_CALL:
1707+
if (opline->op2_type != IS_CONST
1708+
|| Z_TYPE_P(RT_CONSTANT(opline, opline->op2)) != IS_STRING) {
1709+
break;
1710+
}
1711+
ADD_OP1_TRACE_GUARD();
1712+
break;
17061713
case ZEND_INIT_DYNAMIC_CALL:
17071714
if (orig_op2_type == IS_OBJECT && op2_ce == zend_ce_closure) {
17081715
ADD_OP2_TRACE_GUARD();
@@ -4979,31 +4986,76 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par
49794986
goto jit_failure;
49804987
}
49814988
goto done;
4982-
case ZEND_INIT_DYNAMIC_CALL:
4983-
if (orig_op2_type == IS_OBJECT && op2_ce == zend_ce_closure) {
4984-
op2_info = OP2_INFO();
4985-
CHECK_OP2_TRACE_TYPE();
4986-
if (!zend_jit_init_closure_call(&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)) {
4987-
goto jit_failure;
4989+
case ZEND_INIT_METHOD_CALL:
4990+
if (opline->op2_type != IS_CONST
4991+
|| Z_TYPE_P(RT_CONSTANT(opline, opline->op2)) != IS_STRING) {
4992+
goto generic_dynamic_call;
4993+
}
4994+
delayed_fetch_this = 0;
4995+
ce = NULL;
4996+
ce_is_instanceof = 0;
4997+
if (opline->op1_type == IS_UNUSED) {
4998+
op1_info = MAY_BE_OBJECT|MAY_BE_RC1|MAY_BE_RCN;
4999+
ce = op_array->scope;
5000+
ce_is_instanceof = (ce->ce_flags & ZEND_ACC_FINAL) != 0;
5001+
op1_addr = 0;
5002+
} else {
5003+
op1_info = OP1_INFO();
5004+
if (!(op1_info & MAY_BE_OBJECT)) {
5005+
goto generic_dynamic_call;
5006+
}
5007+
op1_addr = OP1_REG_ADDR();
5008+
if (orig_op1_type != IS_UNKNOWN
5009+
&& (orig_op1_type & IS_TRACE_REFERENCE)) {
5010+
if (!zend_jit_fetch_reference(&dasm_state, opline, orig_op1_type, &op1_info, &op1_addr,
5011+
!ssa->var_info[ssa_op->op1_use].guarded_reference, 1)) {
5012+
goto jit_failure;
5013+
}
5014+
if (opline->op1_type == IS_CV
5015+
&& zend_jit_var_may_alias(op_array, op_array_ssa, EX_VAR_TO_NUM(opline->op1.var)) == NO_ALIAS) {
5016+
ssa->var_info[ssa_op->op1_use].guarded_reference = 1;
5017+
}
5018+
} else {
5019+
CHECK_OP1_TRACE_TYPE();
5020+
}
5021+
if (ssa->var_info && ssa->ops) {
5022+
if (ssa_op->op1_use >= 0) {
5023+
zend_ssa_var_info *op1_ssa = ssa->var_info + ssa_op->op1_use;
5024+
if (op1_ssa->ce && !op1_ssa->ce->create_object) {
5025+
ce = op1_ssa->ce;
5026+
ce_is_instanceof = op1_ssa->is_instanceof;
5027+
}
5028+
}
5029+
}
5030+
if (ssa_op->op1_use >= 0) {
5031+
delayed_fetch_this = ssa->var_info[ssa_op->op1_use].delayed_fetch_this;
49885032
}
4989-
goto done;
49905033
}
4991-
/* break missing intentionally */
4992-
case ZEND_INIT_METHOD_CALL:
4993-
if (!zend_jit_trace_handler(&dasm_state, op_array, opline, zend_may_throw(opline, ssa_op, op_array, ssa), p + 1)) {
5034+
if (!zend_jit_init_method_call(&dasm_state, opline,
5035+
op_array_ssa->cfg.map ? op_array_ssa->cfg.map[opline - op_array->opcodes] : -1,
5036+
op_array, ssa, ssa_op, frame->call_level,
5037+
op1_info, op1_addr, ce, ce_is_instanceof, delayed_fetch_this, op1_ce,
5038+
p + 1)) {
49945039
goto jit_failure;
49955040
}
4996-
if ((p+1)->op == ZEND_JIT_TRACE_INIT_CALL && (p+1)->func) {
4997-
if (!zend_jit_init_fcall_guard(&dasm_state, 0, (p+1)->func, opline+1)) {
4998-
goto jit_failure;
4999-
}
5041+
goto done;
5042+
case ZEND_INIT_DYNAMIC_CALL:
5043+
if (orig_op2_type != IS_OBJECT || op2_ce != zend_ce_closure) {
5044+
goto generic_dynamic_call;
5045+
}
5046+
op2_info = OP2_INFO();
5047+
CHECK_OP2_TRACE_TYPE();
5048+
if (!zend_jit_init_closure_call(&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)) {
5049+
goto jit_failure;
50005050
}
50015051
goto done;
50025052
case ZEND_INIT_STATIC_METHOD_CALL:
5053+
generic_dynamic_call:
50035054
if (!zend_jit_trace_handler(&dasm_state, op_array, opline, zend_may_throw(opline, ssa_op, op_array, ssa), p + 1)) {
50045055
goto jit_failure;
50055056
}
5006-
if ((opline->op1_type != IS_CONST
5057+
if ((opline->opcode != ZEND_INIT_STATIC_METHOD_CALL
5058+
|| opline->op1_type != IS_CONST
50075059
|| opline->op2_type != IS_CONST)
50085060
&& (p+1)->op == ZEND_JIT_TRACE_INIT_CALL && (p+1)->func) {
50095061
if (!zend_jit_init_fcall_guard(&dasm_state, 0, (p+1)->func, opline+1)) {

0 commit comments

Comments
 (0)