summaryrefslogtreecommitdiff
path: root/ext/opcache/Optimizer/optimize_func_calls.c
blob: 14f82556537903349fbda3d861c21513dcdaaf7b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
/* pass 4
 * - optimize INIT_FCALL_BY_NAME to DO_FCALL
 */
#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO

typedef struct _optimizer_call_info {
	zend_function *func;
	zend_op       *opline;
} optimizer_call_info;

static void optimize_func_calls(zend_op_array *op_array, zend_persistent_script *script TSRMLS_DC) {
	zend_op *opline = op_array->opcodes;
	zend_op *end = opline + op_array->last;
	int call = 0;
#if ZEND_EXTENSION_API_NO > PHP_5_4_X_API_NO
	optimizer_call_info *call_stack = ecalloc(op_array->nested_calls + 1, sizeof(optimizer_call_info));
#else
	int stack_size = 4;
	optimizer_call_info *call_stack = ecalloc(stack_size, sizeof(optimizer_call_info));
#endif

	while (opline < end) {
		switch (opline->opcode) {
			case ZEND_INIT_FCALL_BY_NAME:
			case ZEND_INIT_NS_FCALL_BY_NAME:
				if (ZEND_OP2_TYPE(opline) == IS_CONST) {
					zend_function *func;
					zval *function_name = &op_array->literals[opline->op2.constant + 1].constant;
					if ((zend_hash_quick_find(&script->function_table,
							Z_STRVAL_P(function_name), Z_STRLEN_P(function_name) + 1,
							Z_HASH_P(function_name), (void **)&func) == SUCCESS)) {
						call_stack[call].func = func;
					}
				}
				/* break missing intentionally */
			case ZEND_NEW:
			case ZEND_INIT_METHOD_CALL:
			case ZEND_INIT_STATIC_METHOD_CALL:
				call_stack[call].opline = opline;
				call++;
#if ZEND_EXTENSION_API_NO < PHP_5_5_X_API_NO
				if (call == stack_size) {
					stack_size += 4;
					call_stack = erealloc(call_stack, sizeof(optimizer_call_info) * stack_size);
					memset(call_stack + 4, 0, 4 * sizeof(optimizer_call_info));
				}
#endif
				break;
			case ZEND_DO_FCALL_BY_NAME:
				call--;
				if (call_stack[call].func && call_stack[call].opline) {
					zend_op *fcall = call_stack[call].opline;

					opline->opcode = ZEND_DO_FCALL;
					ZEND_OP1_TYPE(opline) = IS_CONST;
					opline->op1.constant = fcall->op2.constant + 1;
					op_array->literals[fcall->op2.constant + 1].cache_slot = op_array->literals[fcall->op2.constant].cache_slot;
					literal_dtor(&ZEND_OP2_LITERAL(fcall));
					if (fcall->opcode == ZEND_INIT_NS_FCALL_BY_NAME) {
						literal_dtor(&op_array->literals[fcall->op2.constant + 2].constant);
					}
					MAKE_NOP(fcall);
				} else if (opline->extended_value == 0 &&
				           call_stack[call].opline &&
				           call_stack[call].opline->opcode == ZEND_INIT_FCALL_BY_NAME &&
				           ZEND_OP2_TYPE(call_stack[call].opline) == IS_CONST) {

					zend_op *fcall = call_stack[call].opline;

					opline->opcode = ZEND_DO_FCALL;
					ZEND_OP1_TYPE(opline) = IS_CONST;
					opline->op1.constant = fcall->op2.constant + 1;
					op_array->literals[fcall->op2.constant + 1].cache_slot = op_array->literals[fcall->op2.constant].cache_slot;
					literal_dtor(&ZEND_OP2_LITERAL(fcall));
					MAKE_NOP(fcall);
				}
				call_stack[call].func = NULL;
				call_stack[call].opline = NULL;
				break;
			case ZEND_FETCH_FUNC_ARG:
			case ZEND_FETCH_OBJ_FUNC_ARG:
			case ZEND_FETCH_DIM_FUNC_ARG:
				if (call_stack[call - 1].func) {
					if (ARG_SHOULD_BE_SENT_BY_REF(call_stack[call - 1].func, (opline->extended_value & ZEND_FETCH_ARG_MASK))) {
						opline->extended_value = 0;
						opline->opcode -= 9;
					} else {
						opline->extended_value = 0;
						opline->opcode -= 12;
					}
				}
				break;
			case ZEND_SEND_VAL:
				if (opline->extended_value == ZEND_DO_FCALL_BY_NAME && call_stack[call - 1].func) {
					if (ARG_MUST_BE_SENT_BY_REF(call_stack[call - 1].func, opline->op2.num)) {
						/* We won't convert it into_DO_FCALL to emit error at run-time */
						call_stack[call - 1].opline = NULL;
					} else {
						opline->extended_value = ZEND_DO_FCALL;
					}
				}
				break;
			case ZEND_SEND_VAR:
				if (opline->extended_value == ZEND_DO_FCALL_BY_NAME && call_stack[call - 1].func) {
					if (ARG_SHOULD_BE_SENT_BY_REF(call_stack[call - 1].func, opline->op2.num)) {
						opline->opcode = ZEND_SEND_REF;
					}
					opline->extended_value = ZEND_DO_FCALL;
				}
				break;
			case ZEND_SEND_VAR_NO_REF:
				if (!(opline->extended_value & ZEND_ARG_COMPILE_TIME_BOUND) && call_stack[call - 1].func) {
					if (ARG_SHOULD_BE_SENT_BY_REF(call_stack[call - 1].func, opline->op2.num)) {
						opline->extended_value |= ZEND_ARG_COMPILE_TIME_BOUND | ZEND_ARG_SEND_BY_REF;
					} else if (opline->extended_value) {
						opline->extended_value |= ZEND_ARG_COMPILE_TIME_BOUND;
					} else {
						opline->opcode = ZEND_SEND_VAR;
						opline->extended_value = ZEND_DO_FCALL;
					}
				}
				break;
			case ZEND_SEND_REF:
				if (opline->extended_value == ZEND_DO_FCALL_BY_NAME && call_stack[call - 1].func) {
					/* We won't handle run-time pass by reference */
					call_stack[call - 1].opline = NULL;
				}
				break;
#if ZEND_EXTENSION_API_NO > PHP_5_5_X_API_NO
			case ZEND_SEND_UNPACK:
				call_stack[call - 1].func = NULL;
				call_stack[call - 1].opline = NULL;
				break;
#endif
			default:
				break;
		}
		opline++;
	}

	efree(call_stack);
}
#endif