From: Dmitry Stogov <dmitry@zend.com>
Date: Wed, 18 Mar 2020 20:56:03 +0000 (+0300)
Subject: JIT for ZEND_CHECK_FUNC_ARG
X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=8e879b9f4452e88bb22cf323df0969ea0f4e3e28;p=php

JIT for ZEND_CHECK_FUNC_ARG
---

diff --git a/ext/opcache/Optimizer/zend_inference.c b/ext/opcache/Optimizer/zend_inference.c
index b6bf6eb474..34d6286769 100644
--- a/ext/opcache/Optimizer/zend_inference.c
+++ b/ext/opcache/Optimizer/zend_inference.c
@@ -4327,6 +4327,7 @@ int zend_may_throw(const zend_op *opline, const zend_ssa_op *ssa_op, const zend_
 		case ZEND_FUNC_NUM_ARGS:
 		case ZEND_FUNC_GET_ARGS:
 		case ZEND_COPY_TMP:
+		case ZEND_CHECK_FUNC_ARG:
 			return 0;
 		case ZEND_INIT_FCALL:
 			/* can't throw, because call is resolved at compile time */
diff --git a/ext/opcache/jit/zend_jit.c b/ext/opcache/jit/zend_jit.c
index ccff9e097d..012b42b48c 100644
--- a/ext/opcache/jit/zend_jit.c
+++ b/ext/opcache/jit/zend_jit.c
@@ -2480,6 +2480,14 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op
 							goto jit_failure;
 						}
 						goto done;
+					case ZEND_CHECK_FUNC_ARG:
+						if (opline->op2.num > MAX_ARG_FLAG_NUM) {
+							break;
+						}
+						if (!zend_jit_check_func_arg(&dasm_state, opline, op_array)) {
+							goto jit_failure;
+						}
+						goto done;
 					case ZEND_DO_UCALL:
 						is_terminated = 1;
 						/* break missing intentionally */
diff --git a/ext/opcache/jit/zend_jit_internal.h b/ext/opcache/jit/zend_jit_internal.h
index 504cb67065..922f4b25ac 100644
--- a/ext/opcache/jit/zend_jit_internal.h
+++ b/ext/opcache/jit/zend_jit_internal.h
@@ -336,6 +336,7 @@ struct _zend_jit_trace_stack_frame {
 			int8_t              return_value_used;
 			int8_t              nested;
 			int8_t              num_args;
+			int8_t				last_send_by_ref;
 		};
 		int                     return_ssa_var;
 	};
diff --git a/ext/opcache/jit/zend_jit_trace.c b/ext/opcache/jit/zend_jit_trace.c
index 242ab347da..ed4672d3fc 100644
--- a/ext/opcache/jit/zend_jit_trace.c
+++ b/ext/opcache/jit/zend_jit_trace.c
@@ -1664,6 +1664,7 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par
 	frame->return_value_used = -1;
 	frame->nested = 0;
 	frame->num_args = -1;
+	frame->last_send_by_ref = -1;
 	stack = frame->stack;
 
 	if (trace_buffer->start == ZEND_JIT_TRACE_START_ENTER) {
@@ -2232,6 +2233,17 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par
 							}
 						}
 						goto done;
+					case ZEND_CHECK_FUNC_ARG:
+						if (opline->op2.num > MAX_ARG_FLAG_NUM
+						 && (!JIT_G(current_frame)
+						  || !JIT_G(current_frame)->call
+						  || !JIT_G(current_frame)->call->func)) {
+							break;
+						}
+						if (!zend_jit_check_func_arg(&dasm_state, opline, op_array)) {
+							goto jit_failure;
+						}
+						goto done;
 					case ZEND_DO_UCALL:
 					case ZEND_DO_ICALL:
 					case ZEND_DO_FCALL_BY_NAME:
@@ -2861,6 +2873,7 @@ done:
 				call->func = (const zend_function*)op_array;
 				call->nested = 0;
 				call->num_args = -1; // TODO: should be possible to get the real number ???
+				call->last_send_by_ref = -1;
 				top = zend_jit_trace_call_frame(top, op_array);
 				i = 0;
 				while (i < p->op_array->num_args) {
@@ -2908,6 +2921,7 @@ done:
 				frame->return_value_used = -1;
 				frame->nested = 0;
 				frame->num_args = -1;
+				frame->last_send_by_ref = -1;
 				stack = frame->stack;
 				for (i = 0; i < op_array->last_var + op_array->T; i++) {
 					/* Initialize abstract stack using SSA */
@@ -2937,6 +2951,7 @@ done:
 			call->func = p->func;
 			call->nested = 1;
 			call->num_args = find_call_num_args(p-1);
+			call->last_send_by_ref = p->fake ? -1 : 0;
 			frame->call = call;
 			top = zend_jit_trace_call_frame(top, p->op_array);
 			if (p->func->type == ZEND_USER_FUNCTION) {
diff --git a/ext/opcache/jit/zend_jit_x86.dasc b/ext/opcache/jit/zend_jit_x86.dasc
index 2cb8955ff2..a9b0888ab1 100644
--- a/ext/opcache/jit/zend_jit_x86.dasc
+++ b/ext/opcache/jit/zend_jit_x86.dasc
@@ -8728,6 +8728,63 @@ static int zend_jit_send_var(dasm_State **Dst, const zend_op *opline, const zend
 	return 1;
 }
 
+static int zend_jit_check_func_arg(dasm_State **Dst, const zend_op *opline, const zend_op_array *op_array)
+{
+	uint32_t arg_num = opline->op2.num;
+
+	if (zend_jit_trigger == ZEND_JIT_ON_HOT_TRACE
+	 && JIT_G(current_frame)
+	 && JIT_G(current_frame)->call
+	 && JIT_G(current_frame)->call->func) {
+		if (ARG_SHOULD_BE_SENT_BY_REF(JIT_G(current_frame)->call->func, arg_num)) {
+			if (JIT_G(current_frame)->call->last_send_by_ref != 1) {
+				JIT_G(current_frame)->call->last_send_by_ref = 1;
+				|	// ZEND_ADD_CALL_FLAG(EX(call), ZEND_CALL_SEND_ARG_BY_REF);
+				||	if (reuse_ip) {
+				|		or dword [RX + offsetof(zend_execute_data, This.u1.type_info)], ZEND_CALL_SEND_ARG_BY_REF
+				||	} else {
+				|		mov r0, EX->call
+				|		or dword [r0 + offsetof(zend_execute_data, This.u1.type_info)], ZEND_CALL_SEND_ARG_BY_REF
+				||	}
+			}
+		} else {
+			if (JIT_G(current_frame)->call->last_send_by_ref != 0) {
+				JIT_G(current_frame)->call->last_send_by_ref = 0;
+				|	// ZEND_DEL_CALL_FLAG(EX(call), ZEND_CALL_SEND_ARG_BY_REF);
+				||	if (reuse_ip) {
+				|		and dword [RX + offsetof(zend_execute_data, This.u1.type_info)], ~ZEND_CALL_SEND_ARG_BY_REF
+				||	} else {
+				|		mov r0, EX->call
+				|		and dword [r0 + offsetof(zend_execute_data, This.u1.type_info)], ~ZEND_CALL_SEND_ARG_BY_REF
+				||	}
+			}
+		}
+	} else {
+		// if (QUICK_ARG_SHOULD_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
+		uint32_t mask = (ZEND_SEND_BY_REF|ZEND_SEND_PREFER_REF) << ((arg_num + 3) * 2);
+
+		if (!reuse_ip) {
+			zend_jit_start_reuse_ip();
+			|	// call = EX(call);
+			|	mov RX, EX->call
+		}
+		|	mov r0, EX:RX->func
+		|	test dword [r0 + offsetof(zend_function, quick_arg_flags)], mask
+		|	jnz	>1
+		|.cold_code
+		|1:
+		|	// ZEND_ADD_CALL_FLAG(EX(call), ZEND_CALL_SEND_ARG_BY_REF);
+		|	or dword [RX + offsetof(zend_execute_data, This.u1.type_info)], ZEND_CALL_SEND_ARG_BY_REF
+		|	jmp >1
+		|.code
+		|	// ZEND_DEL_CALL_FLAG(EX(call), ZEND_CALL_SEND_ARG_BY_REF);
+		|	and dword [RX + offsetof(zend_execute_data, This.u1.type_info)], ~ZEND_CALL_SEND_ARG_BY_REF
+		|1:
+	}
+
+	return 1;
+}
+
 static int zend_jit_smart_true(dasm_State **Dst, const zend_op *opline, int jmp, zend_uchar smart_branch_opcode, uint32_t target_label, uint32_t target_label2)
 {
 	if (smart_branch_opcode) {