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