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