1 /*
2 +----------------------------------------------------------------------+
3 | Zend OPcache |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 1998-2018 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 | http://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 "php.h"
23 #include "Optimizer/zend_optimizer.h"
24 #include "Optimizer/zend_optimizer_internal.h"
25 #include "zend_API.h"
26 #include "zend_constants.h"
27 #include "zend_execute.h"
28 #include "zend_vm.h"
29 #include "zend_cfg.h"
30 #include "zend_func_info.h"
31 #include "zend_call_graph.h"
32 #include "zend_inference.h"
33 #include "zend_dump.h"
34
35 #ifndef HAVE_DFA_PASS
36 # define HAVE_DFA_PASS 1
37 #endif
38
zend_optimizer_zval_dtor_wrapper(zval * zvalue)39 static void zend_optimizer_zval_dtor_wrapper(zval *zvalue)
40 {
41 zval_ptr_dtor_nogc(zvalue);
42 }
43
zend_optimizer_collect_constant(zend_optimizer_ctx * ctx,zval * name,zval * value)44 void zend_optimizer_collect_constant(zend_optimizer_ctx *ctx, zval *name, zval* value)
45 {
46 zval val;
47
48 if (!ctx->constants) {
49 ctx->constants = zend_arena_alloc(&ctx->arena, sizeof(HashTable));
50 zend_hash_init(ctx->constants, 16, NULL, zend_optimizer_zval_dtor_wrapper, 0);
51 }
52 ZVAL_COPY(&val, value);
53 zend_hash_add(ctx->constants, Z_STR_P(name), &val);
54 }
55
zend_compound_assign_to_binary_op(zend_uchar opcode)56 zend_uchar zend_compound_assign_to_binary_op(zend_uchar opcode)
57 {
58 switch (opcode) {
59 case ZEND_ASSIGN_ADD: return ZEND_ADD;
60 case ZEND_ASSIGN_SUB: return ZEND_SUB;
61 case ZEND_ASSIGN_MUL: return ZEND_MUL;
62 case ZEND_ASSIGN_DIV: return ZEND_DIV;
63 case ZEND_ASSIGN_MOD: return ZEND_MOD;
64 case ZEND_ASSIGN_SL: return ZEND_SL;
65 case ZEND_ASSIGN_SR: return ZEND_SR;
66 case ZEND_ASSIGN_CONCAT: return ZEND_CONCAT;
67 case ZEND_ASSIGN_BW_OR: return ZEND_BW_OR;
68 case ZEND_ASSIGN_BW_AND: return ZEND_BW_AND;
69 case ZEND_ASSIGN_BW_XOR: return ZEND_BW_XOR;
70 case ZEND_ASSIGN_POW: return ZEND_POW;
71 EMPTY_SWITCH_DEFAULT_CASE()
72 }
73 }
74
zend_optimizer_eval_binary_op(zval * result,zend_uchar opcode,zval * op1,zval * op2)75 int zend_optimizer_eval_binary_op(zval *result, zend_uchar opcode, zval *op1, zval *op2) /* {{{ */
76 {
77 binary_op_type binary_op = get_binary_op(opcode);
78 int er, ret;
79
80 if (zend_binary_op_produces_numeric_string_error(opcode, op1, op2)) {
81 /* produces numeric string E_NOTICE/E_WARNING */
82 return FAILURE;
83 }
84
85 switch (opcode) {
86 case ZEND_ADD:
87 if ((Z_TYPE_P(op1) == IS_ARRAY
88 || Z_TYPE_P(op2) == IS_ARRAY)
89 && Z_TYPE_P(op1) != Z_TYPE_P(op2)) {
90 /* produces "Unsupported operand types" exception */
91 return FAILURE;
92 }
93 break;
94 case ZEND_DIV:
95 case ZEND_MOD:
96 if (zval_get_long(op2) == 0) {
97 /* division by 0 */
98 return FAILURE;
99 }
100 /* break missing intentionally */
101 case ZEND_SUB:
102 case ZEND_MUL:
103 case ZEND_POW:
104 case ZEND_CONCAT:
105 case ZEND_FAST_CONCAT:
106 if (Z_TYPE_P(op1) == IS_ARRAY
107 || Z_TYPE_P(op2) == IS_ARRAY) {
108 /* produces "Unsupported operand types" exception */
109 return FAILURE;
110 }
111 break;
112 case ZEND_SL:
113 case ZEND_SR:
114 if (zval_get_long(op2) < 0) {
115 /* shift by negative number */
116 return FAILURE;
117 }
118 break;
119 }
120
121 er = EG(error_reporting);
122 EG(error_reporting) = 0;
123 ret = binary_op(result, op1, op2);
124 EG(error_reporting) = er;
125
126 return ret;
127 }
128 /* }}} */
129
zend_optimizer_eval_unary_op(zval * result,zend_uchar opcode,zval * op1)130 int zend_optimizer_eval_unary_op(zval *result, zend_uchar opcode, zval *op1) /* {{{ */
131 {
132 unary_op_type unary_op = get_unary_op(opcode);
133
134 if (unary_op) {
135 if (opcode == ZEND_BW_NOT
136 && Z_TYPE_P(op1) != IS_LONG
137 && Z_TYPE_P(op1) != IS_DOUBLE
138 && Z_TYPE_P(op1) != IS_STRING) {
139 /* produces "Unsupported operand types" exception */
140 return FAILURE;
141 }
142 return unary_op(result, op1);
143 } else { /* ZEND_BOOL */
144 ZVAL_BOOL(result, zend_is_true(op1));
145 return SUCCESS;
146 }
147 }
148 /* }}} */
149
zend_optimizer_eval_cast(zval * result,uint32_t type,zval * op1)150 int zend_optimizer_eval_cast(zval *result, uint32_t type, zval *op1) /* {{{ */
151 {
152 switch (type) {
153 case IS_NULL:
154 ZVAL_NULL(result);
155 return SUCCESS;
156 case _IS_BOOL:
157 ZVAL_BOOL(result, zval_is_true(op1));
158 return SUCCESS;
159 case IS_LONG:
160 ZVAL_LONG(result, zval_get_long(op1));
161 return SUCCESS;
162 case IS_DOUBLE:
163 ZVAL_DOUBLE(result, zval_get_double(op1));
164 return SUCCESS;
165 case IS_STRING:
166 /* Conversion from double to string takes into account run-time
167 'precision' setting and cannot be evaluated at compile-time */
168 if (Z_TYPE_P(op1) != IS_ARRAY && Z_TYPE_P(op1) != IS_DOUBLE) {
169 ZVAL_STR(result, zval_get_string(op1));
170 return SUCCESS;
171 }
172 break;
173 case IS_ARRAY:
174 ZVAL_COPY(result, op1);
175 convert_to_array(result);
176 return SUCCESS;
177 }
178 return FAILURE;
179 }
180 /* }}} */
181
zend_optimizer_eval_strlen(zval * result,zval * op1)182 int zend_optimizer_eval_strlen(zval *result, zval *op1) /* {{{ */
183 {
184 if (Z_TYPE_P(op1) != IS_STRING) {
185 return FAILURE;
186 }
187 ZVAL_LONG(result, Z_STRLEN_P(op1));
188 return SUCCESS;
189 }
190 /* }}} */
191
zend_optimizer_get_collected_constant(HashTable * constants,zval * name,zval * value)192 int zend_optimizer_get_collected_constant(HashTable *constants, zval *name, zval* value)
193 {
194 zval *val;
195
196 if ((val = zend_hash_find(constants, Z_STR_P(name))) != NULL) {
197 ZVAL_COPY(value, val);
198 return 1;
199 }
200 return 0;
201 }
202
zend_optimizer_add_literal(zend_op_array * op_array,zval * zv)203 int zend_optimizer_add_literal(zend_op_array *op_array, zval *zv)
204 {
205 int i = op_array->last_literal;
206 op_array->last_literal++;
207 op_array->literals = (zval*)erealloc(op_array->literals, op_array->last_literal * sizeof(zval));
208 ZVAL_COPY_VALUE(&op_array->literals[i], zv);
209 Z_EXTRA(op_array->literals[i]) = 0;
210 return i;
211 }
212
zend_optimizer_add_literal_string(zend_op_array * op_array,zend_string * str)213 static inline int zend_optimizer_add_literal_string(zend_op_array *op_array, zend_string *str) {
214 zval zv;
215 ZVAL_STR(&zv, str);
216 zend_string_hash_val(str);
217 return zend_optimizer_add_literal(op_array, &zv);
218 }
219
zend_optimizer_is_disabled_func(const char * name,size_t len)220 int zend_optimizer_is_disabled_func(const char *name, size_t len) {
221 zend_function *fbc = (zend_function *)zend_hash_str_find_ptr(EG(function_table), name, len);
222
223 return (fbc && fbc->type == ZEND_INTERNAL_FUNCTION &&
224 fbc->internal_function.handler == ZEND_FN(display_disabled_function));
225 }
226
drop_leading_backslash(zval * val)227 static inline void drop_leading_backslash(zval *val) {
228 if (Z_STRVAL_P(val)[0] == '\\') {
229 zend_string *str = zend_string_init(Z_STRVAL_P(val) + 1, Z_STRLEN_P(val) - 1, 0);
230 zval_ptr_dtor_nogc(val);
231 ZVAL_STR(val, str);
232 }
233 }
234
alloc_cache_slots(zend_op_array * op_array,uint32_t num)235 static inline uint32_t alloc_cache_slots(zend_op_array *op_array, uint32_t num) {
236 uint32_t ret = op_array->cache_size;
237 op_array->cache_size += num * sizeof(void *);
238 return ret;
239 }
240
241 #define REQUIRES_STRING(val) do { \
242 if (Z_TYPE_P(val) != IS_STRING) { \
243 return 0; \
244 } \
245 } while (0)
246
247 #define TO_STRING_NOWARN(val) do { \
248 if (Z_TYPE_P(val) >= IS_ARRAY) { \
249 return 0; \
250 } \
251 convert_to_string(val); \
252 } while (0)
253
zend_optimizer_update_op1_const(zend_op_array * op_array,zend_op * opline,zval * val)254 int zend_optimizer_update_op1_const(zend_op_array *op_array,
255 zend_op *opline,
256 zval *val)
257 {
258 switch (opline->opcode) {
259 case ZEND_FREE:
260 case ZEND_CHECK_VAR:
261 MAKE_NOP(opline);
262 zval_ptr_dtor_nogc(val);
263 return 1;
264 case ZEND_SEND_VAR_EX:
265 case ZEND_SEND_FUNC_ARG:
266 case ZEND_FETCH_DIM_W:
267 case ZEND_FETCH_DIM_RW:
268 case ZEND_FETCH_DIM_FUNC_ARG:
269 case ZEND_FETCH_DIM_UNSET:
270 case ZEND_FETCH_LIST_W:
271 case ZEND_ASSIGN_DIM:
272 case ZEND_RETURN_BY_REF:
273 case ZEND_INSTANCEOF:
274 case ZEND_MAKE_REF:
275 return 0;
276 case ZEND_CATCH:
277 REQUIRES_STRING(val);
278 drop_leading_backslash(val);
279 opline->op1.constant = zend_optimizer_add_literal(op_array, val);
280 opline->extended_value = alloc_cache_slots(op_array, 1) | (opline->extended_value & ZEND_LAST_CATCH);
281 zend_optimizer_add_literal_string(op_array, zend_string_tolower(Z_STR_P(val)));
282 break;
283 case ZEND_DEFINED:
284 REQUIRES_STRING(val);
285 drop_leading_backslash(val);
286 opline->op1.constant = zend_optimizer_add_literal(op_array, val);
287 opline->extended_value = alloc_cache_slots(op_array, 1);
288 zend_optimizer_add_literal_string(op_array, zend_string_tolower(Z_STR_P(val)));
289 break;
290 case ZEND_NEW:
291 REQUIRES_STRING(val);
292 drop_leading_backslash(val);
293 opline->op1.constant = zend_optimizer_add_literal(op_array, val);
294 opline->op2.num = alloc_cache_slots(op_array, 1);
295 zend_optimizer_add_literal_string(op_array, zend_string_tolower(Z_STR_P(val)));
296 break;
297 case ZEND_INIT_STATIC_METHOD_CALL:
298 REQUIRES_STRING(val);
299 drop_leading_backslash(val);
300 opline->op1.constant = zend_optimizer_add_literal(op_array, val);
301 if (opline->op2_type != IS_CONST) {
302 opline->result.num = alloc_cache_slots(op_array, 1);
303 }
304 zend_optimizer_add_literal_string(op_array, zend_string_tolower(Z_STR_P(val)));
305 break;
306 case ZEND_FETCH_CLASS_CONSTANT:
307 REQUIRES_STRING(val);
308 drop_leading_backslash(val);
309 opline->op1.constant = zend_optimizer_add_literal(op_array, val);
310 if (opline->op2_type != IS_CONST) {
311 opline->extended_value = alloc_cache_slots(op_array, 1);
312 }
313 zend_optimizer_add_literal_string(op_array, zend_string_tolower(Z_STR_P(val)));
314 break;
315 case ZEND_FETCH_STATIC_PROP_R:
316 case ZEND_FETCH_STATIC_PROP_W:
317 case ZEND_FETCH_STATIC_PROP_RW:
318 case ZEND_FETCH_STATIC_PROP_IS:
319 case ZEND_FETCH_STATIC_PROP_UNSET:
320 case ZEND_FETCH_STATIC_PROP_FUNC_ARG:
321 case ZEND_UNSET_STATIC_PROP:
322 TO_STRING_NOWARN(val);
323 opline->op1.constant = zend_optimizer_add_literal(op_array, val);
324 if (opline->op2_type == IS_CONST && opline->extended_value + sizeof(void*) == op_array->cache_size) {
325 op_array->cache_size += sizeof(void *);
326 } else {
327 opline->extended_value = alloc_cache_slots(op_array, 2);
328 }
329 break;
330 case ZEND_ISSET_ISEMPTY_STATIC_PROP:
331 TO_STRING_NOWARN(val);
332 opline->op1.constant = zend_optimizer_add_literal(op_array, val);
333 if (opline->op2_type == IS_CONST && (opline->extended_value & ~ZEND_ISEMPTY) + sizeof(void*) == op_array->cache_size) {
334 op_array->cache_size += sizeof(void *);
335 } else {
336 opline->extended_value = alloc_cache_slots(op_array, 2) | (opline->extended_value & ZEND_ISEMPTY);
337 }
338 break;
339 case ZEND_SEND_VAR:
340 opline->opcode = ZEND_SEND_VAL;
341 opline->op1.constant = zend_optimizer_add_literal(op_array, val);
342 break;
343 case ZEND_SEPARATE:
344 case ZEND_SEND_VAR_NO_REF:
345 case ZEND_SEND_VAR_NO_REF_EX:
346 return 0;
347 case ZEND_VERIFY_RETURN_TYPE:
348 /* This would require a non-local change.
349 * zend_optimizer_replace_by_const() supports this. */
350 return 0;
351 case ZEND_CASE:
352 case ZEND_FETCH_LIST_R:
353 return 0;
354 case ZEND_CONCAT:
355 case ZEND_FAST_CONCAT:
356 case ZEND_FETCH_R:
357 case ZEND_FETCH_W:
358 case ZEND_FETCH_RW:
359 case ZEND_FETCH_IS:
360 case ZEND_FETCH_UNSET:
361 case ZEND_FETCH_FUNC_ARG:
362 TO_STRING_NOWARN(val);
363 if (opline->opcode == ZEND_CONCAT && opline->op2_type == IS_CONST) {
364 opline->opcode = ZEND_FAST_CONCAT;
365 }
366 /* break missing intentionally */
367 default:
368 opline->op1.constant = zend_optimizer_add_literal(op_array, val);
369 break;
370 }
371
372 opline->op1_type = IS_CONST;
373 if (Z_TYPE(ZEND_OP1_LITERAL(opline)) == IS_STRING) {
374 zend_string_hash_val(Z_STR(ZEND_OP1_LITERAL(opline)));
375 }
376 return 1;
377 }
378
zend_optimizer_update_op2_const(zend_op_array * op_array,zend_op * opline,zval * val)379 int zend_optimizer_update_op2_const(zend_op_array *op_array,
380 zend_op *opline,
381 zval *val)
382 {
383 zval tmp;
384
385 switch (opline->opcode) {
386 case ZEND_ASSIGN_REF:
387 case ZEND_FAST_CALL:
388 return 0;
389 case ZEND_FETCH_CLASS:
390 if ((opline + 1)->opcode == ZEND_INSTANCEOF &&
391 (opline + 1)->op2.var == opline->result.var) {
392 return 0;
393 }
394 /* break missing intentionally */
395 case ZEND_INSTANCEOF:
396 REQUIRES_STRING(val);
397 drop_leading_backslash(val);
398 opline->op2.constant = zend_optimizer_add_literal(op_array, val);
399 zend_optimizer_add_literal_string(op_array, zend_string_tolower(Z_STR_P(val)));
400 opline->extended_value = alloc_cache_slots(op_array, 1);
401 break;
402 case ZEND_ADD_INTERFACE:
403 case ZEND_ADD_TRAIT:
404 REQUIRES_STRING(val);
405 drop_leading_backslash(val);
406 opline->op2.constant = zend_optimizer_add_literal(op_array, val);
407 zend_optimizer_add_literal_string(op_array, zend_string_tolower(Z_STR_P(val)));
408 break;
409 case ZEND_INIT_FCALL_BY_NAME:
410 REQUIRES_STRING(val);
411 drop_leading_backslash(val);
412 opline->op2.constant = zend_optimizer_add_literal(op_array, val);
413 zend_optimizer_add_literal_string(op_array, zend_string_tolower(Z_STR_P(val)));
414 opline->result.num = alloc_cache_slots(op_array, 1);
415 break;
416 case ZEND_FETCH_STATIC_PROP_R:
417 case ZEND_FETCH_STATIC_PROP_W:
418 case ZEND_FETCH_STATIC_PROP_RW:
419 case ZEND_FETCH_STATIC_PROP_IS:
420 case ZEND_FETCH_STATIC_PROP_UNSET:
421 case ZEND_FETCH_STATIC_PROP_FUNC_ARG:
422 case ZEND_UNSET_STATIC_PROP:
423 REQUIRES_STRING(val);
424 drop_leading_backslash(val);
425 opline->op2.constant = zend_optimizer_add_literal(op_array, val);
426 zend_optimizer_add_literal_string(op_array, zend_string_tolower(Z_STR_P(val)));
427 if (opline->op1_type != IS_CONST) {
428 opline->extended_value = alloc_cache_slots(op_array, 1);
429 }
430 break;
431 case ZEND_ISSET_ISEMPTY_STATIC_PROP:
432 REQUIRES_STRING(val);
433 drop_leading_backslash(val);
434 opline->op2.constant = zend_optimizer_add_literal(op_array, val);
435 zend_optimizer_add_literal_string(op_array, zend_string_tolower(Z_STR_P(val)));
436 if (opline->op1_type != IS_CONST) {
437 opline->extended_value = alloc_cache_slots(op_array, 1) | (opline->extended_value & ZEND_ISEMPTY);
438 }
439 break;
440 case ZEND_INIT_FCALL:
441 REQUIRES_STRING(val);
442 if (Z_REFCOUNT_P(val) == 1) {
443 zend_str_tolower(Z_STRVAL_P(val), Z_STRLEN_P(val));
444 } else {
445 ZVAL_STR(&tmp, zend_string_tolower(Z_STR_P(val)));
446 zval_ptr_dtor_nogc(val);
447 val = &tmp;
448 }
449 opline->op2.constant = zend_optimizer_add_literal(op_array, val);
450 opline->result.num = alloc_cache_slots(op_array, 1);
451 break;
452 case ZEND_INIT_DYNAMIC_CALL:
453 if (Z_TYPE_P(val) == IS_STRING) {
454 if (zend_memrchr(Z_STRVAL_P(val), ':', Z_STRLEN_P(val))) {
455 return 0;
456 }
457
458 if (zend_optimizer_classify_function(Z_STR_P(val), opline->extended_value)) {
459 /* Dynamic call to various special functions must stay dynamic,
460 * otherwise would drop a warning */
461 return 0;
462 }
463
464 opline->opcode = ZEND_INIT_FCALL_BY_NAME;
465 drop_leading_backslash(val);
466 opline->op2.constant = zend_optimizer_add_literal(op_array, val);
467 zend_optimizer_add_literal_string(op_array, zend_string_tolower(Z_STR_P(val)));
468 opline->result.num = alloc_cache_slots(op_array, 1);
469 } else {
470 opline->op2.constant = zend_optimizer_add_literal(op_array, val);
471 }
472 break;
473 case ZEND_INIT_METHOD_CALL:
474 REQUIRES_STRING(val);
475 opline->op2.constant = zend_optimizer_add_literal(op_array, val);
476 zend_optimizer_add_literal_string(op_array, zend_string_tolower(Z_STR_P(val)));
477 opline->result.num = alloc_cache_slots(op_array, 2);
478 break;
479 case ZEND_INIT_STATIC_METHOD_CALL:
480 REQUIRES_STRING(val);
481 opline->op2.constant = zend_optimizer_add_literal(op_array, val);
482 zend_optimizer_add_literal_string(op_array, zend_string_tolower(Z_STR_P(val)));
483 if (opline->op1_type != IS_CONST) {
484 opline->result.num = alloc_cache_slots(op_array, 2);
485 }
486 break;
487 case ZEND_ASSIGN_OBJ:
488 case ZEND_FETCH_OBJ_R:
489 case ZEND_FETCH_OBJ_W:
490 case ZEND_FETCH_OBJ_RW:
491 case ZEND_FETCH_OBJ_IS:
492 case ZEND_FETCH_OBJ_UNSET:
493 case ZEND_FETCH_OBJ_FUNC_ARG:
494 case ZEND_UNSET_OBJ:
495 case ZEND_PRE_INC_OBJ:
496 case ZEND_PRE_DEC_OBJ:
497 case ZEND_POST_INC_OBJ:
498 case ZEND_POST_DEC_OBJ:
499 TO_STRING_NOWARN(val);
500 opline->op2.constant = zend_optimizer_add_literal(op_array, val);
501 opline->extended_value = alloc_cache_slots(op_array, 2);
502 break;
503 case ZEND_ISSET_ISEMPTY_PROP_OBJ:
504 TO_STRING_NOWARN(val);
505 opline->op2.constant = zend_optimizer_add_literal(op_array, val);
506 opline->extended_value = alloc_cache_slots(op_array, 2) | (opline->extended_value & ZEND_ISEMPTY);
507 break;
508 case ZEND_ASSIGN_ADD:
509 case ZEND_ASSIGN_SUB:
510 case ZEND_ASSIGN_MUL:
511 case ZEND_ASSIGN_DIV:
512 case ZEND_ASSIGN_POW:
513 case ZEND_ASSIGN_MOD:
514 case ZEND_ASSIGN_SL:
515 case ZEND_ASSIGN_SR:
516 case ZEND_ASSIGN_CONCAT:
517 case ZEND_ASSIGN_BW_OR:
518 case ZEND_ASSIGN_BW_AND:
519 case ZEND_ASSIGN_BW_XOR:
520 if (opline->extended_value == ZEND_ASSIGN_OBJ) {
521 TO_STRING_NOWARN(val);
522 opline->op2.constant = zend_optimizer_add_literal(op_array, val);
523 (opline+1)->extended_value = alloc_cache_slots(op_array, 2);
524 } else if (opline->extended_value == ZEND_ASSIGN_DIM) {
525 if (Z_TYPE_P(val) == IS_STRING) {
526 zend_ulong index;
527
528 if (ZEND_HANDLE_NUMERIC(Z_STR_P(val), index)) {
529 ZVAL_LONG(&tmp, index);
530 opline->op2.constant = zend_optimizer_add_literal(op_array, &tmp);
531 zend_string_hash_val(Z_STR_P(val));
532 zend_optimizer_add_literal(op_array, val);
533 Z_EXTRA(op_array->literals[opline->op2.constant]) = ZEND_EXTRA_VALUE;
534 break;
535 }
536 }
537 opline->op2.constant = zend_optimizer_add_literal(op_array, val);
538 } else {
539 opline->op2.constant = zend_optimizer_add_literal(op_array, val);
540 }
541 break;
542 case ZEND_ISSET_ISEMPTY_DIM_OBJ:
543 case ZEND_ASSIGN_DIM:
544 case ZEND_UNSET_DIM:
545 case ZEND_FETCH_DIM_R:
546 case ZEND_FETCH_DIM_W:
547 case ZEND_FETCH_DIM_RW:
548 case ZEND_FETCH_DIM_IS:
549 case ZEND_FETCH_DIM_FUNC_ARG:
550 case ZEND_FETCH_DIM_UNSET:
551 case ZEND_FETCH_LIST_R:
552 case ZEND_FETCH_LIST_W:
553 if (Z_TYPE_P(val) == IS_STRING) {
554 zend_ulong index;
555
556 if (ZEND_HANDLE_NUMERIC(Z_STR_P(val), index)) {
557 ZVAL_LONG(&tmp, index);
558 opline->op2.constant = zend_optimizer_add_literal(op_array, &tmp);
559 zend_string_hash_val(Z_STR_P(val));
560 zend_optimizer_add_literal(op_array, val);
561 Z_EXTRA(op_array->literals[opline->op2.constant]) = ZEND_EXTRA_VALUE;
562 break;
563 }
564 }
565 opline->op2.constant = zend_optimizer_add_literal(op_array, val);
566 break;
567 case ZEND_ADD_ARRAY_ELEMENT:
568 case ZEND_INIT_ARRAY:
569 if (Z_TYPE_P(val) == IS_STRING) {
570 zend_ulong index;
571 if (ZEND_HANDLE_NUMERIC(Z_STR_P(val), index)) {
572 zval_ptr_dtor_nogc(val);
573 ZVAL_LONG(val, index);
574 }
575 }
576 opline->op2.constant = zend_optimizer_add_literal(op_array, val);
577 break;
578 case ZEND_ROPE_INIT:
579 case ZEND_ROPE_ADD:
580 case ZEND_ROPE_END:
581 case ZEND_CONCAT:
582 case ZEND_FAST_CONCAT:
583 TO_STRING_NOWARN(val);
584 if (opline->opcode == ZEND_CONCAT && opline->op1_type == IS_CONST) {
585 opline->opcode = ZEND_FAST_CONCAT;
586 }
587 /* break missing intentionally */
588 default:
589 opline->op2.constant = zend_optimizer_add_literal(op_array, val);
590 break;
591 }
592
593 opline->op2_type = IS_CONST;
594 if (Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_STRING) {
595 zend_string_hash_val(Z_STR(ZEND_OP2_LITERAL(opline)));
596 }
597 return 1;
598 }
599
zend_optimizer_remove_live_range(zend_op_array * op_array,uint32_t var)600 void zend_optimizer_remove_live_range(zend_op_array *op_array, uint32_t var)
601 {
602 if (op_array->last_live_range) {
603 int i = 0;
604 int j = 0;
605
606 do {
607 if ((op_array->live_range[i].var & ~ZEND_LIVE_MASK) != var) {
608 if (i != j) {
609 op_array->live_range[j] = op_array->live_range[i];
610 }
611 j++;
612 }
613 i++;
614 } while (i < op_array->last_live_range);
615 if (i != j) {
616 op_array->last_live_range = j;
617 if (j == 0) {
618 efree(op_array->live_range);
619 op_array->live_range = NULL;
620 }
621 }
622 }
623 }
624
zend_determine_constructor_call(zend_op_array * op_array,uint32_t start)625 static uint32_t zend_determine_constructor_call(zend_op_array *op_array, uint32_t start) {
626 int call = 0;
627 while (++start < op_array->last) {
628 switch (op_array->opcodes[start].opcode) {
629 case ZEND_INIT_FCALL_BY_NAME:
630 case ZEND_INIT_NS_FCALL_BY_NAME:
631 case ZEND_INIT_STATIC_METHOD_CALL:
632 case ZEND_INIT_METHOD_CALL:
633 case ZEND_INIT_FCALL:
634 case ZEND_NEW:
635 case ZEND_INIT_DYNAMIC_CALL:
636 case ZEND_INIT_USER_CALL:
637 call++;
638 break;
639 case ZEND_DO_FCALL:
640 if (call == 0) {
641 return start;
642 }
643 /* break missing intentionally */
644 case ZEND_DO_ICALL:
645 case ZEND_DO_UCALL:
646 case ZEND_DO_FCALL_BY_NAME:
647 call--;
648 break;
649 default:
650 break;
651 }
652 }
653
654 ZEND_ASSERT(0);
655 return -1;
656 }
657
zend_optimizer_remove_live_range_ex(zend_op_array * op_array,uint32_t var,uint32_t start)658 void zend_optimizer_remove_live_range_ex(zend_op_array *op_array, uint32_t var, uint32_t start)
659 {
660 uint32_t i = 0;
661
662 switch (op_array->opcodes[start].opcode) {
663 case ZEND_ROPE_ADD:
664 case ZEND_ADD_ARRAY_ELEMENT:
665 return;
666 case ZEND_ROPE_INIT:
667 var |= ZEND_LIVE_ROPE;
668 break;
669 case ZEND_BEGIN_SILENCE:
670 var |= ZEND_LIVE_SILENCE;
671 break;
672 case ZEND_FE_RESET_R:
673 case ZEND_FE_RESET_RW:
674 var |= ZEND_LIVE_LOOP;
675 start++;
676 break;
677 case ZEND_NEW:
678 start = zend_determine_constructor_call(op_array, start);
679 start++;
680 break;
681 default:
682 start++;
683 }
684
685 while (i < op_array->last_live_range) {
686 if (op_array->live_range[i].var == var
687 && op_array->live_range[i].start == start) {
688 op_array->last_live_range--;
689 if (i < op_array->last_live_range) {
690 memmove(&op_array->live_range[i], &op_array->live_range[i+1], (op_array->last_live_range - i) * sizeof(zend_live_range));
691 }
692 break;
693 }
694 i++;
695 }
696 }
697
zend_optimizer_replace_by_const(zend_op_array * op_array,zend_op * opline,zend_uchar type,uint32_t var,zval * val)698 int zend_optimizer_replace_by_const(zend_op_array *op_array,
699 zend_op *opline,
700 zend_uchar type,
701 uint32_t var,
702 zval *val)
703 {
704 zend_op *end = op_array->opcodes + op_array->last;
705
706 while (opline < end) {
707 if (opline->op1_type == type &&
708 opline->op1.var == var) {
709 switch (opline->opcode) {
710 case ZEND_FETCH_DIM_W:
711 case ZEND_FETCH_DIM_RW:
712 case ZEND_FETCH_DIM_FUNC_ARG:
713 case ZEND_FETCH_DIM_UNSET:
714 case ZEND_FETCH_LIST_W:
715 case ZEND_ASSIGN_DIM:
716 case ZEND_SEPARATE:
717 case ZEND_RETURN_BY_REF:
718 return 0;
719 case ZEND_SEND_VAR:
720 opline->extended_value = 0;
721 opline->opcode = ZEND_SEND_VAL;
722 break;
723 case ZEND_SEND_VAR_EX:
724 case ZEND_SEND_FUNC_ARG:
725 opline->extended_value = 0;
726 opline->opcode = ZEND_SEND_VAL_EX;
727 break;
728 case ZEND_SEND_VAR_NO_REF:
729 return 0;
730 case ZEND_SEND_VAR_NO_REF_EX:
731 opline->opcode = ZEND_SEND_VAL;
732 break;
733 case ZEND_SEND_USER:
734 opline->opcode = ZEND_SEND_VAL_EX;
735 break;
736 /* In most cases IS_TMP_VAR operand may be used only once.
737 * The operands are usually destroyed by the opcode handler.
738 * ZEND_CASE and ZEND_FETCH_LIST_R are exceptions, they keeps operand
739 * unchanged, and allows its reuse. these instructions
740 * usually terminated by ZEND_FREE that finally kills the value.
741 */
742 case ZEND_FETCH_LIST_R: {
743 zend_op *m = opline;
744
745 do {
746 if (m->opcode == ZEND_FETCH_LIST_R &&
747 m->op1_type == type &&
748 m->op1.var == var) {
749 zval v;
750 ZVAL_COPY(&v, val);
751 if (Z_TYPE(v) == IS_STRING) {
752 zend_string_hash_val(Z_STR(v));
753 }
754 m->op1.constant = zend_optimizer_add_literal(op_array, &v);
755 m->op1_type = IS_CONST;
756 }
757 m++;
758 } while (m->opcode != ZEND_FREE || m->op1_type != type || m->op1.var != var);
759
760 ZEND_ASSERT(m->opcode == ZEND_FREE && m->op1_type == type && m->op1.var == var);
761 MAKE_NOP(m);
762 zval_ptr_dtor_nogc(val);
763 zend_optimizer_remove_live_range(op_array, var);
764 return 1;
765 }
766 case ZEND_SWITCH_LONG:
767 case ZEND_SWITCH_STRING:
768 case ZEND_CASE:
769 case ZEND_FREE: {
770 zend_op *m, *n;
771 int brk = op_array->last_live_range;
772 zend_bool in_switch = 0;
773 while (brk--) {
774 if (op_array->live_range[brk].start <= (uint32_t)(opline - op_array->opcodes) &&
775 op_array->live_range[brk].end > (uint32_t)(opline - op_array->opcodes)) {
776 in_switch = 1;
777 break;
778 }
779 }
780
781 if (!in_switch) {
782 ZEND_ASSERT(opline->opcode == ZEND_FREE);
783 MAKE_NOP(opline);
784 zval_ptr_dtor_nogc(val);
785 return 1;
786 }
787
788 m = opline;
789 n = op_array->opcodes + op_array->live_range[brk].end;
790 if (n->opcode == ZEND_FREE &&
791 !(n->extended_value & ZEND_FREE_ON_RETURN)) {
792 n++;
793 } else {
794 n = op_array->opcodes + op_array->last;
795 }
796
797 while (m < n) {
798 if (m->op1_type == type &&
799 m->op1.var == var) {
800 if (m->opcode == ZEND_CASE
801 || m->opcode == ZEND_SWITCH_LONG
802 || m->opcode == ZEND_SWITCH_STRING) {
803 zval v;
804
805 if (m->opcode == ZEND_CASE) {
806 m->opcode = ZEND_IS_EQUAL;
807 }
808 ZVAL_COPY(&v, val);
809 if (Z_TYPE(v) == IS_STRING) {
810 zend_string_hash_val(Z_STR(v));
811 }
812 m->op1.constant = zend_optimizer_add_literal(op_array, &v);
813 m->op1_type = IS_CONST;
814 } else if (m->opcode == ZEND_FREE) {
815 MAKE_NOP(m);
816 } else {
817 ZEND_ASSERT(0);
818 }
819 }
820 m++;
821 }
822 zval_ptr_dtor_nogc(val);
823 zend_optimizer_remove_live_range(op_array, var);
824 return 1;
825 }
826 case ZEND_VERIFY_RETURN_TYPE: {
827 zend_arg_info *ret_info = op_array->arg_info - 1;
828 if (ZEND_TYPE_IS_CLASS(ret_info->type)
829 || ZEND_TYPE_CODE(ret_info->type) == IS_CALLABLE
830 || !ZEND_SAME_FAKE_TYPE(ZEND_TYPE_CODE(ret_info->type), Z_TYPE_P(val))
831 || (op_array->fn_flags & ZEND_ACC_RETURN_REFERENCE)) {
832 return 0;
833 }
834 MAKE_NOP(opline);
835
836 /* zend_handle_loops_and_finally may inserts other oplines */
837 do {
838 ++opline;
839 } while (opline->opcode != ZEND_RETURN && opline->opcode != ZEND_RETURN_BY_REF);
840 ZEND_ASSERT(opline->op1.var == var);
841
842 break;
843 }
844 default:
845 break;
846 }
847 if (zend_optimizer_update_op1_const(op_array, opline, val)) {
848 zend_optimizer_remove_live_range(op_array, var);
849 return 1;
850 }
851 return 0;
852 }
853
854 if (opline->op2_type == type &&
855 opline->op2.var == var) {
856 if (zend_optimizer_update_op2_const(op_array, opline, val)) {
857 zend_optimizer_remove_live_range(op_array, var);
858 return 1;
859 }
860 return 0;
861 }
862 opline++;
863 }
864
865 return 1;
866 }
867
868 /* Update jump offsets after a jump was migrated to another opline */
zend_optimizer_migrate_jump(zend_op_array * op_array,zend_op * new_opline,zend_op * opline)869 void zend_optimizer_migrate_jump(zend_op_array *op_array, zend_op *new_opline, zend_op *opline) {
870 switch (new_opline->opcode) {
871 case ZEND_JMP:
872 case ZEND_FAST_CALL:
873 ZEND_SET_OP_JMP_ADDR(new_opline, new_opline->op1, ZEND_OP1_JMP_ADDR(opline));
874 break;
875 case ZEND_JMPZNZ:
876 new_opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, new_opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value));
877 /* break missing intentionally */
878 case ZEND_JMPZ:
879 case ZEND_JMPNZ:
880 case ZEND_JMPZ_EX:
881 case ZEND_JMPNZ_EX:
882 case ZEND_FE_RESET_R:
883 case ZEND_FE_RESET_RW:
884 case ZEND_JMP_SET:
885 case ZEND_COALESCE:
886 case ZEND_ASSERT_CHECK:
887 ZEND_SET_OP_JMP_ADDR(new_opline, new_opline->op2, ZEND_OP2_JMP_ADDR(opline));
888 break;
889 case ZEND_DECLARE_ANON_CLASS:
890 case ZEND_DECLARE_ANON_INHERITED_CLASS:
891 case ZEND_FE_FETCH_R:
892 case ZEND_FE_FETCH_RW:
893 new_opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, new_opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value));
894 break;
895 case ZEND_CATCH:
896 if (!(opline->extended_value & ZEND_LAST_CATCH)) {
897 ZEND_SET_OP_JMP_ADDR(new_opline, new_opline->op2, ZEND_OP2_JMP_ADDR(opline));
898 }
899 break;
900 case ZEND_SWITCH_LONG:
901 case ZEND_SWITCH_STRING:
902 {
903 HashTable *jumptable = Z_ARRVAL(ZEND_OP2_LITERAL(opline));
904 zval *zv;
905 ZEND_HASH_FOREACH_VAL(jumptable, zv) {
906 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)));
907 } ZEND_HASH_FOREACH_END();
908 new_opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, new_opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value));
909 break;
910 }
911 }
912 }
913
914 /* Shift jump offsets based on shiftlist */
zend_optimizer_shift_jump(zend_op_array * op_array,zend_op * opline,uint32_t * shiftlist)915 void zend_optimizer_shift_jump(zend_op_array *op_array, zend_op *opline, uint32_t *shiftlist) {
916 switch (opline->opcode) {
917 case ZEND_JMP:
918 case ZEND_FAST_CALL:
919 ZEND_SET_OP_JMP_ADDR(opline, opline->op1, ZEND_OP1_JMP_ADDR(opline) - shiftlist[ZEND_OP1_JMP_ADDR(opline) - op_array->opcodes]);
920 break;
921 case ZEND_JMPZNZ:
922 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)]);
923 /* break missing intentionally */
924 case ZEND_JMPZ:
925 case ZEND_JMPNZ:
926 case ZEND_JMPZ_EX:
927 case ZEND_JMPNZ_EX:
928 case ZEND_FE_RESET_R:
929 case ZEND_FE_RESET_RW:
930 case ZEND_JMP_SET:
931 case ZEND_COALESCE:
932 case ZEND_ASSERT_CHECK:
933 ZEND_SET_OP_JMP_ADDR(opline, opline->op2, ZEND_OP2_JMP_ADDR(opline) - shiftlist[ZEND_OP2_JMP_ADDR(opline) - op_array->opcodes]);
934 break;
935 case ZEND_CATCH:
936 if (!(opline->extended_value & ZEND_LAST_CATCH)) {
937 ZEND_SET_OP_JMP_ADDR(opline, opline->op2, ZEND_OP2_JMP_ADDR(opline) - shiftlist[ZEND_OP2_JMP_ADDR(opline) - op_array->opcodes]);
938 }
939 break;
940 case ZEND_DECLARE_ANON_CLASS:
941 case ZEND_DECLARE_ANON_INHERITED_CLASS:
942 case ZEND_FE_FETCH_R:
943 case ZEND_FE_FETCH_RW:
944 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)]);
945 break;
946 case ZEND_SWITCH_LONG:
947 case ZEND_SWITCH_STRING:
948 {
949 HashTable *jumptable = Z_ARRVAL(ZEND_OP2_LITERAL(opline));
950 zval *zv;
951 ZEND_HASH_FOREACH_VAL(jumptable, zv) {
952 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))]);
953 } ZEND_HASH_FOREACH_END();
954 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)]);
955 break;
956 }
957 }
958 }
959
get_class_entry_from_op1(zend_script * script,zend_op_array * op_array,zend_op * opline,zend_bool rt_constants)960 static zend_class_entry *get_class_entry_from_op1(
961 zend_script *script, zend_op_array *op_array, zend_op *opline, zend_bool rt_constants) {
962 if (opline->op1_type == IS_CONST) {
963 zval *op1 = CRT_CONSTANT_EX(op_array, opline, opline->op1, rt_constants);
964 if (Z_TYPE_P(op1) == IS_STRING) {
965 zend_string *class_name = Z_STR_P(op1 + 1);
966 zend_class_entry *ce;
967 if (script && (ce = zend_hash_find_ptr(&script->class_table, class_name))) {
968 return ce;
969 } else if ((ce = zend_hash_find_ptr(EG(class_table), class_name))) {
970 if (ce->type == ZEND_INTERNAL_CLASS) {
971 return ce;
972 } else if (ce->type == ZEND_USER_CLASS &&
973 ce->info.user.filename &&
974 ce->info.user.filename == op_array->filename) {
975 return ce;
976 }
977 }
978 }
979 } else if (opline->op1_type == IS_UNUSED && op_array->scope
980 && !(op_array->scope->ce_flags & ZEND_ACC_TRAIT)
981 && (opline->op1.num & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_SELF) {
982 return op_array->scope;
983 }
984 return NULL;
985 }
986
zend_optimizer_get_called_func(zend_script * script,zend_op_array * op_array,zend_op * opline,zend_bool rt_constants)987 zend_function *zend_optimizer_get_called_func(
988 zend_script *script, zend_op_array *op_array, zend_op *opline, zend_bool rt_constants)
989 {
990 #define GET_OP(op) CRT_CONSTANT_EX(op_array, opline, opline->op, rt_constants)
991 switch (opline->opcode) {
992 case ZEND_INIT_FCALL:
993 {
994 zend_string *function_name = Z_STR_P(GET_OP(op2));
995 zend_function *func;
996 if (script && (func = zend_hash_find_ptr(&script->function_table, function_name)) != NULL) {
997 return func;
998 } else if ((func = zend_hash_find_ptr(EG(function_table), function_name)) != NULL) {
999 if (func->type == ZEND_INTERNAL_FUNCTION) {
1000 return func;
1001 } else if (func->type == ZEND_USER_FUNCTION &&
1002 func->op_array.filename &&
1003 func->op_array.filename == op_array->filename) {
1004 return func;
1005 }
1006 }
1007 break;
1008 }
1009 case ZEND_INIT_FCALL_BY_NAME:
1010 case ZEND_INIT_NS_FCALL_BY_NAME:
1011 if (opline->op2_type == IS_CONST && Z_TYPE_P(GET_OP(op2)) == IS_STRING) {
1012 zval *function_name = GET_OP(op2) + 1;
1013 zend_function *func;
1014 if (script && (func = zend_hash_find_ptr(&script->function_table, Z_STR_P(function_name)))) {
1015 return func;
1016 } else if ((func = zend_hash_find_ptr(EG(function_table), Z_STR_P(function_name))) != NULL) {
1017 if (func->type == ZEND_INTERNAL_FUNCTION) {
1018 return func;
1019 } else if (func->type == ZEND_USER_FUNCTION &&
1020 func->op_array.filename &&
1021 func->op_array.filename == op_array->filename) {
1022 return func;
1023 }
1024 }
1025 }
1026 break;
1027 case ZEND_INIT_STATIC_METHOD_CALL:
1028 if (opline->op2_type == IS_CONST && Z_TYPE_P(GET_OP(op2)) == IS_STRING) {
1029 zend_class_entry *ce = get_class_entry_from_op1(
1030 script, op_array, opline, rt_constants);
1031 if (ce) {
1032 zend_string *func_name = Z_STR_P(GET_OP(op2) + 1);
1033 return zend_hash_find_ptr(&ce->function_table, func_name);
1034 }
1035 }
1036 break;
1037 case ZEND_INIT_METHOD_CALL:
1038 if (opline->op1_type == IS_UNUSED
1039 && opline->op2_type == IS_CONST && Z_TYPE_P(GET_OP(op2)) == IS_STRING
1040 && op_array->scope && !(op_array->scope->ce_flags & ZEND_ACC_TRAIT)) {
1041 zend_string *method_name = Z_STR_P(GET_OP(op2) + 1);
1042 zend_function *fbc = zend_hash_find_ptr(
1043 &op_array->scope->function_table, method_name);
1044 if (fbc) {
1045 zend_bool is_private = (fbc->common.fn_flags & ZEND_ACC_PRIVATE) != 0;
1046 zend_bool is_final = (fbc->common.fn_flags & ZEND_ACC_FINAL) != 0;
1047 zend_bool same_scope = fbc->common.scope == op_array->scope;
1048 if ((is_private && same_scope)
1049 || (is_final && (!is_private || same_scope))) {
1050 return fbc;
1051 }
1052 }
1053 }
1054 break;
1055 case ZEND_NEW:
1056 {
1057 zend_class_entry *ce = get_class_entry_from_op1(
1058 script, op_array, opline, rt_constants);
1059 if (ce && ce->type == ZEND_USER_CLASS) {
1060 return ce->constructor;
1061 }
1062 break;
1063 }
1064 }
1065 return NULL;
1066 #undef GET_OP
1067 }
1068
zend_optimizer_classify_function(zend_string * name,uint32_t num_args)1069 uint32_t zend_optimizer_classify_function(zend_string *name, uint32_t num_args) {
1070 if (zend_string_equals_literal(name, "extract")) {
1071 return ZEND_FUNC_INDIRECT_VAR_ACCESS;
1072 } else if (zend_string_equals_literal(name, "compact")) {
1073 return ZEND_FUNC_INDIRECT_VAR_ACCESS;
1074 } else if (zend_string_equals_literal(name, "parse_str") && num_args <= 1) {
1075 return ZEND_FUNC_INDIRECT_VAR_ACCESS;
1076 } else if (zend_string_equals_literal(name, "mb_parse_str") && num_args <= 1) {
1077 return ZEND_FUNC_INDIRECT_VAR_ACCESS;
1078 } else if (zend_string_equals_literal(name, "get_defined_vars")) {
1079 return ZEND_FUNC_INDIRECT_VAR_ACCESS;
1080 } else if (zend_string_equals_literal(name, "assert")) {
1081 return ZEND_FUNC_INDIRECT_VAR_ACCESS;
1082 } else if (zend_string_equals_literal(name, "db2_execute")) {
1083 return ZEND_FUNC_INDIRECT_VAR_ACCESS;
1084 } else if (zend_string_equals_literal(name, "func_num_args")) {
1085 return ZEND_FUNC_VARARG;
1086 } else if (zend_string_equals_literal(name, "func_get_arg")) {
1087 return ZEND_FUNC_VARARG;
1088 } else if (zend_string_equals_literal(name, "func_get_args")) {
1089 return ZEND_FUNC_VARARG;
1090 } else {
1091 return 0;
1092 }
1093 }
1094
zend_optimize(zend_op_array * op_array,zend_optimizer_ctx * ctx)1095 static void zend_optimize(zend_op_array *op_array,
1096 zend_optimizer_ctx *ctx)
1097 {
1098 if (op_array->type == ZEND_EVAL_CODE) {
1099 return;
1100 }
1101
1102 if (ctx->debug_level & ZEND_DUMP_BEFORE_OPTIMIZER) {
1103 zend_dump_op_array(op_array, 0, "before optimizer", NULL);
1104 }
1105
1106 /* pass 1
1107 * - substitute persistent constants (true, false, null, etc)
1108 * - perform compile-time evaluation of constant binary and unary operations
1109 * - optimize series of ADD_STRING and/or ADD_CHAR
1110 * - convert CAST(IS_BOOL,x) into BOOL(x)
1111 * - pre-evaluate constant function calls
1112 */
1113 if (ZEND_OPTIMIZER_PASS_1 & ctx->optimization_level) {
1114 zend_optimizer_pass1(op_array, ctx);
1115 if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_1) {
1116 zend_dump_op_array(op_array, 0, "after pass 1", NULL);
1117 }
1118 }
1119
1120 /* pass 2:
1121 * - convert non-numeric constants to numeric constants in numeric operators
1122 * - optimize constant conditional JMPs
1123 */
1124 if (ZEND_OPTIMIZER_PASS_2 & ctx->optimization_level) {
1125 zend_optimizer_pass2(op_array);
1126 if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_2) {
1127 zend_dump_op_array(op_array, 0, "after pass 2", NULL);
1128 }
1129 }
1130
1131 /* pass 3:
1132 * - optimize $i = $i+expr to $i+=expr
1133 * - optimize series of JMPs
1134 * - change $i++ to ++$i where possible
1135 */
1136 if (ZEND_OPTIMIZER_PASS_3 & ctx->optimization_level) {
1137 zend_optimizer_pass3(op_array, ctx);
1138 if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_3) {
1139 zend_dump_op_array(op_array, 0, "after pass 3", NULL);
1140 }
1141 }
1142
1143 /* pass 4:
1144 * - INIT_FCALL_BY_NAME -> DO_FCALL
1145 */
1146 if (ZEND_OPTIMIZER_PASS_4 & ctx->optimization_level) {
1147 zend_optimize_func_calls(op_array, ctx);
1148 if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_4) {
1149 zend_dump_op_array(op_array, 0, "after pass 4", NULL);
1150 }
1151 }
1152
1153 /* pass 5:
1154 * - CFG optimization
1155 */
1156 if (ZEND_OPTIMIZER_PASS_5 & ctx->optimization_level) {
1157 zend_optimize_cfg(op_array, ctx);
1158 if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_5) {
1159 zend_dump_op_array(op_array, 0, "after pass 5", NULL);
1160 }
1161 }
1162
1163 #if HAVE_DFA_PASS
1164 /* pass 6:
1165 * - DFA optimization
1166 */
1167 if ((ZEND_OPTIMIZER_PASS_6 & ctx->optimization_level) &&
1168 !(ZEND_OPTIMIZER_PASS_7 & ctx->optimization_level)) {
1169 zend_optimize_dfa(op_array, ctx);
1170 if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_6) {
1171 zend_dump_op_array(op_array, 0, "after pass 6", NULL);
1172 }
1173 }
1174 #endif
1175
1176 /* pass 9:
1177 * - Optimize temp variables usage
1178 */
1179 if (ZEND_OPTIMIZER_PASS_9 & ctx->optimization_level) {
1180 zend_optimize_temporary_variables(op_array, ctx);
1181 if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_9) {
1182 zend_dump_op_array(op_array, 0, "after pass 9", NULL);
1183 }
1184 }
1185
1186 /* pass 10:
1187 * - remove NOPs
1188 */
1189 if (((ZEND_OPTIMIZER_PASS_10|ZEND_OPTIMIZER_PASS_5) & ctx->optimization_level) == ZEND_OPTIMIZER_PASS_10) {
1190 zend_optimizer_nop_removal(op_array, ctx);
1191 if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_10) {
1192 zend_dump_op_array(op_array, 0, "after pass 10", NULL);
1193 }
1194 }
1195
1196 /* pass 11:
1197 * - Compact literals table
1198 */
1199 if ((ZEND_OPTIMIZER_PASS_11 & ctx->optimization_level) &&
1200 (!(ZEND_OPTIMIZER_PASS_6 & ctx->optimization_level) ||
1201 !(ZEND_OPTIMIZER_PASS_7 & ctx->optimization_level))) {
1202 zend_optimizer_compact_literals(op_array, ctx);
1203 if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_11) {
1204 zend_dump_op_array(op_array, 0, "after pass 11", NULL);
1205 }
1206 }
1207
1208 if ((ZEND_OPTIMIZER_PASS_13 & ctx->optimization_level) &&
1209 (!(ZEND_OPTIMIZER_PASS_6 & ctx->optimization_level) ||
1210 !(ZEND_OPTIMIZER_PASS_7 & ctx->optimization_level))) {
1211 zend_optimizer_compact_vars(op_array);
1212 if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_13) {
1213 zend_dump_op_array(op_array, 0, "after pass 13", NULL);
1214 }
1215 }
1216
1217 if (ZEND_OPTIMIZER_PASS_7 & ctx->optimization_level) {
1218 return;
1219 }
1220
1221 if (ctx->debug_level & ZEND_DUMP_AFTER_OPTIMIZER) {
1222 zend_dump_op_array(op_array, 0, "after optimizer", NULL);
1223 }
1224 }
1225
zend_revert_pass_two(zend_op_array * op_array)1226 static void zend_revert_pass_two(zend_op_array *op_array)
1227 {
1228 zend_op *opline, *end;
1229
1230 opline = op_array->opcodes;
1231 end = opline + op_array->last;
1232 while (opline < end) {
1233 if (opline->op1_type == IS_CONST) {
1234 ZEND_PASS_TWO_UNDO_CONSTANT(op_array, opline, opline->op1);
1235 }
1236 if (opline->op2_type == IS_CONST) {
1237 ZEND_PASS_TWO_UNDO_CONSTANT(op_array, opline, opline->op2);
1238 }
1239 opline++;
1240 }
1241 #if !ZEND_USE_ABS_CONST_ADDR
1242 if (op_array->literals) {
1243 zval *literals = emalloc(sizeof(zval) * op_array->last_literal);
1244 memcpy(literals, op_array->literals, sizeof(zval) * op_array->last_literal);
1245 op_array->literals = literals;
1246 }
1247 #endif
1248 }
1249
zend_redo_pass_two(zend_op_array * op_array)1250 static void zend_redo_pass_two(zend_op_array *op_array)
1251 {
1252 zend_op *opline, *end;
1253 #if ZEND_USE_ABS_JMP_ADDR && !ZEND_USE_ABS_CONST_ADDR
1254 zend_op *old_opcodes = op_array->opcodes;
1255 #endif
1256
1257 #if !ZEND_USE_ABS_CONST_ADDR
1258 if (op_array->last_literal) {
1259 op_array->opcodes = (zend_op *) erealloc(op_array->opcodes,
1260 ZEND_MM_ALIGNED_SIZE_EX(sizeof(zend_op) * op_array->last, 16) +
1261 sizeof(zval) * op_array->last_literal);
1262 memcpy(((char*)op_array->opcodes) + ZEND_MM_ALIGNED_SIZE_EX(sizeof(zend_op) * op_array->last, 16),
1263 op_array->literals, sizeof(zval) * op_array->last_literal);
1264 efree(op_array->literals);
1265 op_array->literals = (zval*)(((char*)op_array->opcodes) + ZEND_MM_ALIGNED_SIZE_EX(sizeof(zend_op) * op_array->last, 16));
1266 } else {
1267 if (op_array->literals) {
1268 efree(op_array->literals);
1269 }
1270 op_array->literals = NULL;
1271 }
1272 #endif
1273
1274 opline = op_array->opcodes;
1275 end = opline + op_array->last;
1276 while (opline < end) {
1277 if (opline->op1_type == IS_CONST) {
1278 ZEND_PASS_TWO_UPDATE_CONSTANT(op_array, opline, opline->op1);
1279 }
1280 if (opline->op2_type == IS_CONST) {
1281 ZEND_PASS_TWO_UPDATE_CONSTANT(op_array, opline, opline->op2);
1282 }
1283 #if ZEND_USE_ABS_JMP_ADDR && !ZEND_USE_ABS_CONST_ADDR
1284 if (op_array->fn_flags & ZEND_ACC_DONE_PASS_TWO) {
1285 /* fix jumps to point to new array */
1286 switch (opline->opcode) {
1287 case ZEND_JMP:
1288 case ZEND_FAST_CALL:
1289 opline->op1.jmp_addr = &op_array->opcodes[opline->op1.jmp_addr - old_opcodes];
1290 break;
1291 case ZEND_JMPZNZ:
1292 /* relative extended_value don't have to be changed */
1293 /* break omitted intentionally */
1294 case ZEND_JMPZ:
1295 case ZEND_JMPNZ:
1296 case ZEND_JMPZ_EX:
1297 case ZEND_JMPNZ_EX:
1298 case ZEND_JMP_SET:
1299 case ZEND_COALESCE:
1300 case ZEND_FE_RESET_R:
1301 case ZEND_FE_RESET_RW:
1302 case ZEND_ASSERT_CHECK:
1303 opline->op2.jmp_addr = &op_array->opcodes[opline->op2.jmp_addr - old_opcodes];
1304 break;
1305 case ZEND_CATCH:
1306 if (!(opline->extended_value & ZEND_LAST_CATCH)) {
1307 opline->op2.jmp_addr = &op_array->opcodes[opline->op2.jmp_addr - old_opcodes];
1308 }
1309 break;
1310 case ZEND_DECLARE_ANON_CLASS:
1311 case ZEND_DECLARE_ANON_INHERITED_CLASS:
1312 case ZEND_FE_FETCH_R:
1313 case ZEND_FE_FETCH_RW:
1314 case ZEND_SWITCH_LONG:
1315 case ZEND_SWITCH_STRING:
1316 /* relative extended_value don't have to be changed */
1317 break;
1318 }
1319 }
1320 #endif
1321 ZEND_VM_SET_OPCODE_HANDLER(opline);
1322 opline++;
1323 }
1324 }
1325
1326 #if HAVE_DFA_PASS
zend_redo_pass_two_ex(zend_op_array * op_array,zend_ssa * ssa)1327 static void zend_redo_pass_two_ex(zend_op_array *op_array, zend_ssa *ssa)
1328 {
1329 zend_op *opline, *end;
1330 #if ZEND_USE_ABS_JMP_ADDR && !ZEND_USE_ABS_CONST_ADDR
1331 zend_op *old_opcodes = op_array->opcodes;
1332 #endif
1333
1334 #if !ZEND_USE_ABS_CONST_ADDR
1335 if (op_array->last_literal) {
1336 op_array->opcodes = (zend_op *) erealloc(op_array->opcodes,
1337 ZEND_MM_ALIGNED_SIZE_EX(sizeof(zend_op) * op_array->last, 16) +
1338 sizeof(zval) * op_array->last_literal);
1339 memcpy(((char*)op_array->opcodes) + ZEND_MM_ALIGNED_SIZE_EX(sizeof(zend_op) * op_array->last, 16),
1340 op_array->literals, sizeof(zval) * op_array->last_literal);
1341 efree(op_array->literals);
1342 op_array->literals = (zval*)(((char*)op_array->opcodes) + ZEND_MM_ALIGNED_SIZE_EX(sizeof(zend_op) * op_array->last, 16));
1343 } else {
1344 if (op_array->literals) {
1345 efree(op_array->literals);
1346 }
1347 op_array->literals = NULL;
1348 }
1349 #endif
1350
1351 opline = op_array->opcodes;
1352 end = opline + op_array->last;
1353 while (opline < end) {
1354 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));
1355 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));
1356 uint32_t res_info =
1357 (opline->opcode == ZEND_PRE_INC ||
1358 opline->opcode == ZEND_PRE_DEC ||
1359 opline->opcode == ZEND_POST_INC ||
1360 opline->opcode == ZEND_POST_DEC) ?
1361 ((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) :
1362 (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)));
1363
1364 if (opline->op1_type == IS_CONST) {
1365 ZEND_PASS_TWO_UPDATE_CONSTANT(op_array, opline, opline->op1);
1366 }
1367 if (opline->op2_type == IS_CONST) {
1368 ZEND_PASS_TWO_UPDATE_CONSTANT(op_array, opline, opline->op2);
1369 }
1370
1371 zend_vm_set_opcode_handler_ex(opline, op1_info, op2_info, res_info);
1372 #if ZEND_USE_ABS_JMP_ADDR && !ZEND_USE_ABS_CONST_ADDR
1373 if (op_array->fn_flags & ZEND_ACC_DONE_PASS_TWO) {
1374 /* fix jumps to point to new array */
1375 switch (opline->opcode) {
1376 case ZEND_JMP:
1377 case ZEND_FAST_CALL:
1378 opline->op1.jmp_addr = &op_array->opcodes[opline->op1.jmp_addr - old_opcodes];
1379 break;
1380 case ZEND_JMPZNZ:
1381 /* relative extended_value don't have to be changed */
1382 /* break omitted intentionally */
1383 case ZEND_JMPZ:
1384 case ZEND_JMPNZ:
1385 case ZEND_JMPZ_EX:
1386 case ZEND_JMPNZ_EX:
1387 case ZEND_JMP_SET:
1388 case ZEND_COALESCE:
1389 case ZEND_FE_RESET_R:
1390 case ZEND_FE_RESET_RW:
1391 case ZEND_ASSERT_CHECK:
1392 opline->op2.jmp_addr = &op_array->opcodes[opline->op2.jmp_addr - old_opcodes];
1393 break;
1394 case ZEND_CATCH:
1395 if (!(opline->extended_value & ZEND_LAST_CATCH)) {
1396 opline->op2.jmp_addr = &op_array->opcodes[opline->op2.jmp_addr - old_opcodes];
1397 }
1398 break;
1399 case ZEND_DECLARE_ANON_CLASS:
1400 case ZEND_DECLARE_ANON_INHERITED_CLASS:
1401 case ZEND_FE_FETCH_R:
1402 case ZEND_FE_FETCH_RW:
1403 case ZEND_SWITCH_LONG:
1404 case ZEND_SWITCH_STRING:
1405 /* relative extended_value don't have to be changed */
1406 break;
1407 }
1408 }
1409 #endif
1410 opline++;
1411 }
1412 }
1413 #endif
1414
zend_optimize_op_array(zend_op_array * op_array,zend_optimizer_ctx * ctx)1415 static void zend_optimize_op_array(zend_op_array *op_array,
1416 zend_optimizer_ctx *ctx)
1417 {
1418 /* Revert pass_two() */
1419 zend_revert_pass_two(op_array);
1420
1421 /* Do actual optimizations */
1422 zend_optimize(op_array, ctx);
1423
1424 /* Redo pass_two() */
1425 zend_redo_pass_two(op_array);
1426 }
1427
zend_adjust_fcall_stack_size(zend_op_array * op_array,zend_optimizer_ctx * ctx)1428 static void zend_adjust_fcall_stack_size(zend_op_array *op_array, zend_optimizer_ctx *ctx)
1429 {
1430 zend_function *func;
1431 zend_op *opline, *end;
1432
1433 opline = op_array->opcodes;
1434 end = opline + op_array->last;
1435 while (opline < end) {
1436 if (opline->opcode == ZEND_INIT_FCALL) {
1437 func = zend_hash_find_ptr(
1438 &ctx->script->function_table,
1439 Z_STR_P(RT_CONSTANT(opline, opline->op2)));
1440 if (func) {
1441 opline->op1.num = zend_vm_calc_used_stack(opline->extended_value, func);
1442 }
1443 }
1444 opline++;
1445 }
1446 }
1447
1448 #if HAVE_DFA_PASS
zend_adjust_fcall_stack_size_graph(zend_op_array * op_array)1449 static void zend_adjust_fcall_stack_size_graph(zend_op_array *op_array)
1450 {
1451 zend_func_info *func_info = ZEND_FUNC_INFO(op_array);
1452
1453 if (func_info) {
1454 zend_call_info *call_info =func_info->callee_info;
1455
1456 while (call_info) {
1457 zend_op *opline = call_info->caller_init_opline;
1458
1459 if (opline && call_info->callee_func && opline->opcode == ZEND_INIT_FCALL) {
1460 opline->op1.num = zend_vm_calc_used_stack(opline->extended_value, call_info->callee_func);
1461 }
1462 call_info = call_info->next_callee;
1463 }
1464 }
1465 }
1466 #endif
1467
zend_optimize_script(zend_script * script,zend_long optimization_level,zend_long debug_level)1468 int zend_optimize_script(zend_script *script, zend_long optimization_level, zend_long debug_level)
1469 {
1470 zend_class_entry *ce;
1471 zend_op_array *op_array;
1472 zend_string *name;
1473 zend_optimizer_ctx ctx;
1474 #if HAVE_DFA_PASS
1475 zend_call_graph call_graph;
1476 #endif
1477
1478 ctx.arena = zend_arena_create(64 * 1024);
1479 ctx.script = script;
1480 ctx.constants = NULL;
1481 ctx.optimization_level = optimization_level;
1482 ctx.debug_level = debug_level;
1483
1484 zend_optimize_op_array(&script->main_op_array, &ctx);
1485
1486 ZEND_HASH_FOREACH_PTR(&script->function_table, op_array) {
1487 zend_optimize_op_array(op_array, &ctx);
1488 } ZEND_HASH_FOREACH_END();
1489
1490 ZEND_HASH_FOREACH_PTR(&script->class_table, ce) {
1491 ZEND_HASH_FOREACH_STR_KEY_PTR(&ce->function_table, name, op_array) {
1492 if (op_array->scope == ce) {
1493 zend_optimize_op_array(op_array, &ctx);
1494 } else if (op_array->type == ZEND_USER_FUNCTION) {
1495 zend_op_array *orig_op_array;
1496 if ((orig_op_array = zend_hash_find_ptr(&op_array->scope->function_table, name)) != NULL) {
1497 HashTable *ht = op_array->static_variables;
1498 *op_array = *orig_op_array;
1499 op_array->static_variables = ht;
1500 }
1501 }
1502 } ZEND_HASH_FOREACH_END();
1503 } ZEND_HASH_FOREACH_END();
1504
1505 #if HAVE_DFA_PASS
1506 if ((ZEND_OPTIMIZER_PASS_6 & optimization_level) &&
1507 (ZEND_OPTIMIZER_PASS_7 & optimization_level) &&
1508 zend_build_call_graph(&ctx.arena, script, ZEND_RT_CONSTANTS, &call_graph) == SUCCESS) {
1509 /* Optimize using call-graph */
1510 void *checkpoint = zend_arena_checkpoint(ctx.arena);
1511 int i;
1512 zend_func_info *func_info;
1513
1514 for (i = 0; i < call_graph.op_arrays_count; i++) {
1515 zend_revert_pass_two(call_graph.op_arrays[i]);
1516 }
1517
1518 for (i = 0; i < call_graph.op_arrays_count; i++) {
1519 func_info = ZEND_FUNC_INFO(call_graph.op_arrays[i]);
1520 if (func_info) {
1521 func_info->call_map = zend_build_call_map(&ctx.arena, func_info, call_graph.op_arrays[i]);
1522 if (call_graph.op_arrays[i]->fn_flags & ZEND_ACC_HAS_RETURN_TYPE) {
1523 zend_init_func_return_info(call_graph.op_arrays[i], script, &func_info->return_info);
1524 }
1525 }
1526 }
1527
1528 for (i = 0; i < call_graph.op_arrays_count; i++) {
1529 func_info = ZEND_FUNC_INFO(call_graph.op_arrays[i]);
1530 if (func_info) {
1531 if (zend_dfa_analyze_op_array(call_graph.op_arrays[i], &ctx, &func_info->ssa) == SUCCESS) {
1532 func_info->flags = func_info->ssa.cfg.flags;
1533 } else {
1534 ZEND_SET_FUNC_INFO(call_graph.op_arrays[i], NULL);
1535 }
1536 }
1537 }
1538
1539 //TODO: perform inner-script inference???
1540 for (i = 0; i < call_graph.op_arrays_count; i++) {
1541 func_info = ZEND_FUNC_INFO(call_graph.op_arrays[i]);
1542 if (func_info) {
1543 zend_dfa_optimize_op_array(call_graph.op_arrays[i], &ctx, &func_info->ssa, func_info->call_map);
1544 }
1545 }
1546
1547 if (debug_level & ZEND_DUMP_AFTER_PASS_7) {
1548 for (i = 0; i < call_graph.op_arrays_count; i++) {
1549 zend_dump_op_array(call_graph.op_arrays[i], 0, "after pass 7", NULL);
1550 }
1551 }
1552
1553 if (ZEND_OPTIMIZER_PASS_11 & optimization_level) {
1554 for (i = 0; i < call_graph.op_arrays_count; i++) {
1555 zend_optimizer_compact_literals(call_graph.op_arrays[i], &ctx);
1556 if (debug_level & ZEND_DUMP_AFTER_PASS_11) {
1557 zend_dump_op_array(call_graph.op_arrays[i], 0, "after pass 11", NULL);
1558 }
1559 }
1560 }
1561
1562 if (ZEND_OPTIMIZER_PASS_13 & optimization_level) {
1563 for (i = 0; i < call_graph.op_arrays_count; i++) {
1564 zend_optimizer_compact_vars(call_graph.op_arrays[i]);
1565 if (debug_level & ZEND_DUMP_AFTER_PASS_13) {
1566 zend_dump_op_array(call_graph.op_arrays[i], 0, "after pass 13", NULL);
1567 }
1568 }
1569 }
1570
1571 if (ZEND_OPTIMIZER_PASS_12 & optimization_level) {
1572 for (i = 0; i < call_graph.op_arrays_count; i++) {
1573 zend_adjust_fcall_stack_size_graph(call_graph.op_arrays[i]);
1574 }
1575 }
1576
1577 for (i = 0; i < call_graph.op_arrays_count; i++) {
1578 func_info = ZEND_FUNC_INFO(call_graph.op_arrays[i]);
1579 if (func_info && func_info->ssa.var_info) {
1580 zend_redo_pass_two_ex(call_graph.op_arrays[i], &func_info->ssa);
1581 } else {
1582 zend_redo_pass_two(call_graph.op_arrays[i]);
1583 }
1584 }
1585
1586 for (i = 0; i < call_graph.op_arrays_count; i++) {
1587 ZEND_SET_FUNC_INFO(call_graph.op_arrays[i], NULL);
1588 }
1589
1590 ZEND_HASH_FOREACH_PTR(&script->class_table, ce) {
1591 ZEND_HASH_FOREACH_STR_KEY_PTR(&ce->function_table, name, op_array) {
1592 if (op_array->scope != ce) {
1593 zend_op_array *orig_op_array;
1594 if ((orig_op_array = zend_hash_find_ptr(&op_array->scope->function_table, name)) != NULL) {
1595 HashTable *ht = op_array->static_variables;
1596 *op_array = *orig_op_array;
1597 op_array->static_variables = ht;
1598 }
1599 }
1600 } ZEND_HASH_FOREACH_END();
1601 } ZEND_HASH_FOREACH_END();
1602
1603 zend_arena_release(&ctx.arena, checkpoint);
1604 } else
1605 #endif
1606
1607 if (ZEND_OPTIMIZER_PASS_12 & optimization_level) {
1608 zend_adjust_fcall_stack_size(&script->main_op_array, &ctx);
1609
1610 ZEND_HASH_FOREACH_PTR(&script->function_table, op_array) {
1611 zend_adjust_fcall_stack_size(op_array, &ctx);
1612 } ZEND_HASH_FOREACH_END();
1613
1614 ZEND_HASH_FOREACH_PTR(&script->class_table, ce) {
1615 ZEND_HASH_FOREACH_STR_KEY_PTR(&ce->function_table, name, op_array) {
1616 if (op_array->scope == ce) {
1617 zend_adjust_fcall_stack_size(op_array, &ctx);
1618 } else if (op_array->type == ZEND_USER_FUNCTION) {
1619 zend_op_array *orig_op_array;
1620 if ((orig_op_array = zend_hash_find_ptr(&op_array->scope->function_table, name)) != NULL) {
1621 HashTable *ht = op_array->static_variables;
1622 *op_array = *orig_op_array;
1623 op_array->static_variables = ht;
1624 }
1625 }
1626 } ZEND_HASH_FOREACH_END();
1627 } ZEND_HASH_FOREACH_END();
1628 }
1629
1630 if ((debug_level & ZEND_DUMP_AFTER_OPTIMIZER) &&
1631 (ZEND_OPTIMIZER_PASS_7 & optimization_level)) {
1632 zend_dump_op_array(&script->main_op_array, ZEND_DUMP_RT_CONSTANTS, "after optimizer", NULL);
1633
1634 ZEND_HASH_FOREACH_PTR(&script->function_table, op_array) {
1635 zend_dump_op_array(op_array, ZEND_DUMP_RT_CONSTANTS, "after optimizer", NULL);
1636 } ZEND_HASH_FOREACH_END();
1637
1638 ZEND_HASH_FOREACH_PTR(&script->class_table, ce) {
1639 ZEND_HASH_FOREACH_STR_KEY_PTR(&ce->function_table, name, op_array) {
1640 if (op_array->scope == ce) {
1641 zend_dump_op_array(op_array, ZEND_DUMP_RT_CONSTANTS, "after optimizer", NULL);
1642 }
1643 } ZEND_HASH_FOREACH_END();
1644 } ZEND_HASH_FOREACH_END();
1645 }
1646
1647 if (ctx.constants) {
1648 zend_hash_destroy(ctx.constants);
1649 }
1650 zend_arena_destroy(ctx.arena);
1651
1652 return 1;
1653 }
1654
zend_optimizer_startup(void)1655 int zend_optimizer_startup(void)
1656 {
1657 return zend_func_info_startup();
1658 }
1659
zend_optimizer_shutdown(void)1660 int zend_optimizer_shutdown(void)
1661 {
1662 return zend_func_info_shutdown();
1663 }
1664
1665 /*
1666 * Local variables:
1667 * tab-width: 4
1668 * c-basic-offset: 4
1669 * indent-tabs-mode: t
1670 * End:
1671 */
1672