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