/src/php-src/Zend/Optimizer/zend_optimizer.c
Line | Count | Source |
1 | | /* |
2 | | +----------------------------------------------------------------------+ |
3 | | | Zend OPcache | |
4 | | +----------------------------------------------------------------------+ |
5 | | | Copyright (c) The PHP Group | |
6 | | +----------------------------------------------------------------------+ |
7 | | | This source file is subject to version 3.01 of the PHP license, | |
8 | | | that is bundled with this package in the file LICENSE, and is | |
9 | | | available through the world-wide-web at the following url: | |
10 | | | https://www.php.net/license/3_01.txt | |
11 | | | If you did not receive a copy of the PHP license and are unable to | |
12 | | | obtain it through the world-wide-web, please send a note to | |
13 | | | license@php.net so we can mail you a copy immediately. | |
14 | | +----------------------------------------------------------------------+ |
15 | | | Authors: Andi Gutmans <andi@php.net> | |
16 | | | Zeev Suraski <zeev@php.net> | |
17 | | | Stanislav Malyshev <stas@zend.com> | |
18 | | | Dmitry Stogov <dmitry@php.net> | |
19 | | +----------------------------------------------------------------------+ |
20 | | */ |
21 | | |
22 | | #include "Optimizer/zend_optimizer.h" |
23 | | #include "Optimizer/zend_optimizer_internal.h" |
24 | | #include "zend_API.h" |
25 | | #include "zend_constants.h" |
26 | | #include "zend_execute.h" |
27 | | #include "zend_vm.h" |
28 | | #include "zend_cfg.h" |
29 | | #include "zend_func_info.h" |
30 | | #include "zend_call_graph.h" |
31 | | #include "zend_inference.h" |
32 | | #include "zend_dump.h" |
33 | | #include "php.h" |
34 | | |
35 | | #ifndef ZEND_OPTIMIZER_MAX_REGISTERED_PASSES |
36 | 0 | # define ZEND_OPTIMIZER_MAX_REGISTERED_PASSES 32 |
37 | | #endif |
38 | | |
39 | | struct { |
40 | | zend_optimizer_pass_t pass[ZEND_OPTIMIZER_MAX_REGISTERED_PASSES]; |
41 | | int last; |
42 | | } zend_optimizer_registered_passes = {{NULL}, 0}; |
43 | | |
44 | | void zend_optimizer_collect_constant(zend_optimizer_ctx *ctx, zval *name, zval* value) |
45 | 0 | { |
46 | 0 | if (!ctx->constants) { |
47 | 0 | ctx->constants = zend_arena_alloc(&ctx->arena, sizeof(HashTable)); |
48 | 0 | zend_hash_init(ctx->constants, 16, NULL, zval_ptr_dtor_nogc, 0); |
49 | 0 | } |
50 | |
|
51 | 0 | if (zend_hash_add(ctx->constants, Z_STR_P(name), value)) { |
52 | 0 | Z_TRY_ADDREF_P(value); |
53 | 0 | } |
54 | 0 | } |
55 | | |
56 | | zend_result zend_optimizer_eval_binary_op(zval *result, uint8_t opcode, zval *op1, zval *op2) /* {{{ */ |
57 | 30.2k | { |
58 | 30.2k | if (zend_binary_op_produces_error(opcode, op1, op2)) { |
59 | 26.7k | return FAILURE; |
60 | 26.7k | } |
61 | | |
62 | 3.51k | binary_op_type binary_op = get_binary_op(opcode); |
63 | 3.51k | return binary_op(result, op1, op2); |
64 | 30.2k | } |
65 | | /* }}} */ |
66 | | |
67 | | zend_result zend_optimizer_eval_unary_op(zval *result, uint8_t opcode, zval *op1) /* {{{ */ |
68 | 3.32k | { |
69 | 3.32k | unary_op_type unary_op = get_unary_op(opcode); |
70 | | |
71 | 3.32k | if (unary_op) { |
72 | 1.04k | if (zend_unary_op_produces_error(opcode, op1)) { |
73 | 879 | return FAILURE; |
74 | 879 | } |
75 | 168 | return unary_op(result, op1); |
76 | 2.27k | } else { /* ZEND_BOOL */ |
77 | 2.27k | if (Z_TYPE_P(op1) == IS_DOUBLE && zend_isnan(Z_DVAL_P(op1))) { |
78 | 4 | return FAILURE; |
79 | 4 | } |
80 | 2.27k | ZVAL_BOOL(result, zend_is_true(op1)); |
81 | 2.27k | return SUCCESS; |
82 | 2.27k | } |
83 | 3.32k | } |
84 | | /* }}} */ |
85 | | |
86 | | zend_result zend_optimizer_eval_cast(zval *result, uint32_t type, zval *op1) /* {{{ */ |
87 | 11.3k | { |
88 | 11.3k | if (zend_try_ct_eval_cast(result, type, op1)) { |
89 | 10.2k | return SUCCESS; |
90 | 10.2k | } |
91 | 1.13k | return FAILURE; |
92 | 11.3k | } |
93 | | /* }}} */ |
94 | | |
95 | | zend_result zend_optimizer_eval_strlen(zval *result, const zval *op1) /* {{{ */ |
96 | 30 | { |
97 | 30 | if (Z_TYPE_P(op1) != IS_STRING) { |
98 | 30 | return FAILURE; |
99 | 30 | } |
100 | 0 | ZVAL_LONG(result, Z_STRLEN_P(op1)); |
101 | 0 | return SUCCESS; |
102 | 30 | } |
103 | | /* }}} */ |
104 | | |
105 | | zend_result zend_optimizer_eval_special_func_call( |
106 | 0 | zval *result, zend_string *name, zend_string *arg) { |
107 | 0 | if (zend_string_equals_literal(name, "function_exists") || |
108 | 0 | zend_string_equals_literal(name, "is_callable")) { |
109 | 0 | zend_string *lc_name = zend_string_tolower(arg); |
110 | 0 | zend_internal_function *func = zend_hash_find_ptr(EG(function_table), lc_name); |
111 | 0 | zend_string_release_ex(lc_name, 0); |
112 | |
|
113 | 0 | if (func && func->type == ZEND_INTERNAL_FUNCTION |
114 | 0 | && func->module->type == MODULE_PERSISTENT |
115 | | #ifdef ZEND_WIN32 |
116 | | && func->module->handle == NULL |
117 | | #endif |
118 | 0 | ) { |
119 | 0 | ZVAL_TRUE(result); |
120 | 0 | return SUCCESS; |
121 | 0 | } |
122 | 0 | return FAILURE; |
123 | 0 | } |
124 | 0 | if (zend_string_equals_literal(name, "extension_loaded")) { |
125 | 0 | zend_string *lc_name = zend_string_tolower(arg); |
126 | 0 | zend_module_entry *m = zend_hash_find_ptr(&module_registry, lc_name); |
127 | 0 | zend_string_release_ex(lc_name, 0); |
128 | |
|
129 | 0 | if (!m) { |
130 | 0 | if (PG(enable_dl)) { |
131 | 0 | return FAILURE; |
132 | 0 | } |
133 | 0 | ZVAL_FALSE(result); |
134 | 0 | return SUCCESS; |
135 | 0 | } |
136 | | |
137 | 0 | if (m->type == MODULE_PERSISTENT |
138 | | #ifdef ZEND_WIN32 |
139 | | && m->handle == NULL |
140 | | #endif |
141 | 0 | ) { |
142 | 0 | ZVAL_TRUE(result); |
143 | 0 | return SUCCESS; |
144 | 0 | } |
145 | 0 | return FAILURE; |
146 | 0 | } |
147 | 0 | if (zend_string_equals_literal(name, "constant")) { |
148 | 0 | return zend_optimizer_get_persistent_constant(arg, result, 1) ? SUCCESS : FAILURE; |
149 | 0 | } |
150 | 0 | if (zend_string_equals_literal(name, "dirname")) { |
151 | 0 | if (!IS_ABSOLUTE_PATH(ZSTR_VAL(arg), ZSTR_LEN(arg))) { |
152 | 0 | return FAILURE; |
153 | 0 | } |
154 | | |
155 | 0 | zend_string *dirname = zend_string_init(ZSTR_VAL(arg), ZSTR_LEN(arg), 0); |
156 | 0 | ZSTR_LEN(dirname) = zend_dirname(ZSTR_VAL(dirname), ZSTR_LEN(dirname)); |
157 | 0 | if (IS_ABSOLUTE_PATH(ZSTR_VAL(dirname), ZSTR_LEN(dirname))) { |
158 | 0 | ZVAL_STR(result, dirname); |
159 | 0 | return SUCCESS; |
160 | 0 | } |
161 | 0 | zend_string_release_ex(dirname, 0); |
162 | 0 | return FAILURE; |
163 | 0 | } |
164 | 0 | if (zend_string_equals_literal(name, "ini_get")) { |
165 | 0 | zend_ini_entry *ini_entry = zend_hash_find_ptr(EG(ini_directives), arg); |
166 | 0 | if (!ini_entry) { |
167 | 0 | if (PG(enable_dl)) { |
168 | 0 | return FAILURE; |
169 | 0 | } |
170 | 0 | ZVAL_FALSE(result); |
171 | 0 | } else if (ini_entry->modifiable != ZEND_INI_SYSTEM) { |
172 | 0 | return FAILURE; |
173 | 0 | } else if (ini_entry->value) { |
174 | 0 | ZVAL_STR_COPY(result, ini_entry->value); |
175 | 0 | } else { |
176 | 0 | ZVAL_EMPTY_STRING(result); |
177 | 0 | } |
178 | 0 | return SUCCESS; |
179 | 0 | } |
180 | 0 | return FAILURE; |
181 | 0 | } |
182 | | |
183 | | bool zend_optimizer_get_collected_constant(HashTable *constants, zval *name, zval* value) |
184 | 0 | { |
185 | 0 | zval *val; |
186 | |
|
187 | 0 | if ((val = zend_hash_find(constants, Z_STR_P(name))) != NULL) { |
188 | 0 | ZVAL_COPY(value, val); |
189 | 0 | return true; |
190 | 0 | } |
191 | 0 | return false; |
192 | 0 | } |
193 | | |
194 | | void zend_optimizer_convert_to_free_op1(zend_op_array *op_array, zend_op *opline) |
195 | 5.86k | { |
196 | 5.86k | if (opline->op1_type == IS_CV) { |
197 | 60 | opline->opcode = ZEND_CHECK_VAR; |
198 | 60 | SET_UNUSED(opline->op2); |
199 | 60 | SET_UNUSED(opline->result); |
200 | 60 | opline->extended_value = 0; |
201 | 5.80k | } else if (opline->op1_type & (IS_TMP_VAR|IS_VAR)) { |
202 | 4.36k | opline->opcode = ZEND_FREE; |
203 | 4.36k | SET_UNUSED(opline->op2); |
204 | 4.36k | SET_UNUSED(opline->result); |
205 | 4.36k | opline->extended_value = 0; |
206 | 4.36k | } else { |
207 | 1.43k | ZEND_ASSERT(opline->op1_type == IS_CONST); |
208 | 1.43k | literal_dtor(&ZEND_OP1_LITERAL(opline)); |
209 | 1.43k | MAKE_NOP(opline); |
210 | 1.43k | } |
211 | 5.86k | } |
212 | | |
213 | | int zend_optimizer_add_literal(zend_op_array *op_array, const zval *zv) |
214 | 27.2k | { |
215 | 27.2k | int i = op_array->last_literal; |
216 | 27.2k | op_array->last_literal++; |
217 | 27.2k | op_array->literals = (zval*)erealloc(op_array->literals, op_array->last_literal * sizeof(zval)); |
218 | 27.2k | ZVAL_COPY_VALUE(&op_array->literals[i], zv); |
219 | 27.2k | Z_EXTRA(op_array->literals[i]) = 0; |
220 | 27.2k | return i; |
221 | 27.2k | } |
222 | | |
223 | 88 | static inline int zend_optimizer_add_literal_string(zend_op_array *op_array, zend_string *str) { |
224 | 88 | zval zv; |
225 | 88 | ZVAL_STR(&zv, str); |
226 | 88 | zend_string_hash_val(str); |
227 | 88 | return zend_optimizer_add_literal(op_array, &zv); |
228 | 88 | } |
229 | | |
230 | 86 | static inline void drop_leading_backslash(zval *val) { |
231 | 86 | if (Z_STRVAL_P(val)[0] == '\\') { |
232 | 4 | zend_string *str = zend_string_init(Z_STRVAL_P(val) + 1, Z_STRLEN_P(val) - 1, 0); |
233 | 4 | zval_ptr_dtor_nogc(val); |
234 | 4 | ZVAL_STR(val, str); |
235 | 4 | } |
236 | 86 | } |
237 | | |
238 | 180 | static inline uint32_t alloc_cache_slots(zend_op_array *op_array, uint32_t num) { |
239 | 180 | uint32_t ret = op_array->cache_size; |
240 | 180 | op_array->cache_size += num * sizeof(void *); |
241 | 180 | return ret; |
242 | 180 | } |
243 | | |
244 | 108 | #define REQUIRES_STRING(val) do { \ |
245 | 108 | if (Z_TYPE_P(val) != IS_STRING) { \ |
246 | 20 | return 0; \ |
247 | 20 | } \ |
248 | 108 | } while (0) |
249 | | |
250 | 2.08k | #define TO_STRING_NOWARN(val) do { \ |
251 | 2.08k | if (Z_TYPE_P(val) >= IS_ARRAY) { \ |
252 | 4 | return 0; \ |
253 | 4 | } \ |
254 | 2.08k | convert_to_string(val); \ |
255 | 2.07k | } while (0) |
256 | | |
257 | | bool zend_optimizer_update_op1_const(zend_op_array *op_array, |
258 | | zend_op *opline, |
259 | | zval *val) |
260 | 11.8k | { |
261 | 11.8k | switch (opline->opcode) { |
262 | 392 | case ZEND_OP_DATA: |
263 | 392 | switch ((opline-1)->opcode) { |
264 | 0 | case ZEND_ASSIGN_OBJ_REF: |
265 | 0 | case ZEND_ASSIGN_STATIC_PROP_REF: |
266 | 0 | return false; |
267 | 392 | } |
268 | 392 | opline->op1.constant = zend_optimizer_add_literal(op_array, val); |
269 | 392 | break; |
270 | 953 | case ZEND_FREE: |
271 | 953 | case ZEND_CHECK_VAR: |
272 | 953 | MAKE_NOP(opline); |
273 | 953 | zval_ptr_dtor_nogc(val); |
274 | 953 | return true; |
275 | 0 | case ZEND_SEND_VAR_EX: |
276 | 0 | case ZEND_SEND_FUNC_ARG: |
277 | 0 | case ZEND_FETCH_DIM_W: |
278 | 0 | case ZEND_FETCH_DIM_RW: |
279 | 8 | case ZEND_FETCH_DIM_FUNC_ARG: |
280 | 8 | case ZEND_FETCH_DIM_UNSET: |
281 | 8 | case ZEND_FETCH_LIST_W: |
282 | 8 | case ZEND_ASSIGN_DIM: |
283 | 42 | case ZEND_RETURN_BY_REF: |
284 | 42 | case ZEND_INSTANCEOF: |
285 | 42 | case ZEND_MAKE_REF: |
286 | 46 | case ZEND_SEPARATE: |
287 | 46 | case ZEND_SEND_VAR_NO_REF: |
288 | 72 | case ZEND_SEND_VAR_NO_REF_EX: |
289 | 72 | return false; |
290 | 0 | case ZEND_CATCH: |
291 | 0 | REQUIRES_STRING(val); |
292 | 0 | drop_leading_backslash(val); |
293 | 0 | opline->op1.constant = zend_optimizer_add_literal(op_array, val); |
294 | 0 | opline->extended_value = alloc_cache_slots(op_array, 1) | (opline->extended_value & ZEND_LAST_CATCH); |
295 | 0 | zend_optimizer_add_literal_string(op_array, zend_string_tolower(Z_STR_P(val))); |
296 | 0 | break; |
297 | 0 | case ZEND_DEFINED: |
298 | 0 | REQUIRES_STRING(val); |
299 | 0 | drop_leading_backslash(val); |
300 | 0 | opline->op1.constant = zend_optimizer_add_literal(op_array, val); |
301 | 0 | opline->extended_value = alloc_cache_slots(op_array, 1); |
302 | 0 | zend_optimizer_add_literal_string(op_array, zend_string_tolower(Z_STR_P(val))); |
303 | 0 | break; |
304 | 10 | case ZEND_NEW: |
305 | 10 | REQUIRES_STRING(val); |
306 | 10 | drop_leading_backslash(val); |
307 | 10 | opline->op1.constant = zend_optimizer_add_literal(op_array, val); |
308 | 10 | opline->op2.num = alloc_cache_slots(op_array, 1); |
309 | 10 | zend_optimizer_add_literal_string(op_array, zend_string_tolower(Z_STR_P(val))); |
310 | 10 | break; |
311 | 6 | case ZEND_INIT_STATIC_METHOD_CALL: |
312 | 6 | REQUIRES_STRING(val); |
313 | 6 | drop_leading_backslash(val); |
314 | 6 | opline->op1.constant = zend_optimizer_add_literal(op_array, val); |
315 | 6 | if (opline->op2_type != IS_CONST) { |
316 | 0 | opline->result.num = alloc_cache_slots(op_array, 1); |
317 | 0 | } |
318 | 6 | zend_optimizer_add_literal_string(op_array, zend_string_tolower(Z_STR_P(val))); |
319 | 6 | break; |
320 | 12 | case ZEND_FETCH_CLASS_CONSTANT: |
321 | 12 | REQUIRES_STRING(val); |
322 | 10 | drop_leading_backslash(val); |
323 | 10 | opline->op1.constant = zend_optimizer_add_literal(op_array, val); |
324 | 10 | if (opline->op2_type != IS_CONST) { |
325 | 4 | opline->extended_value = alloc_cache_slots(op_array, 1); |
326 | 4 | } |
327 | 10 | zend_optimizer_add_literal_string(op_array, zend_string_tolower(Z_STR_P(val))); |
328 | 10 | break; |
329 | 0 | case ZEND_ASSIGN_OP: |
330 | 0 | case ZEND_ASSIGN_DIM_OP: |
331 | 0 | case ZEND_ASSIGN_OBJ_OP: |
332 | 0 | break; |
333 | 0 | case ZEND_ASSIGN_STATIC_PROP_OP: |
334 | 0 | case ZEND_ASSIGN_STATIC_PROP: |
335 | 0 | case ZEND_ASSIGN_STATIC_PROP_REF: |
336 | 4 | case ZEND_FETCH_STATIC_PROP_R: |
337 | 4 | case ZEND_FETCH_STATIC_PROP_W: |
338 | 4 | case ZEND_FETCH_STATIC_PROP_RW: |
339 | 4 | case ZEND_FETCH_STATIC_PROP_IS: |
340 | 4 | case ZEND_FETCH_STATIC_PROP_UNSET: |
341 | 4 | case ZEND_FETCH_STATIC_PROP_FUNC_ARG: |
342 | 4 | case ZEND_UNSET_STATIC_PROP: |
343 | 4 | case ZEND_ISSET_ISEMPTY_STATIC_PROP: |
344 | 4 | case ZEND_PRE_INC_STATIC_PROP: |
345 | 4 | case ZEND_PRE_DEC_STATIC_PROP: |
346 | 4 | case ZEND_POST_INC_STATIC_PROP: |
347 | 4 | case ZEND_POST_DEC_STATIC_PROP: |
348 | 4 | TO_STRING_NOWARN(val); |
349 | 4 | opline->op1.constant = zend_optimizer_add_literal(op_array, val); |
350 | 4 | if (opline->op2_type == IS_CONST && (opline->extended_value & ~ZEND_FETCH_OBJ_FLAGS) + sizeof(void*) == op_array->cache_size) { |
351 | 0 | op_array->cache_size += sizeof(void *); |
352 | 4 | } else { |
353 | 4 | opline->extended_value = alloc_cache_slots(op_array, 3) | (opline->extended_value & ZEND_FETCH_OBJ_FLAGS); |
354 | 4 | } |
355 | 4 | break; |
356 | 294 | case ZEND_SEND_VAR: |
357 | 294 | opline->opcode = ZEND_SEND_VAL; |
358 | 294 | opline->op1.constant = zend_optimizer_add_literal(op_array, val); |
359 | 294 | break; |
360 | 18 | case ZEND_CASE: |
361 | 18 | opline->opcode = ZEND_IS_EQUAL; |
362 | 18 | opline->op1.constant = zend_optimizer_add_literal(op_array, val); |
363 | 18 | break; |
364 | 2 | case ZEND_CASE_STRICT: |
365 | 2 | opline->opcode = ZEND_IS_IDENTICAL; |
366 | 2 | opline->op1.constant = zend_optimizer_add_literal(op_array, val); |
367 | 2 | break; |
368 | 0 | case ZEND_VERIFY_RETURN_TYPE: |
369 | | /* This would require a non-local change. |
370 | | * zend_optimizer_replace_by_const() supports this. */ |
371 | 0 | return false; |
372 | 0 | case ZEND_COPY_TMP: |
373 | 6 | case ZEND_FETCH_CLASS_NAME: |
374 | 6 | return false; |
375 | 919 | case ZEND_ECHO: |
376 | 919 | { |
377 | 919 | zval zv; |
378 | 919 | if (Z_TYPE_P(val) != IS_STRING && zend_optimizer_eval_cast(&zv, IS_STRING, val) == SUCCESS) { |
379 | 16 | zval_ptr_dtor_nogc(val); |
380 | 16 | val = &zv; |
381 | 16 | } |
382 | 919 | opline->op1.constant = zend_optimizer_add_literal(op_array, val); |
383 | 919 | if (Z_TYPE_P(val) == IS_STRING && Z_STRLEN_P(val) == 0) { |
384 | 6 | MAKE_NOP(opline); |
385 | 6 | return true; |
386 | 6 | } |
387 | | /* TODO: In a subsequent pass, *after* this step and compacting nops, combine consecutive ZEND_ECHOs using the block information from ssa->cfg */ |
388 | | /* (e.g. for ext/opcache/tests/opt/sccp_010.phpt) */ |
389 | 913 | break; |
390 | 919 | } |
391 | 913 | case ZEND_CONCAT: |
392 | 87 | case ZEND_FAST_CONCAT: |
393 | 91 | case ZEND_FETCH_R: |
394 | 91 | case ZEND_FETCH_W: |
395 | 91 | case ZEND_FETCH_RW: |
396 | 91 | case ZEND_FETCH_IS: |
397 | 91 | case ZEND_FETCH_UNSET: |
398 | 91 | case ZEND_FETCH_FUNC_ARG: |
399 | 95 | case ZEND_ISSET_ISEMPTY_VAR: |
400 | 95 | case ZEND_UNSET_VAR: |
401 | 95 | TO_STRING_NOWARN(val); |
402 | 95 | if (opline->opcode == ZEND_CONCAT && opline->op2_type == IS_CONST) { |
403 | 85 | opline->opcode = ZEND_FAST_CONCAT; |
404 | 85 | } |
405 | 95 | ZEND_FALLTHROUGH; |
406 | 9.18k | default: |
407 | 9.18k | opline->op1.constant = zend_optimizer_add_literal(op_array, val); |
408 | 9.18k | break; |
409 | 11.8k | } |
410 | | |
411 | 10.8k | opline->op1_type = IS_CONST; |
412 | 10.8k | if (Z_TYPE(ZEND_OP1_LITERAL(opline)) == IS_STRING) { |
413 | 2.04k | zend_string_hash_val(Z_STR(ZEND_OP1_LITERAL(opline))); |
414 | 2.04k | } |
415 | 10.8k | return true; |
416 | 11.8k | } |
417 | | |
418 | | bool zend_optimizer_update_op2_const(zend_op_array *op_array, |
419 | | zend_op *opline, |
420 | | zval *val) |
421 | 4.85k | { |
422 | 4.85k | zval tmp; |
423 | | |
424 | 4.85k | switch (opline->opcode) { |
425 | 82 | case ZEND_ASSIGN_REF: |
426 | 82 | case ZEND_FAST_CALL: |
427 | 82 | return false; |
428 | 46 | case ZEND_FETCH_CLASS: |
429 | 46 | case ZEND_INSTANCEOF: |
430 | 46 | REQUIRES_STRING(val); |
431 | 32 | drop_leading_backslash(val); |
432 | 32 | opline->op2.constant = zend_optimizer_add_literal(op_array, val); |
433 | 32 | zend_optimizer_add_literal_string(op_array, zend_string_tolower(Z_STR_P(val))); |
434 | 32 | opline->extended_value = alloc_cache_slots(op_array, 1); |
435 | 32 | break; |
436 | 0 | case ZEND_INIT_FCALL_BY_NAME: |
437 | 0 | REQUIRES_STRING(val); |
438 | 0 | drop_leading_backslash(val); |
439 | 0 | opline->op2.constant = zend_optimizer_add_literal(op_array, val); |
440 | 0 | zend_optimizer_add_literal_string(op_array, zend_string_tolower(Z_STR_P(val))); |
441 | 0 | opline->result.num = alloc_cache_slots(op_array, 1); |
442 | 0 | break; |
443 | 0 | case ZEND_ASSIGN_STATIC_PROP: |
444 | 0 | case ZEND_ASSIGN_STATIC_PROP_REF: |
445 | 26 | case ZEND_FETCH_STATIC_PROP_R: |
446 | 26 | case ZEND_FETCH_STATIC_PROP_W: |
447 | 26 | case ZEND_FETCH_STATIC_PROP_RW: |
448 | 26 | case ZEND_FETCH_STATIC_PROP_IS: |
449 | 26 | case ZEND_FETCH_STATIC_PROP_UNSET: |
450 | 32 | case ZEND_FETCH_STATIC_PROP_FUNC_ARG: |
451 | 32 | case ZEND_UNSET_STATIC_PROP: |
452 | 32 | case ZEND_ISSET_ISEMPTY_STATIC_PROP: |
453 | 32 | case ZEND_PRE_INC_STATIC_PROP: |
454 | 32 | case ZEND_PRE_DEC_STATIC_PROP: |
455 | 32 | case ZEND_POST_INC_STATIC_PROP: |
456 | 32 | case ZEND_POST_DEC_STATIC_PROP: |
457 | 32 | case ZEND_ASSIGN_STATIC_PROP_OP: |
458 | 32 | REQUIRES_STRING(val); |
459 | 28 | drop_leading_backslash(val); |
460 | 28 | opline->op2.constant = zend_optimizer_add_literal(op_array, val); |
461 | 28 | zend_optimizer_add_literal_string(op_array, zend_string_tolower(Z_STR_P(val))); |
462 | 28 | if (opline->op1_type != IS_CONST) { |
463 | 0 | opline->extended_value = alloc_cache_slots(op_array, 1) | (opline->extended_value & (ZEND_RETURNS_FUNCTION|ZEND_ISEMPTY|ZEND_FETCH_OBJ_FLAGS)); |
464 | 0 | } |
465 | 28 | break; |
466 | 0 | case ZEND_INIT_FCALL: |
467 | 0 | REQUIRES_STRING(val); |
468 | 0 | if (Z_REFCOUNT_P(val) == 1) { |
469 | 0 | zend_str_tolower(Z_STRVAL_P(val), Z_STRLEN_P(val)); |
470 | 0 | } else { |
471 | 0 | ZVAL_STR(&tmp, zend_string_tolower(Z_STR_P(val))); |
472 | 0 | zval_ptr_dtor_nogc(val); |
473 | 0 | val = &tmp; |
474 | 0 | } |
475 | 0 | opline->op2.constant = zend_optimizer_add_literal(op_array, val); |
476 | 0 | opline->result.num = alloc_cache_slots(op_array, 1); |
477 | 0 | break; |
478 | 53 | case ZEND_INIT_DYNAMIC_CALL: |
479 | 53 | if (Z_TYPE_P(val) == IS_STRING) { |
480 | 22 | if (zend_memrchr(Z_STRVAL_P(val), ':', Z_STRLEN_P(val))) { |
481 | 10 | return false; |
482 | 10 | } |
483 | | |
484 | 12 | if (zend_optimizer_classify_function(Z_STR_P(val), opline->extended_value)) { |
485 | | /* Dynamic call to various special functions must stay dynamic, |
486 | | * otherwise would drop a warning */ |
487 | 12 | return false; |
488 | 12 | } |
489 | | |
490 | 0 | opline->opcode = ZEND_INIT_FCALL_BY_NAME; |
491 | 0 | drop_leading_backslash(val); |
492 | 0 | opline->op2.constant = zend_optimizer_add_literal(op_array, val); |
493 | 0 | zend_optimizer_add_literal_string(op_array, zend_string_tolower(Z_STR_P(val))); |
494 | 0 | opline->result.num = alloc_cache_slots(op_array, 1); |
495 | 31 | } else { |
496 | 31 | opline->op2.constant = zend_optimizer_add_literal(op_array, val); |
497 | 31 | } |
498 | 31 | break; |
499 | 31 | case ZEND_INIT_METHOD_CALL: |
500 | 0 | REQUIRES_STRING(val); |
501 | 0 | opline->op2.constant = zend_optimizer_add_literal(op_array, val); |
502 | 0 | zend_optimizer_add_literal_string(op_array, zend_string_tolower(Z_STR_P(val))); |
503 | 0 | opline->result.num = alloc_cache_slots(op_array, 2); |
504 | 0 | break; |
505 | 2 | case ZEND_INIT_STATIC_METHOD_CALL: |
506 | 2 | REQUIRES_STRING(val); |
507 | 2 | opline->op2.constant = zend_optimizer_add_literal(op_array, val); |
508 | 2 | zend_optimizer_add_literal_string(op_array, zend_string_tolower(Z_STR_P(val))); |
509 | 2 | if (opline->op1_type != IS_CONST) { |
510 | 2 | opline->result.num = alloc_cache_slots(op_array, 2); |
511 | 2 | } |
512 | 2 | break; |
513 | 28 | case ZEND_ASSIGN_OBJ: |
514 | 32 | case ZEND_ASSIGN_OBJ_REF: |
515 | 56 | case ZEND_FETCH_OBJ_R: |
516 | 56 | case ZEND_FETCH_OBJ_W: |
517 | 56 | case ZEND_FETCH_OBJ_RW: |
518 | 56 | case ZEND_FETCH_OBJ_IS: |
519 | 56 | case ZEND_FETCH_OBJ_UNSET: |
520 | 56 | case ZEND_FETCH_OBJ_FUNC_ARG: |
521 | 56 | case ZEND_UNSET_OBJ: |
522 | 56 | case ZEND_PRE_INC_OBJ: |
523 | 64 | case ZEND_PRE_DEC_OBJ: |
524 | 64 | case ZEND_POST_INC_OBJ: |
525 | 64 | case ZEND_POST_DEC_OBJ: |
526 | 64 | TO_STRING_NOWARN(val); |
527 | 64 | opline->op2.constant = zend_optimizer_add_literal(op_array, val); |
528 | 64 | opline->extended_value = alloc_cache_slots(op_array, 3); |
529 | 64 | break; |
530 | 64 | case ZEND_ASSIGN_OBJ_OP: |
531 | 64 | TO_STRING_NOWARN(val); |
532 | 64 | opline->op2.constant = zend_optimizer_add_literal(op_array, val); |
533 | 64 | ZEND_ASSERT((opline + 1)->opcode == ZEND_OP_DATA); |
534 | 64 | (opline + 1)->extended_value = alloc_cache_slots(op_array, 3); |
535 | 64 | break; |
536 | 0 | case ZEND_ISSET_ISEMPTY_PROP_OBJ: |
537 | 0 | TO_STRING_NOWARN(val); |
538 | 0 | opline->op2.constant = zend_optimizer_add_literal(op_array, val); |
539 | 0 | opline->extended_value = alloc_cache_slots(op_array, 3) | (opline->extended_value & ZEND_ISEMPTY); |
540 | 0 | break; |
541 | 7 | case ZEND_ASSIGN_DIM_OP: |
542 | 46 | case ZEND_ISSET_ISEMPTY_DIM_OBJ: |
543 | 263 | case ZEND_ASSIGN_DIM: |
544 | 370 | case ZEND_UNSET_DIM: |
545 | 471 | case ZEND_FETCH_DIM_R: |
546 | 511 | case ZEND_FETCH_DIM_W: |
547 | 511 | case ZEND_FETCH_DIM_RW: |
548 | 511 | case ZEND_FETCH_DIM_IS: |
549 | 525 | case ZEND_FETCH_DIM_FUNC_ARG: |
550 | 525 | case ZEND_FETCH_DIM_UNSET: |
551 | 525 | case ZEND_FETCH_LIST_R: |
552 | 525 | case ZEND_FETCH_LIST_W: |
553 | 525 | if (Z_TYPE_P(val) == IS_STRING) { |
554 | 253 | zend_ulong index; |
555 | | |
556 | 253 | if (ZEND_HANDLE_NUMERIC(Z_STR_P(val), index)) { |
557 | 191 | ZVAL_LONG(&tmp, index); |
558 | 191 | opline->op2.constant = zend_optimizer_add_literal(op_array, &tmp); |
559 | 191 | zend_string_hash_val(Z_STR_P(val)); |
560 | 191 | zend_optimizer_add_literal(op_array, val); |
561 | 191 | Z_EXTRA(op_array->literals[opline->op2.constant]) = ZEND_EXTRA_VALUE; |
562 | 191 | break; |
563 | 191 | } |
564 | 253 | } |
565 | 334 | opline->op2.constant = zend_optimizer_add_literal(op_array, val); |
566 | 334 | break; |
567 | 4 | case ZEND_ADD_ARRAY_ELEMENT: |
568 | 8 | case ZEND_INIT_ARRAY: |
569 | 8 | if (Z_TYPE_P(val) == IS_STRING) { |
570 | 0 | zend_ulong index; |
571 | 0 | if (ZEND_HANDLE_NUMERIC(Z_STR_P(val), index)) { |
572 | 0 | zval_ptr_dtor_nogc(val); |
573 | 0 | ZVAL_LONG(val, index); |
574 | 0 | } |
575 | 0 | } |
576 | 8 | opline->op2.constant = zend_optimizer_add_literal(op_array, val); |
577 | 8 | break; |
578 | 0 | case ZEND_ROPE_INIT: |
579 | 1.74k | case ZEND_ROPE_ADD: |
580 | 1.74k | case ZEND_ROPE_END: |
581 | 1.85k | case ZEND_CONCAT: |
582 | 1.85k | case ZEND_FAST_CONCAT: |
583 | 1.85k | TO_STRING_NOWARN(val); |
584 | 1.84k | if (opline->opcode == ZEND_CONCAT && opline->op1_type == IS_CONST) { |
585 | 28 | opline->opcode = ZEND_FAST_CONCAT; |
586 | 28 | } |
587 | 1.84k | ZEND_FALLTHROUGH; |
588 | 3.97k | default: |
589 | 3.97k | opline->op2.constant = zend_optimizer_add_literal(op_array, val); |
590 | 3.97k | break; |
591 | 4.85k | } |
592 | | |
593 | 4.72k | opline->op2_type = IS_CONST; |
594 | 4.72k | if (Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_STRING) { |
595 | 2.40k | zend_string_hash_val(Z_STR(ZEND_OP2_LITERAL(opline))); |
596 | 2.40k | } |
597 | 4.72k | return true; |
598 | 4.85k | } |
599 | | |
600 | | bool zend_optimizer_replace_by_const(zend_op_array *op_array, |
601 | | zend_op *opline, |
602 | | uint8_t type, |
603 | | uint32_t var, |
604 | | zval *val) |
605 | 2.27k | { |
606 | 2.27k | zend_op *end = op_array->opcodes + op_array->last; |
607 | | |
608 | 2.32k | while (opline < end) { |
609 | 2.32k | if (opline->op1_type == type && |
610 | 1.96k | opline->op1.var == var) { |
611 | 1.94k | switch (opline->opcode) { |
612 | | /* In most cases IS_TMP_VAR operand may be used only once. |
613 | | * The operands are usually destroyed by the opcode handler. |
614 | | * However, there are some exception which keep the operand alive. In that case |
615 | | * we want to try to replace all uses of the temporary. |
616 | | */ |
617 | 0 | case ZEND_FETCH_LIST_R: |
618 | 6 | case ZEND_CASE: |
619 | 6 | case ZEND_CASE_STRICT: |
620 | 6 | case ZEND_SWITCH_LONG: |
621 | 6 | case ZEND_SWITCH_STRING: |
622 | 6 | case ZEND_MATCH: |
623 | 6 | case ZEND_MATCH_ERROR: |
624 | 12 | case ZEND_JMP_NULL: { |
625 | 12 | zend_op *end = op_array->opcodes + op_array->last; |
626 | 60 | while (opline < end) { |
627 | 60 | if (opline->op1_type == type && opline->op1.var == var) { |
628 | | /* If this opcode doesn't keep the operand alive, we're done. Check |
629 | | * this early, because op replacement may modify the opline. */ |
630 | 24 | bool is_last = opline->opcode != ZEND_FETCH_LIST_R |
631 | 24 | && opline->opcode != ZEND_CASE |
632 | 18 | && opline->opcode != ZEND_CASE_STRICT |
633 | 18 | && opline->opcode != ZEND_SWITCH_LONG |
634 | 18 | && opline->opcode != ZEND_SWITCH_STRING |
635 | 18 | && opline->opcode != ZEND_MATCH |
636 | 18 | && opline->opcode != ZEND_MATCH_ERROR |
637 | 18 | && opline->opcode != ZEND_JMP_NULL |
638 | 12 | && (opline->opcode != ZEND_FREE |
639 | 6 | || opline->extended_value != ZEND_FREE_ON_RETURN); |
640 | | |
641 | 24 | Z_TRY_ADDREF_P(val); |
642 | 24 | if (!zend_optimizer_update_op1_const(op_array, opline, val)) { |
643 | 0 | zval_ptr_dtor(val); |
644 | 0 | return false; |
645 | 0 | } |
646 | 24 | if (is_last) { |
647 | 12 | break; |
648 | 12 | } |
649 | 24 | } |
650 | 48 | opline++; |
651 | 48 | } |
652 | 12 | zval_ptr_dtor_nogc(val); |
653 | 12 | return true; |
654 | 12 | } |
655 | 4 | case ZEND_VERIFY_RETURN_TYPE: { |
656 | 4 | zend_arg_info *ret_info = op_array->arg_info - 1; |
657 | 4 | if (!ZEND_TYPE_CONTAINS_CODE(ret_info->type, Z_TYPE_P(val)) |
658 | 4 | || (op_array->fn_flags & ZEND_ACC_RETURN_REFERENCE)) { |
659 | 0 | return false; |
660 | 0 | } |
661 | 4 | MAKE_NOP(opline); |
662 | | |
663 | | /* zend_handle_loops_and_finally may inserts other oplines */ |
664 | 4 | do { |
665 | 4 | ++opline; |
666 | 4 | } while (opline->opcode != ZEND_RETURN && opline->opcode != ZEND_RETURN_BY_REF); |
667 | 4 | ZEND_ASSERT(opline->op1.var == var); |
668 | | |
669 | 4 | break; |
670 | 4 | } |
671 | 1.92k | default: |
672 | 1.92k | break; |
673 | 1.94k | } |
674 | 1.92k | return zend_optimizer_update_op1_const(op_array, opline, val); |
675 | 1.94k | } |
676 | | |
677 | 384 | if (opline->op2_type == type && |
678 | 342 | opline->op2.var == var) { |
679 | 330 | return zend_optimizer_update_op2_const(op_array, opline, val); |
680 | 330 | } |
681 | 54 | opline++; |
682 | 54 | } |
683 | | |
684 | 0 | return true; |
685 | 2.27k | } |
686 | | |
687 | | /* Update jump offsets after a jump was migrated to another opline */ |
688 | 30.3k | void zend_optimizer_migrate_jump(zend_op_array *op_array, zend_op *new_opline, zend_op *opline) { |
689 | 30.3k | switch (new_opline->opcode) { |
690 | 3.40k | case ZEND_JMP: |
691 | 3.40k | case ZEND_FAST_CALL: |
692 | 3.40k | ZEND_SET_OP_JMP_ADDR(new_opline, new_opline->op1, ZEND_OP1_JMP_ADDR(opline)); |
693 | 3.40k | break; |
694 | 1.82k | case ZEND_JMPZ: |
695 | 5.22k | case ZEND_JMPNZ: |
696 | 5.34k | case ZEND_JMPZ_EX: |
697 | 5.58k | case ZEND_JMPNZ_EX: |
698 | 5.83k | case ZEND_FE_RESET_R: |
699 | 5.84k | case ZEND_FE_RESET_RW: |
700 | 5.98k | case ZEND_JMP_SET: |
701 | 6.61k | case ZEND_COALESCE: |
702 | 6.64k | case ZEND_ASSERT_CHECK: |
703 | 10.3k | case ZEND_JMP_NULL: |
704 | 10.3k | case ZEND_BIND_INIT_STATIC_OR_JMP: |
705 | 10.3k | case ZEND_JMP_FRAMELESS: |
706 | 10.3k | ZEND_SET_OP_JMP_ADDR(new_opline, new_opline->op2, ZEND_OP2_JMP_ADDR(opline)); |
707 | 10.3k | break; |
708 | 256 | case ZEND_FE_FETCH_R: |
709 | 260 | case ZEND_FE_FETCH_RW: |
710 | 260 | new_opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, new_opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)); |
711 | 260 | break; |
712 | 0 | case ZEND_CATCH: |
713 | 0 | if (!(opline->extended_value & ZEND_LAST_CATCH)) { |
714 | 0 | ZEND_SET_OP_JMP_ADDR(new_opline, new_opline->op2, ZEND_OP2_JMP_ADDR(opline)); |
715 | 0 | } |
716 | 0 | break; |
717 | 0 | case ZEND_SWITCH_LONG: |
718 | 0 | case ZEND_SWITCH_STRING: |
719 | 0 | case ZEND_MATCH: |
720 | 0 | { |
721 | 0 | HashTable *jumptable = Z_ARRVAL(ZEND_OP2_LITERAL(opline)); |
722 | 0 | zval *zv; |
723 | 0 | ZEND_HASH_FOREACH_VAL(jumptable, zv) { |
724 | 0 | Z_LVAL_P(zv) = ZEND_OPLINE_NUM_TO_OFFSET(op_array, new_opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, Z_LVAL_P(zv))); |
725 | 0 | } ZEND_HASH_FOREACH_END(); |
726 | 0 | new_opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, new_opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)); |
727 | 0 | break; |
728 | 0 | } |
729 | 30.3k | } |
730 | 30.3k | } |
731 | | |
732 | | /* Shift jump offsets based on shiftlist */ |
733 | 48.0k | void zend_optimizer_shift_jump(zend_op_array *op_array, zend_op *opline, uint32_t *shiftlist) { |
734 | 48.0k | switch (opline->opcode) { |
735 | 4.47k | case ZEND_JMP: |
736 | 4.47k | case ZEND_FAST_CALL: |
737 | 4.47k | ZEND_SET_OP_JMP_ADDR(opline, opline->op1, ZEND_OP1_JMP_ADDR(opline) - shiftlist[ZEND_OP1_JMP_ADDR(opline) - op_array->opcodes]); |
738 | 4.47k | break; |
739 | 2.41k | case ZEND_JMPZ: |
740 | 6.31k | case ZEND_JMPNZ: |
741 | 6.48k | case ZEND_JMPZ_EX: |
742 | 6.83k | case ZEND_JMPNZ_EX: |
743 | 7.20k | case ZEND_FE_RESET_R: |
744 | 7.22k | case ZEND_FE_RESET_RW: |
745 | 7.40k | case ZEND_JMP_SET: |
746 | 8.52k | case ZEND_COALESCE: |
747 | 8.65k | case ZEND_ASSERT_CHECK: |
748 | 18.5k | case ZEND_JMP_NULL: |
749 | 18.5k | case ZEND_BIND_INIT_STATIC_OR_JMP: |
750 | 18.5k | case ZEND_JMP_FRAMELESS: |
751 | 18.5k | ZEND_SET_OP_JMP_ADDR(opline, opline->op2, ZEND_OP2_JMP_ADDR(opline) - shiftlist[ZEND_OP2_JMP_ADDR(opline) - op_array->opcodes]); |
752 | 18.5k | break; |
753 | 0 | case ZEND_CATCH: |
754 | 0 | if (!(opline->extended_value & ZEND_LAST_CATCH)) { |
755 | 0 | ZEND_SET_OP_JMP_ADDR(opline, opline->op2, ZEND_OP2_JMP_ADDR(opline) - shiftlist[ZEND_OP2_JMP_ADDR(opline) - op_array->opcodes]); |
756 | 0 | } |
757 | 0 | break; |
758 | 372 | case ZEND_FE_FETCH_R: |
759 | 388 | case ZEND_FE_FETCH_RW: |
760 | 388 | opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value) - shiftlist[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)]); |
761 | 388 | break; |
762 | 0 | case ZEND_SWITCH_LONG: |
763 | 8 | case ZEND_SWITCH_STRING: |
764 | 46 | case ZEND_MATCH: |
765 | 46 | { |
766 | 46 | HashTable *jumptable = Z_ARRVAL(ZEND_OP2_LITERAL(opline)); |
767 | 46 | zval *zv; |
768 | 310 | ZEND_HASH_FOREACH_VAL(jumptable, zv) { |
769 | 310 | Z_LVAL_P(zv) = ZEND_OPLINE_NUM_TO_OFFSET(op_array, opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, Z_LVAL_P(zv)) - shiftlist[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, Z_LVAL_P(zv))]); |
770 | 310 | } ZEND_HASH_FOREACH_END(); |
771 | 46 | opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value) - shiftlist[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)]); |
772 | 46 | break; |
773 | 8 | } |
774 | 48.0k | } |
775 | 48.0k | } |
776 | | |
777 | | static bool zend_optimizer_ignore_class(zval *ce_zv, zend_string *filename) |
778 | 75.1k | { |
779 | 75.1k | zend_class_entry *ce = Z_PTR_P(ce_zv); |
780 | | |
781 | 75.1k | if (ce->ce_flags & ZEND_ACC_PRELOADED) { |
782 | 0 | Bucket *ce_bucket = (Bucket*)((uintptr_t)ce_zv - XtOffsetOf(Bucket, val)); |
783 | 0 | size_t offset = ce_bucket - EG(class_table)->arData; |
784 | 0 | if (offset < EG(persistent_classes_count)) { |
785 | 0 | return false; |
786 | 0 | } |
787 | 0 | } |
788 | 75.1k | return ce->type == ZEND_USER_CLASS |
789 | 0 | && (!ce->info.user.filename || ce->info.user.filename != filename); |
790 | 75.1k | } |
791 | | |
792 | | static bool zend_optimizer_ignore_function(zval *fbc_zv, zend_string *filename) |
793 | 171k | { |
794 | 171k | zend_function *fbc = Z_PTR_P(fbc_zv); |
795 | | |
796 | 171k | if (fbc->type == ZEND_INTERNAL_FUNCTION) { |
797 | 171k | return false; |
798 | 171k | } else if (fbc->type == ZEND_USER_FUNCTION) { |
799 | 0 | if (fbc->op_array.fn_flags & ZEND_ACC_PRELOADED) { |
800 | 0 | Bucket *fbc_bucket = (Bucket*)((uintptr_t)fbc_zv - XtOffsetOf(Bucket, val)); |
801 | 0 | size_t offset = fbc_bucket - EG(function_table)->arData; |
802 | 0 | if (offset < EG(persistent_functions_count)) { |
803 | 0 | return false; |
804 | 0 | } |
805 | 0 | } |
806 | 0 | return !fbc->op_array.filename || fbc->op_array.filename != filename; |
807 | 0 | } else { |
808 | 0 | ZEND_ASSERT(fbc->type == ZEND_EVAL_CODE); |
809 | 0 | return true; |
810 | 0 | } |
811 | 171k | } |
812 | | |
813 | | zend_class_entry *zend_optimizer_get_class_entry( |
814 | 141k | const zend_script *script, const zend_op_array *op_array, zend_string *lcname) { |
815 | 141k | zend_class_entry *ce = script ? zend_hash_find_ptr(&script->class_table, lcname) : NULL; |
816 | 141k | if (ce) { |
817 | 49.7k | return ce; |
818 | 49.7k | } |
819 | | |
820 | 91.6k | zval *ce_zv = zend_hash_find(CG(class_table), lcname); |
821 | 91.6k | if (ce_zv && !zend_optimizer_ignore_class(ce_zv, op_array ? op_array->filename : NULL)) { |
822 | 75.1k | return Z_PTR_P(ce_zv); |
823 | 75.1k | } |
824 | | |
825 | 16.4k | if (op_array && op_array->scope && zend_string_equals_ci(op_array->scope->name, lcname)) { |
826 | 99 | return op_array->scope; |
827 | 99 | } |
828 | | |
829 | 16.3k | return NULL; |
830 | 16.4k | } |
831 | | |
832 | | zend_class_entry *zend_optimizer_get_class_entry_from_op1( |
833 | 133k | const zend_script *script, const zend_op_array *op_array, const zend_op *opline) { |
834 | 133k | if (opline->op1_type == IS_CONST) { |
835 | 128k | zval *op1 = CRT_CONSTANT(opline->op1); |
836 | 128k | if (Z_TYPE_P(op1) == IS_STRING) { |
837 | 128k | return zend_optimizer_get_class_entry(script, op_array, Z_STR_P(op1 + 1)); |
838 | 128k | } |
839 | 128k | } else if (opline->op1_type == IS_UNUSED && op_array->scope |
840 | 1.39k | && !(op_array->scope->ce_flags & ZEND_ACC_TRAIT) |
841 | 1.30k | && ((opline->op1.num & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_SELF |
842 | 811 | || ((opline->op1.num & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_STATIC |
843 | 535 | && (op_array->scope->ce_flags & ZEND_ACC_FINAL)))) { |
844 | 535 | return op_array->scope; |
845 | 535 | } |
846 | 4.45k | return NULL; |
847 | 133k | } |
848 | | |
849 | | const zend_class_constant *zend_fetch_class_const_info( |
850 | 7.40k | const zend_script *script, const zend_op_array *op_array, const zend_op *opline, bool *is_prototype) { |
851 | 7.40k | const zend_class_entry *ce = NULL; |
852 | 7.40k | bool is_static_reference = false; |
853 | | |
854 | 7.40k | if (!opline || !op_array || opline->op2_type != IS_CONST || Z_TYPE_P(CRT_CONSTANT(opline->op2)) != IS_STRING) { |
855 | 140 | return NULL; |
856 | 140 | } |
857 | 7.26k | if (opline->op1_type == IS_CONST) { |
858 | 6.36k | zval *op1 = CRT_CONSTANT(opline->op1); |
859 | 6.36k | if (Z_TYPE_P(op1) == IS_STRING) { |
860 | 6.36k | if (script) { |
861 | 6.36k | ce = zend_optimizer_get_class_entry(script, op_array, Z_STR_P(op1 + 1)); |
862 | 6.36k | } else { |
863 | 0 | zval *ce_zv = zend_hash_find(EG(class_table), Z_STR_P(op1 + 1)); |
864 | 0 | if (ce_zv && !zend_optimizer_ignore_class(ce_zv, op_array->filename)) { |
865 | 0 | ce = Z_PTR_P(ce_zv); |
866 | 0 | } |
867 | 0 | } |
868 | 6.36k | } |
869 | 6.36k | } else if (opline->op1_type == IS_UNUSED |
870 | 478 | && op_array->scope && !(op_array->scope->ce_flags & ZEND_ACC_TRAIT) |
871 | 382 | && !(op_array->fn_flags & ZEND_ACC_TRAIT_CLONE)) { |
872 | 382 | int fetch_type = opline->op1.num & ZEND_FETCH_CLASS_MASK; |
873 | 382 | if (fetch_type == ZEND_FETCH_CLASS_SELF) { |
874 | 246 | ce = op_array->scope; |
875 | 246 | } else if (fetch_type == ZEND_FETCH_CLASS_STATIC) { |
876 | 104 | ce = op_array->scope; |
877 | 104 | is_static_reference = true; |
878 | 104 | } else if (fetch_type == ZEND_FETCH_CLASS_PARENT) { |
879 | 32 | if (op_array->scope->ce_flags & ZEND_ACC_LINKED) { |
880 | 16 | ce = op_array->scope->parent; |
881 | 16 | } |
882 | 32 | } |
883 | 382 | } |
884 | 7.26k | if (!ce || (ce->ce_flags & ZEND_ACC_TRAIT)) { |
885 | 4.21k | return NULL; |
886 | 4.21k | } |
887 | 3.05k | zend_class_constant *const_info = zend_hash_find_ptr(&ce->constants_table, Z_STR_P(CRT_CONSTANT(opline->op2))); |
888 | 3.05k | if (!const_info) { |
889 | 426 | return NULL; |
890 | 426 | } |
891 | 2.62k | if ((ZEND_CLASS_CONST_FLAGS(const_info) & ZEND_ACC_DEPRECATED) |
892 | 2.55k | || ((ZEND_CLASS_CONST_FLAGS(const_info) & ZEND_ACC_PPP_MASK) != ZEND_ACC_PUBLIC && const_info->ce != op_array->scope)) { |
893 | 80 | return NULL; |
894 | 80 | } |
895 | 2.54k | *is_prototype = is_static_reference |
896 | 64 | && !(const_info->ce->ce_flags & ZEND_ACC_FINAL) && !(ZEND_CLASS_CONST_FLAGS(const_info) & ZEND_ACC_FINAL); |
897 | | |
898 | 2.54k | return const_info; |
899 | 2.62k | } |
900 | | |
901 | | zend_function *zend_optimizer_get_called_func( |
902 | | zend_script *script, zend_op_array *op_array, zend_op *opline, bool *is_prototype) |
903 | 382k | { |
904 | 382k | *is_prototype = false; |
905 | 382k | switch (opline->opcode) { |
906 | 210k | case ZEND_INIT_FCALL: |
907 | 210k | { |
908 | 210k | zend_string *function_name = Z_STR_P(CRT_CONSTANT(opline->op2)); |
909 | 210k | zend_function *func; |
910 | 210k | zval *func_zv; |
911 | 210k | if (script && (func = zend_hash_find_ptr(&script->function_table, function_name)) != NULL) { |
912 | 39.4k | return func; |
913 | 171k | } else if ((func_zv = zend_hash_find(EG(function_table), function_name)) != NULL) { |
914 | 171k | if (!zend_optimizer_ignore_function(func_zv, op_array->filename)) { |
915 | 171k | return Z_PTR_P(func_zv); |
916 | 171k | } |
917 | 171k | } |
918 | 0 | break; |
919 | 210k | } |
920 | 8.84k | case ZEND_INIT_FCALL_BY_NAME: |
921 | 11.7k | case ZEND_INIT_NS_FCALL_BY_NAME: |
922 | 11.7k | if (opline->op2_type == IS_CONST && Z_TYPE_P(CRT_CONSTANT(opline->op2)) == IS_STRING) { |
923 | 11.7k | zval *function_name = CRT_CONSTANT(opline->op2) + 1; |
924 | 11.7k | zend_function *func; |
925 | 11.7k | zval *func_zv; |
926 | 11.7k | if (script && (func = zend_hash_find_ptr(&script->function_table, Z_STR_P(function_name)))) { |
927 | 1.40k | return func; |
928 | 10.3k | } else if ((func_zv = zend_hash_find(EG(function_table), Z_STR_P(function_name))) != NULL) { |
929 | 10 | if (!zend_optimizer_ignore_function(func_zv, op_array->filename)) { |
930 | 10 | return Z_PTR_P(func_zv); |
931 | 10 | } |
932 | 10 | } |
933 | 11.7k | } |
934 | 10.3k | break; |
935 | 12.6k | case ZEND_INIT_STATIC_METHOD_CALL: |
936 | 12.6k | if (opline->op2_type == IS_CONST && Z_TYPE_P(CRT_CONSTANT(opline->op2)) == IS_STRING) { |
937 | 11.6k | zend_class_entry *ce = zend_optimizer_get_class_entry_from_op1( |
938 | 11.6k | script, op_array, opline); |
939 | 11.6k | if (ce) { |
940 | 8.53k | zend_string *func_name = Z_STR_P(CRT_CONSTANT(opline->op2) + 1); |
941 | 8.53k | zend_function *fbc = zend_hash_find_ptr(&ce->function_table, func_name); |
942 | 8.53k | if (fbc) { |
943 | 7.68k | bool is_public = (fbc->common.fn_flags & ZEND_ACC_PUBLIC) != 0; |
944 | 7.68k | bool same_scope = fbc->common.scope == op_array->scope; |
945 | 7.68k | if (is_public || same_scope) { |
946 | 7.50k | return fbc; |
947 | 7.50k | } |
948 | 7.68k | } |
949 | 8.53k | } |
950 | 11.6k | } |
951 | 5.11k | break; |
952 | 85.7k | case ZEND_INIT_METHOD_CALL: |
953 | 85.7k | if (opline->op1_type == IS_UNUSED |
954 | 1.21k | && opline->op2_type == IS_CONST && Z_TYPE_P(CRT_CONSTANT(opline->op2)) == IS_STRING |
955 | 1.15k | && op_array->scope |
956 | 1.13k | && !(op_array->fn_flags & ZEND_ACC_TRAIT_CLONE) |
957 | 1.13k | && !(op_array->scope->ce_flags & ZEND_ACC_TRAIT)) { |
958 | 1.06k | zend_string *method_name = Z_STR_P(CRT_CONSTANT(opline->op2) + 1); |
959 | 1.06k | zend_function *fbc = zend_hash_find_ptr( |
960 | 1.06k | &op_array->scope->function_table, method_name); |
961 | 1.06k | if (fbc) { |
962 | 868 | bool is_private = (fbc->common.fn_flags & ZEND_ACC_PRIVATE) != 0; |
963 | 868 | if (is_private) { |
964 | | /* Only use private method if in the same scope. We can't even use it |
965 | | * as a prototype, as it may be overridden with changed signature. */ |
966 | 192 | bool same_scope = fbc->common.scope == op_array->scope; |
967 | 192 | return same_scope ? fbc : NULL; |
968 | 192 | } |
969 | | /* Prototype methods are potentially overridden. fbc still contains useful type information. |
970 | | * Some optimizations may not be applied, like inlining or inferring the send-mode of superfluous args. |
971 | | * A method cannot be overridden if the class or method is final. */ |
972 | 676 | if ((fbc->common.fn_flags & ZEND_ACC_FINAL) == 0 && |
973 | 668 | (fbc->common.scope->ce_flags & ZEND_ACC_FINAL) == 0) { |
974 | 668 | *is_prototype = true; |
975 | 668 | } |
976 | 676 | return fbc; |
977 | 868 | } |
978 | 1.06k | } |
979 | 84.8k | break; |
980 | 84.8k | case ZEND_INIT_PARENT_PROPERTY_HOOK_CALL: { |
981 | 380 | zend_class_entry *scope = op_array->scope; |
982 | 380 | ZEND_ASSERT(scope != NULL); |
983 | 380 | if ((scope->ce_flags & ZEND_ACC_LINKED) && scope->parent) { |
984 | 372 | zend_class_entry *parent_scope = scope->parent; |
985 | 372 | zend_string *prop_name = Z_STR_P(CRT_CONSTANT(opline->op1)); |
986 | 372 | zend_property_hook_kind hook_kind = opline->op2.num; |
987 | 372 | zend_property_info *prop_info = zend_get_property_info(parent_scope, prop_name, /* silent */ true); |
988 | | |
989 | 372 | if (prop_info |
990 | 356 | && prop_info != ZEND_WRONG_PROPERTY_INFO |
991 | 340 | && !(prop_info->flags & ZEND_ACC_PRIVATE) |
992 | 340 | && prop_info->hooks) { |
993 | 140 | zend_function *fbc = prop_info->hooks[hook_kind]; |
994 | 140 | if (fbc) { |
995 | 132 | *is_prototype = false; |
996 | 132 | return fbc; |
997 | 132 | } |
998 | 140 | } |
999 | 372 | } |
1000 | 248 | break; |
1001 | 380 | } |
1002 | 61.4k | case ZEND_NEW: |
1003 | 61.4k | { |
1004 | 61.4k | zend_class_entry *ce = zend_optimizer_get_class_entry_from_op1( |
1005 | 61.4k | script, op_array, opline); |
1006 | 61.4k | if (ce && ce->type == ZEND_USER_CLASS) { |
1007 | 12.9k | return ce->constructor; |
1008 | 12.9k | } |
1009 | 48.4k | break; |
1010 | 61.4k | } |
1011 | 382k | } |
1012 | 149k | return NULL; |
1013 | 382k | } |
1014 | | |
1015 | 151k | uint32_t zend_optimizer_classify_function(zend_string *name, uint32_t num_args) { |
1016 | 151k | if (zend_string_equals_literal(name, "extract")) { |
1017 | 44 | return ZEND_FUNC_INDIRECT_VAR_ACCESS; |
1018 | 151k | } else if (zend_string_equals_literal(name, "compact")) { |
1019 | 50 | return ZEND_FUNC_INDIRECT_VAR_ACCESS; |
1020 | 151k | } else if (zend_string_equals_literal(name, "get_defined_vars")) { |
1021 | 124 | return ZEND_FUNC_INDIRECT_VAR_ACCESS; |
1022 | 151k | } else if (zend_string_equals_literal(name, "db2_execute")) { |
1023 | 0 | return ZEND_FUNC_INDIRECT_VAR_ACCESS; |
1024 | 151k | } else if (zend_string_equals_literal(name, "func_num_args")) { |
1025 | 18 | return ZEND_FUNC_VARARG; |
1026 | 151k | } else if (zend_string_equals_literal(name, "func_get_arg")) { |
1027 | 360 | return ZEND_FUNC_VARARG; |
1028 | 151k | } else if (zend_string_equals_literal(name, "func_get_args")) { |
1029 | 52 | return ZEND_FUNC_VARARG; |
1030 | 151k | } else { |
1031 | 151k | return 0; |
1032 | 151k | } |
1033 | 151k | } |
1034 | | |
1035 | 108 | zend_op *zend_optimizer_get_loop_var_def(const zend_op_array *op_array, zend_op *free_opline) { |
1036 | 108 | uint32_t var = free_opline->op1.var; |
1037 | 108 | ZEND_ASSERT(zend_optimizer_is_loop_var_free(free_opline)); |
1038 | | |
1039 | 1.07k | while (--free_opline >= op_array->opcodes) { |
1040 | 1.06k | if ((free_opline->result_type & (IS_TMP_VAR|IS_VAR)) && free_opline->result.var == var) { |
1041 | 106 | return free_opline; |
1042 | 106 | } |
1043 | 1.06k | } |
1044 | 2 | return NULL; |
1045 | 108 | } |
1046 | | |
1047 | | static void zend_optimize(zend_op_array *op_array, |
1048 | | zend_optimizer_ctx *ctx) |
1049 | 102k | { |
1050 | 102k | if (op_array->type == ZEND_EVAL_CODE) { |
1051 | 0 | return; |
1052 | 0 | } |
1053 | | |
1054 | 102k | if (ctx->debug_level & ZEND_DUMP_BEFORE_OPTIMIZER) { |
1055 | 0 | zend_dump_op_array(op_array, ZEND_DUMP_LIVE_RANGES, "before optimizer", NULL); |
1056 | 0 | } |
1057 | | |
1058 | | /* pass 1 (Simple local optimizations) |
1059 | | * - persistent constant substitution (true, false, null, etc) |
1060 | | * - constant casting (ADD expects numbers, CONCAT strings, etc) |
1061 | | * - constant expression evaluation |
1062 | | * - optimize constant conditional JMPs |
1063 | | * - pre-evaluate constant function calls |
1064 | | * - eliminate FETCH $GLOBALS followed by FETCH_DIM/UNSET_DIM/ISSET_ISEMPTY_DIM |
1065 | | */ |
1066 | 102k | if (ZEND_OPTIMIZER_PASS_1 & ctx->optimization_level) { |
1067 | 102k | zend_optimizer_pass1(op_array, ctx); |
1068 | 102k | if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_1) { |
1069 | 0 | zend_dump_op_array(op_array, 0, "after pass 1", NULL); |
1070 | 0 | } |
1071 | 102k | } |
1072 | | |
1073 | | /* pass 3: (Jump optimization) |
1074 | | * - optimize series of JMPs |
1075 | | */ |
1076 | 102k | if (ZEND_OPTIMIZER_PASS_3 & ctx->optimization_level) { |
1077 | 102k | zend_optimizer_pass3(op_array, ctx); |
1078 | 102k | if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_3) { |
1079 | 0 | zend_dump_op_array(op_array, 0, "after pass 3", NULL); |
1080 | 0 | } |
1081 | 102k | } |
1082 | | |
1083 | | /* pass 4: |
1084 | | * - INIT_FCALL_BY_NAME -> DO_FCALL |
1085 | | */ |
1086 | 102k | if (ZEND_OPTIMIZER_PASS_4 & ctx->optimization_level) { |
1087 | 102k | zend_optimize_func_calls(op_array, ctx); |
1088 | 102k | if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_4) { |
1089 | 0 | zend_dump_op_array(op_array, 0, "after pass 4", NULL); |
1090 | 0 | } |
1091 | 102k | } |
1092 | | |
1093 | | /* pass 5: |
1094 | | * - CFG optimization |
1095 | | */ |
1096 | 102k | if (ZEND_OPTIMIZER_PASS_5 & ctx->optimization_level) { |
1097 | 102k | zend_optimize_cfg(op_array, ctx); |
1098 | 102k | if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_5) { |
1099 | 0 | zend_dump_op_array(op_array, 0, "after pass 5", NULL); |
1100 | 0 | } |
1101 | 102k | } |
1102 | | |
1103 | | /* pass 6: |
1104 | | * - DFA optimization |
1105 | | */ |
1106 | 102k | if ((ZEND_OPTIMIZER_PASS_6 & ctx->optimization_level) && |
1107 | 102k | !(ZEND_OPTIMIZER_PASS_7 & ctx->optimization_level)) { |
1108 | 0 | zend_optimize_dfa(op_array, ctx); |
1109 | 0 | if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_6) { |
1110 | 0 | zend_dump_op_array(op_array, 0, "after pass 6", NULL); |
1111 | 0 | } |
1112 | 0 | } |
1113 | | |
1114 | | /* pass 9: |
1115 | | * - Optimize temp variables usage |
1116 | | */ |
1117 | 102k | if ((ZEND_OPTIMIZER_PASS_9 & ctx->optimization_level) && |
1118 | 102k | !(ZEND_OPTIMIZER_PASS_7 & ctx->optimization_level)) { |
1119 | 0 | zend_optimize_temporary_variables(op_array, ctx); |
1120 | 0 | if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_9) { |
1121 | 0 | zend_dump_op_array(op_array, 0, "after pass 9", NULL); |
1122 | 0 | } |
1123 | 0 | } |
1124 | | |
1125 | | /* pass 10: |
1126 | | * - remove NOPs |
1127 | | */ |
1128 | 102k | if (((ZEND_OPTIMIZER_PASS_10|ZEND_OPTIMIZER_PASS_5) & ctx->optimization_level) == ZEND_OPTIMIZER_PASS_10) { |
1129 | 0 | zend_optimizer_nop_removal(op_array, ctx); |
1130 | 0 | if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_10) { |
1131 | 0 | zend_dump_op_array(op_array, 0, "after pass 10", NULL); |
1132 | 0 | } |
1133 | 0 | } |
1134 | | |
1135 | | /* pass 11: |
1136 | | * - Compact literals table |
1137 | | */ |
1138 | 102k | if ((ZEND_OPTIMIZER_PASS_11 & ctx->optimization_level) && |
1139 | 102k | (!(ZEND_OPTIMIZER_PASS_6 & ctx->optimization_level) || |
1140 | 102k | !(ZEND_OPTIMIZER_PASS_7 & ctx->optimization_level))) { |
1141 | 0 | zend_optimizer_compact_literals(op_array, ctx); |
1142 | 0 | if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_11) { |
1143 | 0 | zend_dump_op_array(op_array, 0, "after pass 11", NULL); |
1144 | 0 | } |
1145 | 0 | } |
1146 | | |
1147 | 102k | if ((ZEND_OPTIMIZER_PASS_13 & ctx->optimization_level) && |
1148 | 102k | (!(ZEND_OPTIMIZER_PASS_6 & ctx->optimization_level) || |
1149 | 102k | !(ZEND_OPTIMIZER_PASS_7 & ctx->optimization_level))) { |
1150 | 0 | zend_optimizer_compact_vars(op_array); |
1151 | 0 | if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_13) { |
1152 | 0 | zend_dump_op_array(op_array, 0, "after pass 13", NULL); |
1153 | 0 | } |
1154 | 0 | } |
1155 | | |
1156 | 102k | if (ZEND_OPTIMIZER_PASS_7 & ctx->optimization_level) { |
1157 | 102k | return; |
1158 | 102k | } |
1159 | | |
1160 | 0 | if (ctx->debug_level & ZEND_DUMP_AFTER_OPTIMIZER) { |
1161 | 0 | zend_dump_op_array(op_array, 0, "after optimizer", NULL); |
1162 | 0 | } |
1163 | 0 | } |
1164 | | |
1165 | | static void zend_revert_pass_two(zend_op_array *op_array) |
1166 | 102k | { |
1167 | 102k | zend_op *opline, *end; |
1168 | | |
1169 | 102k | ZEND_ASSERT((op_array->fn_flags & ZEND_ACC_DONE_PASS_TWO) != 0); |
1170 | | |
1171 | 102k | opline = op_array->opcodes; |
1172 | 102k | end = opline + op_array->last; |
1173 | 2.70M | while (opline < end) { |
1174 | 2.60M | if (opline->op1_type == IS_CONST) { |
1175 | 459k | ZEND_PASS_TWO_UNDO_CONSTANT(op_array, opline, opline->op1); |
1176 | 459k | } |
1177 | 2.60M | if (opline->op2_type == IS_CONST) { |
1178 | 628k | ZEND_PASS_TWO_UNDO_CONSTANT(op_array, opline, opline->op2); |
1179 | 628k | } |
1180 | | /* reset smart branch flags IS_SMART_BRANCH_JMP[N]Z */ |
1181 | 2.60M | opline->result_type &= (IS_TMP_VAR|IS_VAR|IS_CV|IS_CONST); |
1182 | 2.60M | opline++; |
1183 | 2.60M | } |
1184 | 102k | #if !ZEND_USE_ABS_CONST_ADDR |
1185 | 102k | if (op_array->literals) { |
1186 | 102k | zval *literals = emalloc(sizeof(zval) * op_array->last_literal); |
1187 | 102k | memcpy(literals, op_array->literals, sizeof(zval) * op_array->last_literal); |
1188 | 102k | op_array->literals = literals; |
1189 | 102k | } |
1190 | 102k | #endif |
1191 | | |
1192 | 102k | op_array->fn_flags &= ~ZEND_ACC_DONE_PASS_TWO; |
1193 | 102k | } |
1194 | | |
1195 | | static void zend_redo_pass_two(zend_op_array *op_array) |
1196 | 28.6k | { |
1197 | 28.6k | zend_op *opline, *end; |
1198 | | #if ZEND_USE_ABS_JMP_ADDR && !ZEND_USE_ABS_CONST_ADDR |
1199 | | zend_op *old_opcodes = op_array->opcodes; |
1200 | | #endif |
1201 | | |
1202 | 28.6k | ZEND_ASSERT((op_array->fn_flags & ZEND_ACC_DONE_PASS_TWO) == 0); |
1203 | | |
1204 | 28.6k | #if !ZEND_USE_ABS_CONST_ADDR |
1205 | 28.6k | if (op_array->last_literal) { |
1206 | 28.6k | op_array->opcodes = (zend_op *) erealloc(op_array->opcodes, |
1207 | 28.6k | ZEND_MM_ALIGNED_SIZE_EX(sizeof(zend_op) * op_array->last, 16) + |
1208 | 28.6k | sizeof(zval) * op_array->last_literal); |
1209 | 28.6k | memcpy(((char*)op_array->opcodes) + ZEND_MM_ALIGNED_SIZE_EX(sizeof(zend_op) * op_array->last, 16), |
1210 | 28.6k | op_array->literals, sizeof(zval) * op_array->last_literal); |
1211 | 28.6k | efree(op_array->literals); |
1212 | 28.6k | op_array->literals = (zval*)(((char*)op_array->opcodes) + ZEND_MM_ALIGNED_SIZE_EX(sizeof(zend_op) * op_array->last, 16)); |
1213 | 28.6k | } else { |
1214 | 20 | if (op_array->literals) { |
1215 | 20 | efree(op_array->literals); |
1216 | 20 | } |
1217 | 20 | op_array->literals = NULL; |
1218 | 20 | } |
1219 | 28.6k | #endif |
1220 | | |
1221 | 28.6k | opline = op_array->opcodes; |
1222 | 28.6k | end = opline + op_array->last; |
1223 | 991k | while (opline < end) { |
1224 | 962k | if (opline->op1_type == IS_CONST) { |
1225 | 217k | ZEND_PASS_TWO_UPDATE_CONSTANT(op_array, opline, opline->op1); |
1226 | 217k | } |
1227 | 962k | if (opline->op2_type == IS_CONST) { |
1228 | 177k | ZEND_PASS_TWO_UPDATE_CONSTANT(op_array, opline, opline->op2); |
1229 | 177k | } |
1230 | | /* fix jumps to point to new array */ |
1231 | 962k | switch (opline->opcode) { |
1232 | | #if ZEND_USE_ABS_JMP_ADDR && !ZEND_USE_ABS_CONST_ADDR |
1233 | | case ZEND_JMP: |
1234 | | case ZEND_FAST_CALL: |
1235 | | opline->op1.jmp_addr = &op_array->opcodes[opline->op1.jmp_addr - old_opcodes]; |
1236 | | break; |
1237 | | case ZEND_JMPZ: |
1238 | | case ZEND_JMPNZ: |
1239 | | case ZEND_JMPZ_EX: |
1240 | | case ZEND_JMPNZ_EX: |
1241 | | case ZEND_JMP_SET: |
1242 | | case ZEND_COALESCE: |
1243 | | case ZEND_FE_RESET_R: |
1244 | | case ZEND_FE_RESET_RW: |
1245 | | case ZEND_ASSERT_CHECK: |
1246 | | case ZEND_JMP_NULL: |
1247 | | case ZEND_BIND_INIT_STATIC_OR_JMP: |
1248 | | case ZEND_JMP_FRAMELESS: |
1249 | | opline->op2.jmp_addr = &op_array->opcodes[opline->op2.jmp_addr - old_opcodes]; |
1250 | | break; |
1251 | | case ZEND_CATCH: |
1252 | | if (!(opline->extended_value & ZEND_LAST_CATCH)) { |
1253 | | opline->op2.jmp_addr = &op_array->opcodes[opline->op2.jmp_addr - old_opcodes]; |
1254 | | } |
1255 | | break; |
1256 | | case ZEND_FE_FETCH_R: |
1257 | | case ZEND_FE_FETCH_RW: |
1258 | | case ZEND_SWITCH_LONG: |
1259 | | case ZEND_SWITCH_STRING: |
1260 | | case ZEND_MATCH: |
1261 | | /* relative extended_value don't have to be changed */ |
1262 | | break; |
1263 | | #endif |
1264 | 159 | case ZEND_IS_IDENTICAL: |
1265 | 221 | case ZEND_IS_NOT_IDENTICAL: |
1266 | 10.1k | case ZEND_IS_EQUAL: |
1267 | 10.5k | case ZEND_IS_NOT_EQUAL: |
1268 | 12.3k | case ZEND_IS_SMALLER: |
1269 | 12.9k | case ZEND_IS_SMALLER_OR_EQUAL: |
1270 | 13.0k | case ZEND_CASE: |
1271 | 13.0k | case ZEND_CASE_STRICT: |
1272 | 13.0k | case ZEND_ISSET_ISEMPTY_CV: |
1273 | 13.1k | case ZEND_ISSET_ISEMPTY_VAR: |
1274 | 13.5k | case ZEND_ISSET_ISEMPTY_DIM_OBJ: |
1275 | 13.7k | case ZEND_ISSET_ISEMPTY_PROP_OBJ: |
1276 | 13.7k | case ZEND_ISSET_ISEMPTY_STATIC_PROP: |
1277 | 13.8k | case ZEND_INSTANCEOF: |
1278 | 14.0k | case ZEND_TYPE_CHECK: |
1279 | 14.0k | case ZEND_DEFINED: |
1280 | 14.0k | case ZEND_IN_ARRAY: |
1281 | 14.0k | case ZEND_ARRAY_KEY_EXISTS: |
1282 | 14.0k | if (opline->result_type & IS_TMP_VAR) { |
1283 | | /* reinitialize result_type of smart branch instructions */ |
1284 | 14.0k | if (opline + 1 < end) { |
1285 | 14.0k | if ((opline+1)->opcode == ZEND_JMPZ |
1286 | 9.88k | && (opline+1)->op1_type == IS_TMP_VAR |
1287 | 9.88k | && (opline+1)->op1.var == opline->result.var) { |
1288 | 9.88k | opline->result_type = IS_SMART_BRANCH_JMPZ | IS_TMP_VAR; |
1289 | 9.88k | } else if ((opline+1)->opcode == ZEND_JMPNZ |
1290 | 1.68k | && (opline+1)->op1_type == IS_TMP_VAR |
1291 | 1.68k | && (opline+1)->op1.var == opline->result.var) { |
1292 | 1.68k | opline->result_type = IS_SMART_BRANCH_JMPNZ | IS_TMP_VAR; |
1293 | 1.68k | } |
1294 | 14.0k | } |
1295 | 14.0k | } |
1296 | 14.0k | break; |
1297 | 962k | } |
1298 | 962k | ZEND_VM_SET_OPCODE_HANDLER(opline); |
1299 | 962k | opline++; |
1300 | 962k | } |
1301 | | |
1302 | 28.6k | op_array->fn_flags |= ZEND_ACC_DONE_PASS_TWO; |
1303 | 28.6k | } |
1304 | | |
1305 | | static void zend_redo_pass_two_ex(zend_op_array *op_array, zend_ssa *ssa) |
1306 | 74.1k | { |
1307 | 74.1k | zend_op *opline, *end; |
1308 | | #if ZEND_USE_ABS_JMP_ADDR && !ZEND_USE_ABS_CONST_ADDR |
1309 | | zend_op *old_opcodes = op_array->opcodes; |
1310 | | #endif |
1311 | | |
1312 | 74.1k | ZEND_ASSERT((op_array->fn_flags & ZEND_ACC_DONE_PASS_TWO) == 0); |
1313 | | |
1314 | 74.1k | #if !ZEND_USE_ABS_CONST_ADDR |
1315 | 74.1k | if (op_array->last_literal) { |
1316 | 72.3k | op_array->opcodes = (zend_op *) erealloc(op_array->opcodes, |
1317 | 72.3k | ZEND_MM_ALIGNED_SIZE_EX(sizeof(zend_op) * op_array->last, 16) + |
1318 | 72.3k | sizeof(zval) * op_array->last_literal); |
1319 | 72.3k | memcpy(((char*)op_array->opcodes) + ZEND_MM_ALIGNED_SIZE_EX(sizeof(zend_op) * op_array->last, 16), |
1320 | 72.3k | op_array->literals, sizeof(zval) * op_array->last_literal); |
1321 | 72.3k | efree(op_array->literals); |
1322 | 72.3k | op_array->literals = (zval*)(((char*)op_array->opcodes) + ZEND_MM_ALIGNED_SIZE_EX(sizeof(zend_op) * op_array->last, 16)); |
1323 | 72.3k | } else { |
1324 | 1.80k | if (op_array->literals) { |
1325 | 1.79k | efree(op_array->literals); |
1326 | 1.79k | } |
1327 | 1.80k | op_array->literals = NULL; |
1328 | 1.80k | } |
1329 | 74.1k | #endif |
1330 | | |
1331 | 74.1k | opline = op_array->opcodes; |
1332 | 74.1k | end = opline + op_array->last; |
1333 | 1.60M | while (opline < end) { |
1334 | 1.53M | zend_ssa_op *ssa_op = &ssa->ops[opline - op_array->opcodes]; |
1335 | 1.53M | uint32_t op1_info = opline->op1_type == IS_UNUSED ? 0 : (OP1_INFO() & (MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_KEY_ANY)); |
1336 | 1.53M | uint32_t op2_info = opline->op1_type == IS_UNUSED ? 0 : (OP2_INFO() & (MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_KEY_ANY)); |
1337 | 1.53M | uint32_t res_info = |
1338 | 1.53M | (opline->opcode == ZEND_PRE_INC || |
1339 | 1.52M | opline->opcode == ZEND_PRE_DEC || |
1340 | 1.52M | opline->opcode == ZEND_POST_INC || |
1341 | 1.52M | opline->opcode == ZEND_POST_DEC) ? |
1342 | 6.06k | ((ssa->ops[opline - op_array->opcodes].op1_def >= 0) ? (OP1_DEF_INFO() & (MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_KEY_ANY)) : MAY_BE_ANY) : |
1343 | 1.53M | (opline->result_type == IS_UNUSED ? 0 : (RES_INFO() & (MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_KEY_ANY))); |
1344 | | |
1345 | 1.53M | if (opline->op1_type == IS_CONST) { |
1346 | 226k | ZEND_PASS_TWO_UPDATE_CONSTANT(op_array, opline, opline->op1); |
1347 | 226k | } |
1348 | 1.53M | if (opline->op2_type == IS_CONST) { |
1349 | 437k | ZEND_PASS_TWO_UPDATE_CONSTANT(op_array, opline, opline->op2); |
1350 | 437k | } |
1351 | | |
1352 | | /* fix jumps to point to new array */ |
1353 | 1.53M | switch (opline->opcode) { |
1354 | | #if ZEND_USE_ABS_JMP_ADDR && !ZEND_USE_ABS_CONST_ADDR |
1355 | | case ZEND_JMP: |
1356 | | case ZEND_FAST_CALL: |
1357 | | opline->op1.jmp_addr = &op_array->opcodes[opline->op1.jmp_addr - old_opcodes]; |
1358 | | break; |
1359 | | case ZEND_JMPZ: |
1360 | | case ZEND_JMPNZ: |
1361 | | case ZEND_JMPZ_EX: |
1362 | | case ZEND_JMPNZ_EX: |
1363 | | case ZEND_JMP_SET: |
1364 | | case ZEND_COALESCE: |
1365 | | case ZEND_FE_RESET_R: |
1366 | | case ZEND_FE_RESET_RW: |
1367 | | case ZEND_ASSERT_CHECK: |
1368 | | case ZEND_JMP_NULL: |
1369 | | case ZEND_BIND_INIT_STATIC_OR_JMP: |
1370 | | case ZEND_JMP_FRAMELESS: |
1371 | | opline->op2.jmp_addr = &op_array->opcodes[opline->op2.jmp_addr - old_opcodes]; |
1372 | | break; |
1373 | | case ZEND_CATCH: |
1374 | | if (!(opline->extended_value & ZEND_LAST_CATCH)) { |
1375 | | opline->op2.jmp_addr = &op_array->opcodes[opline->op2.jmp_addr - old_opcodes]; |
1376 | | } |
1377 | | break; |
1378 | | case ZEND_FE_FETCH_R: |
1379 | | case ZEND_FE_FETCH_RW: |
1380 | | case ZEND_SWITCH_LONG: |
1381 | | case ZEND_SWITCH_STRING: |
1382 | | case ZEND_MATCH: |
1383 | | /* relative extended_value don't have to be changed */ |
1384 | | break; |
1385 | | #endif |
1386 | 1.61k | case ZEND_IS_IDENTICAL: |
1387 | 2.14k | case ZEND_IS_NOT_IDENTICAL: |
1388 | 5.16k | case ZEND_IS_EQUAL: |
1389 | 7.11k | case ZEND_IS_NOT_EQUAL: |
1390 | 14.6k | case ZEND_IS_SMALLER: |
1391 | 16.6k | case ZEND_IS_SMALLER_OR_EQUAL: |
1392 | 16.6k | case ZEND_CASE: |
1393 | 16.7k | case ZEND_CASE_STRICT: |
1394 | 16.8k | case ZEND_ISSET_ISEMPTY_CV: |
1395 | 16.8k | case ZEND_ISSET_ISEMPTY_VAR: |
1396 | 18.8k | case ZEND_ISSET_ISEMPTY_DIM_OBJ: |
1397 | 19.4k | case ZEND_ISSET_ISEMPTY_PROP_OBJ: |
1398 | 19.5k | case ZEND_ISSET_ISEMPTY_STATIC_PROP: |
1399 | 19.7k | case ZEND_INSTANCEOF: |
1400 | 20.5k | case ZEND_TYPE_CHECK: |
1401 | 20.6k | case ZEND_DEFINED: |
1402 | 20.6k | case ZEND_IN_ARRAY: |
1403 | 20.7k | case ZEND_ARRAY_KEY_EXISTS: |
1404 | 20.7k | if (opline->result_type & IS_TMP_VAR) { |
1405 | | /* reinitialize result_type of smart branch instructions */ |
1406 | 20.5k | if (opline + 1 < end) { |
1407 | 20.5k | if ((opline+1)->opcode == ZEND_JMPZ |
1408 | 4.47k | && (opline+1)->op1_type == IS_TMP_VAR |
1409 | 4.46k | && (opline+1)->op1.var == opline->result.var) { |
1410 | 4.46k | opline->result_type = IS_SMART_BRANCH_JMPZ | IS_TMP_VAR; |
1411 | 16.1k | } else if ((opline+1)->opcode == ZEND_JMPNZ |
1412 | 5.12k | && (opline+1)->op1_type == IS_TMP_VAR |
1413 | 5.12k | && (opline+1)->op1.var == opline->result.var) { |
1414 | 5.12k | opline->result_type = IS_SMART_BRANCH_JMPNZ | IS_TMP_VAR; |
1415 | 5.12k | } |
1416 | 20.5k | } |
1417 | 20.5k | } |
1418 | 20.7k | break; |
1419 | 1.53M | } |
1420 | | #ifdef ZEND_VERIFY_TYPE_INFERENCE |
1421 | | if (ssa_op->op1_use >= 0) { |
1422 | | opline->op1_use_type = ssa->var_info[ssa_op->op1_use].type; |
1423 | | } |
1424 | | if (ssa_op->op2_use >= 0) { |
1425 | | opline->op2_use_type = ssa->var_info[ssa_op->op2_use].type; |
1426 | | } |
1427 | | if (ssa_op->result_use >= 0) { |
1428 | | opline->result_use_type = ssa->var_info[ssa_op->result_use].type; |
1429 | | } |
1430 | | if (ssa_op->op1_def >= 0) { |
1431 | | opline->op1_def_type = ssa->var_info[ssa_op->op1_def].type; |
1432 | | } |
1433 | | if (ssa_op->op2_def >= 0) { |
1434 | | opline->op2_def_type = ssa->var_info[ssa_op->op2_def].type; |
1435 | | } |
1436 | | if (ssa_op->result_def >= 0) { |
1437 | | opline->result_def_type = ssa->var_info[ssa_op->result_def].type; |
1438 | | } |
1439 | | #endif |
1440 | 1.53M | zend_vm_set_opcode_handler_ex(opline, op1_info, op2_info, res_info); |
1441 | 1.53M | opline++; |
1442 | 1.53M | } |
1443 | | |
1444 | 74.1k | op_array->fn_flags |= ZEND_ACC_DONE_PASS_TWO; |
1445 | 74.1k | } |
1446 | | |
1447 | | static void zend_optimize_op_array(zend_op_array *op_array, |
1448 | | zend_optimizer_ctx *ctx) |
1449 | 0 | { |
1450 | | /* Revert pass_two() */ |
1451 | 0 | zend_revert_pass_two(op_array); |
1452 | | |
1453 | | /* Do actual optimizations */ |
1454 | 0 | zend_optimize(op_array, ctx); |
1455 | | |
1456 | | /* Redo pass_two() */ |
1457 | 0 | zend_redo_pass_two(op_array); |
1458 | |
|
1459 | 0 | if (op_array->live_range) { |
1460 | 0 | zend_recalc_live_ranges(op_array, NULL); |
1461 | 0 | } |
1462 | 0 | } |
1463 | | |
1464 | | static void zend_adjust_fcall_stack_size(zend_op_array *op_array, zend_optimizer_ctx *ctx) |
1465 | 0 | { |
1466 | 0 | zend_function *func; |
1467 | 0 | zend_op *opline, *end; |
1468 | |
|
1469 | 0 | opline = op_array->opcodes; |
1470 | 0 | end = opline + op_array->last; |
1471 | 0 | while (opline < end) { |
1472 | 0 | if (opline->opcode == ZEND_INIT_FCALL) { |
1473 | 0 | func = zend_hash_find_ptr( |
1474 | 0 | &ctx->script->function_table, |
1475 | 0 | Z_STR_P(RT_CONSTANT(opline, opline->op2))); |
1476 | 0 | if (func) { |
1477 | 0 | opline->op1.num = zend_vm_calc_used_stack(opline->extended_value, func); |
1478 | 0 | } |
1479 | 0 | } |
1480 | 0 | opline++; |
1481 | 0 | } |
1482 | 0 | } |
1483 | | |
1484 | | static void zend_adjust_fcall_stack_size_graph(zend_op_array *op_array) |
1485 | 102k | { |
1486 | 102k | zend_func_info *func_info = ZEND_FUNC_INFO(op_array); |
1487 | | |
1488 | 102k | if (func_info) { |
1489 | 74.1k | zend_call_info *call_info =func_info->callee_info; |
1490 | | |
1491 | 152k | while (call_info) { |
1492 | 78.7k | zend_op *opline = call_info->caller_init_opline; |
1493 | | |
1494 | 78.7k | if (opline && call_info->callee_func && opline->opcode == ZEND_INIT_FCALL) { |
1495 | 75.0k | ZEND_ASSERT(!call_info->is_prototype); |
1496 | 75.0k | opline->op1.num = zend_vm_calc_used_stack(opline->extended_value, call_info->callee_func); |
1497 | 75.0k | } |
1498 | 78.7k | call_info = call_info->next_callee; |
1499 | 78.7k | } |
1500 | 74.1k | } |
1501 | 102k | } |
1502 | | |
1503 | 50.1k | static bool needs_live_range(zend_op_array *op_array, zend_op *def_opline) { |
1504 | 50.1k | zend_func_info *func_info = ZEND_FUNC_INFO(op_array); |
1505 | 50.1k | zend_ssa_op *ssa_op = &func_info->ssa.ops[def_opline - op_array->opcodes]; |
1506 | 50.1k | int ssa_var = ssa_op->result_def; |
1507 | 50.1k | if (ssa_var < 0) { |
1508 | | /* Be conservative. */ |
1509 | 148 | return true; |
1510 | 148 | } |
1511 | | |
1512 | | /* If the variable is used by a PHI, this may be the assignment of the final branch of a |
1513 | | * ternary/etc structure. While this is where the live range starts, the value from the other |
1514 | | * branch may also be used. As such, use the type of the PHI node for the following check. */ |
1515 | 49.9k | if (func_info->ssa.vars[ssa_var].phi_use_chain) { |
1516 | 683 | ssa_var = func_info->ssa.vars[ssa_var].phi_use_chain->ssa_var; |
1517 | 683 | } |
1518 | | |
1519 | 49.9k | uint32_t type = func_info->ssa.var_info[ssa_var].type; |
1520 | 49.9k | return (type & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF)) != 0; |
1521 | 50.1k | } |
1522 | | |
1523 | | static void zend_foreach_op_array_helper( |
1524 | 205k | zend_op_array *op_array, zend_op_array_func_t func, void *context) { |
1525 | 205k | func(op_array, context); |
1526 | 221k | for (uint32_t i = 0; i < op_array->num_dynamic_func_defs; i++) { |
1527 | 15.5k | zend_foreach_op_array_helper(op_array->dynamic_func_defs[i], func, context); |
1528 | 15.5k | } |
1529 | 205k | } |
1530 | | |
1531 | | void zend_foreach_op_array(zend_script *script, zend_op_array_func_t func, void *context) |
1532 | 120k | { |
1533 | 120k | zval *zv; |
1534 | 120k | zend_op_array *op_array; |
1535 | | |
1536 | 120k | zend_foreach_op_array_helper(&script->main_op_array, func, context); |
1537 | | |
1538 | 294k | ZEND_HASH_MAP_FOREACH_PTR(&script->function_table, op_array) { |
1539 | 294k | zend_foreach_op_array_helper(op_array, func, context); |
1540 | 294k | } ZEND_HASH_FOREACH_END(); |
1541 | | |
1542 | 339k | ZEND_HASH_MAP_FOREACH_VAL(&script->class_table, zv) { |
1543 | 339k | if (Z_TYPE_P(zv) == IS_ALIAS_PTR) { |
1544 | 0 | continue; |
1545 | 0 | } |
1546 | 48.8k | zend_class_entry *ce = Z_CE_P(zv); |
1547 | 206k | ZEND_HASH_MAP_FOREACH_PTR(&ce->function_table, op_array) { |
1548 | 206k | if (op_array->scope == ce |
1549 | 40.6k | && op_array->type == ZEND_USER_FUNCTION |
1550 | 40.6k | && !(op_array->fn_flags & ZEND_ACC_ABSTRACT) |
1551 | 39.0k | && !(op_array->fn_flags & ZEND_ACC_TRAIT_CLONE)) { |
1552 | 39.0k | zend_foreach_op_array_helper(op_array, func, context); |
1553 | 39.0k | } |
1554 | 206k | } ZEND_HASH_FOREACH_END(); |
1555 | | |
1556 | 48.8k | zend_property_info *property; |
1557 | 163k | ZEND_HASH_MAP_FOREACH_PTR(&ce->properties_info, property) { |
1558 | 163k | zend_function **hooks = property->hooks; |
1559 | 163k | if (property->ce == ce && property->hooks) { |
1560 | 8.89k | for (uint32_t i = 0; i < ZEND_PROPERTY_HOOK_COUNT; i++) { |
1561 | 5.92k | zend_function *hook = hooks[i]; |
1562 | 5.92k | if (hook && hook->common.scope == ce && !(hooks[i]->op_array.fn_flags & ZEND_ACC_TRAIT_CLONE)) { |
1563 | 3.85k | zend_foreach_op_array_helper(&hooks[i]->op_array, func, context); |
1564 | 3.85k | } |
1565 | 5.92k | } |
1566 | 2.96k | } |
1567 | 163k | } ZEND_HASH_FOREACH_END(); |
1568 | 48.8k | } ZEND_HASH_FOREACH_END(); |
1569 | 120k | } |
1570 | | |
1571 | 0 | static void step_optimize_op_array(zend_op_array *op_array, void *context) { |
1572 | 0 | zend_optimize_op_array(op_array, (zend_optimizer_ctx *) context); |
1573 | 0 | } |
1574 | | |
1575 | 0 | static void step_adjust_fcall_stack_size(zend_op_array *op_array, void *context) { |
1576 | 0 | zend_adjust_fcall_stack_size(op_array, (zend_optimizer_ctx *) context); |
1577 | 0 | } |
1578 | | |
1579 | 0 | static void step_dump_after_optimizer(zend_op_array *op_array, void *context) { |
1580 | 0 | zend_dump_op_array(op_array, ZEND_DUMP_LIVE_RANGES, "after optimizer", NULL); |
1581 | 0 | } |
1582 | | |
1583 | 60.4k | static void zend_optimizer_call_registered_passes(zend_script *script, void *ctx) { |
1584 | 60.4k | for (int i = 0; i < zend_optimizer_registered_passes.last; i++) { |
1585 | 0 | if (!zend_optimizer_registered_passes.pass[i]) { |
1586 | 0 | continue; |
1587 | 0 | } |
1588 | | |
1589 | 0 | zend_optimizer_registered_passes.pass[i](script, ctx); |
1590 | 0 | } |
1591 | 60.4k | } |
1592 | | |
1593 | | ZEND_API void zend_optimize_script(zend_script *script, zend_long optimization_level, zend_long debug_level) |
1594 | 60.4k | { |
1595 | 60.4k | zend_op_array *op_array; |
1596 | 60.4k | zend_string *name; |
1597 | 60.4k | zend_optimizer_ctx ctx; |
1598 | 60.4k | zval *zv; |
1599 | | |
1600 | 60.4k | ctx.arena = zend_arena_create(64 * 1024); |
1601 | 60.4k | ctx.script = script; |
1602 | 60.4k | ctx.constants = NULL; |
1603 | 60.4k | ctx.optimization_level = optimization_level; |
1604 | 60.4k | ctx.debug_level = debug_level; |
1605 | | |
1606 | 60.4k | if ((ZEND_OPTIMIZER_PASS_6 & optimization_level) && |
1607 | 60.4k | (ZEND_OPTIMIZER_PASS_7 & optimization_level)) { |
1608 | | /* Optimize using call-graph */ |
1609 | 60.4k | zend_call_graph call_graph; |
1610 | 60.4k | zend_build_call_graph(&ctx.arena, script, &call_graph); |
1611 | | |
1612 | 60.4k | int i; |
1613 | 60.4k | zend_func_info *func_info; |
1614 | | |
1615 | 163k | for (i = 0; i < call_graph.op_arrays_count; i++) { |
1616 | 102k | zend_revert_pass_two(call_graph.op_arrays[i]); |
1617 | 102k | zend_optimize(call_graph.op_arrays[i], &ctx); |
1618 | 102k | } |
1619 | | |
1620 | 60.4k | zend_analyze_call_graph(&ctx.arena, script, &call_graph); |
1621 | | |
1622 | 163k | for (i = 0; i < call_graph.op_arrays_count; i++) { |
1623 | 102k | func_info = ZEND_FUNC_INFO(call_graph.op_arrays[i]); |
1624 | 102k | if (func_info) { |
1625 | 102k | func_info->call_map = zend_build_call_map(&ctx.arena, func_info, call_graph.op_arrays[i]); |
1626 | 102k | if (call_graph.op_arrays[i]->fn_flags & ZEND_ACC_HAS_RETURN_TYPE) { |
1627 | 6.83k | zend_init_func_return_info(call_graph.op_arrays[i], script, &func_info->return_info); |
1628 | 6.83k | } |
1629 | 102k | } |
1630 | 102k | } |
1631 | | |
1632 | 163k | for (i = 0; i < call_graph.op_arrays_count; i++) { |
1633 | 102k | func_info = ZEND_FUNC_INFO(call_graph.op_arrays[i]); |
1634 | 102k | if (func_info) { |
1635 | 102k | if (zend_dfa_analyze_op_array(call_graph.op_arrays[i], &ctx, &func_info->ssa) == SUCCESS) { |
1636 | 74.1k | func_info->flags = func_info->ssa.cfg.flags; |
1637 | 74.1k | } else { |
1638 | 28.6k | ZEND_SET_FUNC_INFO(call_graph.op_arrays[i], NULL); |
1639 | 28.6k | } |
1640 | 102k | } |
1641 | 102k | } |
1642 | | |
1643 | | //TODO: perform inner-script inference??? |
1644 | 163k | for (i = 0; i < call_graph.op_arrays_count; i++) { |
1645 | 102k | func_info = ZEND_FUNC_INFO(call_graph.op_arrays[i]); |
1646 | 102k | if (func_info) { |
1647 | 74.1k | zend_dfa_optimize_op_array(call_graph.op_arrays[i], &ctx, &func_info->ssa, func_info->call_map); |
1648 | 74.1k | } |
1649 | 102k | } |
1650 | | |
1651 | 60.4k | if (debug_level & ZEND_DUMP_AFTER_PASS_7) { |
1652 | 0 | for (i = 0; i < call_graph.op_arrays_count; i++) { |
1653 | 0 | zend_dump_op_array(call_graph.op_arrays[i], 0, "after pass 7", NULL); |
1654 | 0 | } |
1655 | 0 | } |
1656 | | |
1657 | 60.4k | if (ZEND_OPTIMIZER_PASS_9 & optimization_level) { |
1658 | 163k | for (i = 0; i < call_graph.op_arrays_count; i++) { |
1659 | 102k | zend_optimize_temporary_variables(call_graph.op_arrays[i], &ctx); |
1660 | 102k | if (debug_level & ZEND_DUMP_AFTER_PASS_9) { |
1661 | 0 | zend_dump_op_array(call_graph.op_arrays[i], 0, "after pass 9", NULL); |
1662 | 0 | } |
1663 | 102k | } |
1664 | 60.4k | } |
1665 | | |
1666 | 60.4k | if (ZEND_OPTIMIZER_PASS_11 & optimization_level) { |
1667 | 163k | for (i = 0; i < call_graph.op_arrays_count; i++) { |
1668 | 102k | zend_optimizer_compact_literals(call_graph.op_arrays[i], &ctx); |
1669 | 102k | if (debug_level & ZEND_DUMP_AFTER_PASS_11) { |
1670 | 0 | zend_dump_op_array(call_graph.op_arrays[i], 0, "after pass 11", NULL); |
1671 | 0 | } |
1672 | 102k | } |
1673 | 60.4k | } |
1674 | | |
1675 | 60.4k | if (ZEND_OPTIMIZER_PASS_13 & optimization_level) { |
1676 | 163k | for (i = 0; i < call_graph.op_arrays_count; i++) { |
1677 | 102k | zend_optimizer_compact_vars(call_graph.op_arrays[i]); |
1678 | 102k | if (debug_level & ZEND_DUMP_AFTER_PASS_13) { |
1679 | 0 | zend_dump_op_array(call_graph.op_arrays[i], 0, "after pass 13", NULL); |
1680 | 0 | } |
1681 | 102k | } |
1682 | 60.4k | } |
1683 | | |
1684 | 60.4k | if (ZEND_OPTIMIZER_PASS_12 & optimization_level) { |
1685 | 163k | for (i = 0; i < call_graph.op_arrays_count; i++) { |
1686 | 102k | zend_adjust_fcall_stack_size_graph(call_graph.op_arrays[i]); |
1687 | 102k | } |
1688 | 60.4k | } |
1689 | | |
1690 | 163k | for (i = 0; i < call_graph.op_arrays_count; i++) { |
1691 | 102k | op_array = call_graph.op_arrays[i]; |
1692 | 102k | func_info = ZEND_FUNC_INFO(op_array); |
1693 | 102k | if (func_info && func_info->ssa.var_info) { |
1694 | 74.1k | zend_redo_pass_two_ex(op_array, &func_info->ssa); |
1695 | 74.1k | if (op_array->live_range) { |
1696 | 30.0k | zend_recalc_live_ranges(op_array, needs_live_range); |
1697 | 30.0k | } |
1698 | 74.1k | } else { |
1699 | 28.6k | zend_redo_pass_two(op_array); |
1700 | 28.6k | if (op_array->live_range) { |
1701 | 25.4k | zend_recalc_live_ranges(op_array, NULL); |
1702 | 25.4k | } |
1703 | 28.6k | } |
1704 | 102k | } |
1705 | | |
1706 | 163k | for (i = 0; i < call_graph.op_arrays_count; i++) { |
1707 | 102k | ZEND_SET_FUNC_INFO(call_graph.op_arrays[i], NULL); |
1708 | 102k | } |
1709 | 60.4k | } else { |
1710 | 0 | zend_foreach_op_array(script, step_optimize_op_array, &ctx); |
1711 | |
|
1712 | 0 | if (ZEND_OPTIMIZER_PASS_12 & optimization_level) { |
1713 | 0 | zend_foreach_op_array(script, step_adjust_fcall_stack_size, &ctx); |
1714 | 0 | } |
1715 | 0 | } |
1716 | | |
1717 | 169k | ZEND_HASH_MAP_FOREACH_VAL(&script->class_table, zv) { |
1718 | 169k | if (Z_TYPE_P(zv) == IS_ALIAS_PTR) { |
1719 | 0 | continue; |
1720 | 0 | } |
1721 | 24.4k | zend_class_entry *ce = Z_CE_P(zv); |
1722 | 103k | ZEND_HASH_MAP_FOREACH_STR_KEY_PTR(&ce->function_table, name, op_array) { |
1723 | 103k | if (op_array->scope != ce && op_array->type == ZEND_USER_FUNCTION) { |
1724 | 1.58k | zend_op_array *orig_op_array = |
1725 | 1.58k | zend_hash_find_ptr(&op_array->scope->function_table, name); |
1726 | | |
1727 | 1.58k | ZEND_ASSERT(orig_op_array != NULL); |
1728 | 1.58k | if (orig_op_array != op_array) { |
1729 | 0 | uint32_t fn_flags = op_array->fn_flags; |
1730 | 0 | zend_function *prototype = op_array->prototype; |
1731 | 0 | HashTable *ht = op_array->static_variables; |
1732 | |
|
1733 | 0 | *op_array = *orig_op_array; |
1734 | 0 | op_array->fn_flags = fn_flags; |
1735 | 0 | op_array->prototype = prototype; |
1736 | 0 | op_array->static_variables = ht; |
1737 | 0 | } |
1738 | 1.58k | } |
1739 | 103k | } ZEND_HASH_FOREACH_END(); |
1740 | 24.4k | } ZEND_HASH_FOREACH_END(); |
1741 | | |
1742 | 60.4k | zend_optimizer_call_registered_passes(script, &ctx); |
1743 | | |
1744 | 60.4k | if ((debug_level & ZEND_DUMP_AFTER_OPTIMIZER) && |
1745 | 0 | (ZEND_OPTIMIZER_PASS_7 & optimization_level)) { |
1746 | 0 | zend_foreach_op_array(script, step_dump_after_optimizer, NULL); |
1747 | 0 | } |
1748 | | |
1749 | 60.4k | if (ctx.constants) { |
1750 | 0 | zend_hash_destroy(ctx.constants); |
1751 | 0 | } |
1752 | 60.4k | zend_arena_destroy(ctx.arena); |
1753 | 60.4k | } |
1754 | | |
1755 | | ZEND_API int zend_optimizer_register_pass(zend_optimizer_pass_t pass) |
1756 | 0 | { |
1757 | 0 | if (!pass) { |
1758 | 0 | return -1; |
1759 | 0 | } |
1760 | | |
1761 | 0 | if (zend_optimizer_registered_passes.last == ZEND_OPTIMIZER_MAX_REGISTERED_PASSES) { |
1762 | 0 | return -1; |
1763 | 0 | } |
1764 | | |
1765 | 0 | zend_optimizer_registered_passes.pass[ |
1766 | 0 | zend_optimizer_registered_passes.last++] = pass; |
1767 | |
|
1768 | 0 | return zend_optimizer_registered_passes.last; |
1769 | 0 | } |
1770 | | |
1771 | | ZEND_API void zend_optimizer_unregister_pass(int idx) |
1772 | 0 | { |
1773 | 0 | zend_optimizer_registered_passes.pass[idx-1] = NULL; |
1774 | 0 | } |
1775 | | |
1776 | | zend_result zend_optimizer_startup(void) |
1777 | 16 | { |
1778 | 16 | return zend_func_info_startup(); |
1779 | 16 | } |
1780 | | |
1781 | | zend_result zend_optimizer_shutdown(void) |
1782 | 0 | { |
1783 | 0 | return zend_func_info_shutdown(); |
1784 | 0 | } |