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@zend.com> |
16 | Zeev Suraski <zeev@zend.com> |
17 | Stanislav Malyshev <stas@zend.com> |
18 | Dmitry Stogov <dmitry@zend.com> |
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_DUP(&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_DUP(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_CACHE_SLOT(op_array->literals[i]) = -1;
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_op1(zend_op_array * op_array,zend_op * opline,uint32_t num)235 static inline void alloc_cache_slots_op1(zend_op_array *op_array, zend_op *opline, uint32_t num) {
236 Z_CACHE_SLOT(op_array->literals[opline->op1.constant]) = op_array->cache_size;
237 op_array->cache_size += num * sizeof(void *);
238 }
alloc_cache_slots_op2(zend_op_array * op_array,zend_op * opline,uint32_t num)239 static inline void alloc_cache_slots_op2(zend_op_array *op_array, zend_op *opline, uint32_t num) {
240 Z_CACHE_SLOT(op_array->literals[opline->op2.constant]) = op_array->cache_size;
241 op_array->cache_size += num * sizeof(void *);
242 }
243
244 #define REQUIRES_STRING(val) do { \
245 if (Z_TYPE_P(val) != IS_STRING) { \
246 return 0; \
247 } \
248 } while (0)
249
250 #define TO_STRING_NOWARN(val) do { \
251 if (Z_TYPE_P(val) >= IS_ARRAY) { \
252 return 0; \
253 } \
254 convert_to_string(val); \
255 } while (0)
256
zend_optimizer_update_op1_const(zend_op_array * op_array,zend_op * opline,zval * val)257 int zend_optimizer_update_op1_const(zend_op_array *op_array,
258 zend_op *opline,
259 zval *val)
260 {
261 switch (opline->opcode) {
262 case ZEND_FREE:
263 case ZEND_CHECK_VAR:
264 MAKE_NOP(opline);
265 zval_ptr_dtor_nogc(val);
266 return 1;
267 case ZEND_SEND_VAR_EX:
268 case ZEND_FETCH_DIM_W:
269 case ZEND_FETCH_DIM_RW:
270 case ZEND_FETCH_DIM_FUNC_ARG:
271 case ZEND_FETCH_DIM_UNSET:
272 case ZEND_ASSIGN_DIM:
273 case ZEND_RETURN_BY_REF:
274 return 0;
275 case ZEND_INIT_STATIC_METHOD_CALL:
276 case ZEND_CATCH:
277 case ZEND_FETCH_CONSTANT:
278 case ZEND_FETCH_CLASS_CONSTANT:
279 case ZEND_DEFINED:
280 case ZEND_NEW:
281 REQUIRES_STRING(val);
282 drop_leading_backslash(val);
283 opline->op1.constant = zend_optimizer_add_literal(op_array, val);
284 alloc_cache_slots_op1(op_array, opline, 1);
285 zend_optimizer_add_literal_string(op_array, zend_string_tolower(Z_STR_P(val)));
286 break;
287 case ZEND_FETCH_STATIC_PROP_R:
288 case ZEND_FETCH_STATIC_PROP_W:
289 case ZEND_FETCH_STATIC_PROP_RW:
290 case ZEND_FETCH_STATIC_PROP_IS:
291 case ZEND_FETCH_STATIC_PROP_UNSET:
292 case ZEND_FETCH_STATIC_PROP_FUNC_ARG:
293 TO_STRING_NOWARN(val);
294 opline->op1.constant = zend_optimizer_add_literal(op_array, val);
295 alloc_cache_slots_op1(op_array, opline, 2);
296 break;
297 case ZEND_SEND_VAR:
298 opline->opcode = ZEND_SEND_VAL;
299 opline->op1.constant = zend_optimizer_add_literal(op_array, val);
300 break;
301 case ZEND_SEPARATE:
302 case ZEND_SEND_VAR_NO_REF:
303 case ZEND_SEND_VAR_NO_REF_EX:
304 return 0;
305 case ZEND_VERIFY_RETURN_TYPE:
306 /* This would require a non-local change.
307 * zend_optimizer_replace_by_const() supports this. */
308 return 0;
309 case ZEND_CASE:
310 case ZEND_FETCH_LIST:
311 return 0;
312 case ZEND_CONCAT:
313 case ZEND_FAST_CONCAT:
314 case ZEND_FETCH_R:
315 case ZEND_FETCH_W:
316 case ZEND_FETCH_RW:
317 case ZEND_FETCH_IS:
318 case ZEND_FETCH_UNSET:
319 case ZEND_FETCH_FUNC_ARG:
320 TO_STRING_NOWARN(val);
321 /* break missing intentionally */
322 default:
323 opline->op1.constant = zend_optimizer_add_literal(op_array, val);
324 break;
325 }
326
327 opline->op1_type = IS_CONST;
328 if (Z_TYPE(ZEND_OP1_LITERAL(opline)) == IS_STRING) {
329 zend_string_hash_val(Z_STR(ZEND_OP1_LITERAL(opline)));
330 }
331 return 1;
332 }
333
zend_optimizer_update_op2_const(zend_op_array * op_array,zend_op * opline,zval * val)334 int zend_optimizer_update_op2_const(zend_op_array *op_array,
335 zend_op *opline,
336 zval *val)
337 {
338 zval tmp;
339
340 switch (opline->opcode) {
341 case ZEND_ASSIGN_REF:
342 case ZEND_FAST_CALL:
343 return 0;
344 case ZEND_FETCH_CLASS:
345 case ZEND_INIT_FCALL_BY_NAME:
346 /*case ZEND_INIT_NS_FCALL_BY_NAME:*/
347 case ZEND_ADD_INTERFACE:
348 case ZEND_ADD_TRAIT:
349 case ZEND_INSTANCEOF:
350 case ZEND_FETCH_STATIC_PROP_R:
351 case ZEND_FETCH_STATIC_PROP_W:
352 case ZEND_FETCH_STATIC_PROP_RW:
353 case ZEND_FETCH_STATIC_PROP_IS:
354 case ZEND_FETCH_STATIC_PROP_UNSET:
355 case ZEND_FETCH_STATIC_PROP_FUNC_ARG:
356 case ZEND_UNSET_STATIC_PROP:
357 case ZEND_ISSET_ISEMPTY_STATIC_PROP:
358 REQUIRES_STRING(val);
359 drop_leading_backslash(val);
360 opline->op2.constant = zend_optimizer_add_literal(op_array, val);
361 zend_optimizer_add_literal_string(op_array, zend_string_tolower(Z_STR_P(val)));
362 alloc_cache_slots_op2(op_array, opline, 1);
363 break;
364 case ZEND_INIT_FCALL:
365 REQUIRES_STRING(val);
366 if (Z_REFCOUNT_P(val) == 1) {
367 zend_str_tolower(Z_STRVAL_P(val), Z_STRLEN_P(val));
368 } else {
369 ZVAL_STR(&tmp, zend_string_tolower(Z_STR_P(val)));
370 zval_ptr_dtor_nogc(val);
371 val = &tmp;
372 }
373 opline->op2.constant = zend_optimizer_add_literal(op_array, val);
374 alloc_cache_slots_op2(op_array, opline, 1);
375 break;
376 case ZEND_INIT_DYNAMIC_CALL:
377 if (Z_TYPE_P(val) == IS_STRING) {
378 if (zend_memrchr(Z_STRVAL_P(val), ':', Z_STRLEN_P(val))) {
379 return 0;
380 }
381
382 if (zend_optimizer_classify_function(Z_STR_P(val), opline->extended_value)) {
383 /* Dynamic call to various special functions must stay dynamic,
384 * otherwise would drop a warning */
385 return 0;
386 }
387
388 opline->opcode = ZEND_INIT_FCALL_BY_NAME;
389 drop_leading_backslash(val);
390 opline->op2.constant = zend_optimizer_add_literal(op_array, val);
391 zend_optimizer_add_literal_string(op_array, zend_string_tolower(Z_STR_P(val)));
392 alloc_cache_slots_op2(op_array, opline, 1);
393 } else {
394 opline->op2.constant = zend_optimizer_add_literal(op_array, val);
395 }
396 break;
397 case ZEND_INIT_METHOD_CALL:
398 case ZEND_INIT_STATIC_METHOD_CALL:
399 REQUIRES_STRING(val);
400 opline->op2.constant = zend_optimizer_add_literal(op_array, val);
401 zend_optimizer_add_literal_string(op_array, zend_string_tolower(Z_STR_P(val)));
402 alloc_cache_slots_op2(op_array, opline, 2);
403 break;
404 /*case ZEND_FETCH_CLASS_CONSTANT:*/
405 case ZEND_ASSIGN_OBJ:
406 case ZEND_FETCH_OBJ_R:
407 case ZEND_FETCH_OBJ_W:
408 case ZEND_FETCH_OBJ_RW:
409 case ZEND_FETCH_OBJ_IS:
410 case ZEND_FETCH_OBJ_UNSET:
411 case ZEND_FETCH_OBJ_FUNC_ARG:
412 case ZEND_UNSET_OBJ:
413 case ZEND_PRE_INC_OBJ:
414 case ZEND_PRE_DEC_OBJ:
415 case ZEND_POST_INC_OBJ:
416 case ZEND_POST_DEC_OBJ:
417 case ZEND_ISSET_ISEMPTY_PROP_OBJ:
418 TO_STRING_NOWARN(val);
419 opline->op2.constant = zend_optimizer_add_literal(op_array, val);
420 alloc_cache_slots_op2(op_array, opline, 2);
421 break;
422 case ZEND_ASSIGN_ADD:
423 case ZEND_ASSIGN_SUB:
424 case ZEND_ASSIGN_MUL:
425 case ZEND_ASSIGN_DIV:
426 case ZEND_ASSIGN_POW:
427 case ZEND_ASSIGN_MOD:
428 case ZEND_ASSIGN_SL:
429 case ZEND_ASSIGN_SR:
430 case ZEND_ASSIGN_CONCAT:
431 case ZEND_ASSIGN_BW_OR:
432 case ZEND_ASSIGN_BW_AND:
433 case ZEND_ASSIGN_BW_XOR:
434 if (opline->extended_value == ZEND_ASSIGN_OBJ) {
435 TO_STRING_NOWARN(val);
436 opline->op2.constant = zend_optimizer_add_literal(op_array, val);
437 alloc_cache_slots_op2(op_array, opline, 2);
438 } else {
439 opline->op2.constant = zend_optimizer_add_literal(op_array, val);
440 }
441 break;
442 case ZEND_ISSET_ISEMPTY_DIM_OBJ:
443 case ZEND_ADD_ARRAY_ELEMENT:
444 case ZEND_INIT_ARRAY:
445 case ZEND_ASSIGN_DIM:
446 case ZEND_UNSET_DIM:
447 case ZEND_FETCH_DIM_R:
448 case ZEND_FETCH_DIM_W:
449 case ZEND_FETCH_DIM_RW:
450 case ZEND_FETCH_DIM_IS:
451 case ZEND_FETCH_DIM_FUNC_ARG:
452 case ZEND_FETCH_DIM_UNSET:
453 case ZEND_FETCH_LIST:
454 if (Z_TYPE_P(val) == IS_STRING) {
455 zend_ulong index;
456 if (ZEND_HANDLE_NUMERIC(Z_STR_P(val), index)) {
457 zval_ptr_dtor_nogc(val);
458 ZVAL_LONG(val, index);
459 }
460 }
461 opline->op2.constant = zend_optimizer_add_literal(op_array, val);
462 break;
463 case ZEND_ROPE_INIT:
464 case ZEND_ROPE_ADD:
465 case ZEND_ROPE_END:
466 case ZEND_CONCAT:
467 case ZEND_FAST_CONCAT:
468 TO_STRING_NOWARN(val);
469 /* break missing intentionally */
470 default:
471 opline->op2.constant = zend_optimizer_add_literal(op_array, val);
472 break;
473 }
474
475 opline->op2_type = IS_CONST;
476 if (Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_STRING) {
477 zend_string_hash_val(Z_STR(ZEND_OP2_LITERAL(opline)));
478 }
479 return 1;
480 }
481
zend_optimizer_remove_live_range(zend_op_array * op_array,uint32_t var)482 void zend_optimizer_remove_live_range(zend_op_array *op_array, uint32_t var)
483 {
484 if (op_array->last_live_range) {
485 int i = 0;
486 int j = 0;
487 uint32_t *map;
488 ALLOCA_FLAG(use_heap);
489
490 map = (uint32_t *)do_alloca(sizeof(uint32_t) * op_array->last_live_range, use_heap);
491
492 do {
493 if ((op_array->live_range[i].var & ~ZEND_LIVE_MASK) != var) {
494 map[i] = j;
495 if (i != j) {
496 op_array->live_range[j] = op_array->live_range[i];
497 }
498 j++;
499 }
500 i++;
501 } while (i < op_array->last_live_range);
502 if (i != j) {
503 if ((op_array->last_live_range = j)) {
504 zend_op *opline = op_array->opcodes;
505 zend_op *end = opline + op_array->last;
506
507 while (opline != end) {
508 if ((opline->opcode == ZEND_FREE || opline->opcode == ZEND_FE_FREE) &&
509 opline->extended_value == ZEND_FREE_ON_RETURN) {
510 opline->op2.num = map[opline->op2.num];
511 }
512 opline++;
513 }
514 } else {
515 efree(op_array->live_range);
516 op_array->live_range = NULL;
517 }
518 }
519 free_alloca(map, use_heap);
520 }
521 }
522
zend_optimizer_remove_live_range_ex(zend_op_array * op_array,uint32_t var,uint32_t start)523 void zend_optimizer_remove_live_range_ex(zend_op_array *op_array, uint32_t var, uint32_t start)
524 {
525 uint32_t i = 0;
526
527 switch (op_array->opcodes[start].opcode) {
528 case ZEND_ROPE_ADD:
529 case ZEND_ADD_ARRAY_ELEMENT:
530 return;
531 case ZEND_ROPE_INIT:
532 var |= ZEND_LIVE_ROPE;
533 break;
534 case ZEND_BEGIN_SILENCE:
535 var |= ZEND_LIVE_SILENCE;
536 break;
537 case ZEND_FE_RESET_R:
538 case ZEND_FE_RESET_RW:
539 var |= ZEND_LIVE_LOOP;
540 /* break missing intentionally */
541 default:
542 start++;
543 }
544
545 while (i < op_array->last_live_range) {
546 if (op_array->live_range[i].var == var
547 && op_array->live_range[i].start == start) {
548 op_array->last_live_range--;
549 if (i < op_array->last_live_range) {
550 memmove(&op_array->live_range[i], &op_array->live_range[i+1], (op_array->last_live_range - i) * sizeof(zend_live_range));
551 }
552 break;
553 }
554 i++;
555 }
556 }
557
zend_optimizer_replace_by_const(zend_op_array * op_array,zend_op * opline,zend_uchar type,uint32_t var,zval * val)558 int zend_optimizer_replace_by_const(zend_op_array *op_array,
559 zend_op *opline,
560 zend_uchar type,
561 uint32_t var,
562 zval *val)
563 {
564 zend_op *end = op_array->opcodes + op_array->last;
565
566 while (opline < end) {
567 if (opline->op1_type == type &&
568 opline->op1.var == var) {
569 switch (opline->opcode) {
570 case ZEND_FETCH_DIM_W:
571 case ZEND_FETCH_DIM_RW:
572 case ZEND_FETCH_DIM_FUNC_ARG:
573 case ZEND_FETCH_DIM_UNSET:
574 case ZEND_ASSIGN_DIM:
575 case ZEND_SEPARATE:
576 case ZEND_RETURN_BY_REF:
577 return 0;
578 case ZEND_SEND_VAR:
579 opline->extended_value = 0;
580 opline->opcode = ZEND_SEND_VAL;
581 break;
582 case ZEND_SEND_VAR_EX:
583 opline->extended_value = 0;
584 opline->opcode = ZEND_SEND_VAL_EX;
585 break;
586 case ZEND_SEND_VAR_NO_REF:
587 return 0;
588 case ZEND_SEND_VAR_NO_REF_EX:
589 opline->opcode = ZEND_SEND_VAL;
590 break;
591 case ZEND_SEND_USER:
592 opline->opcode = ZEND_SEND_VAL_EX;
593 break;
594 /* In most cases IS_TMP_VAR operand may be used only once.
595 * The operands are usually destroyed by the opcode handler.
596 * ZEND_CASE and ZEND_FETCH_LIST are exceptions, they keeps operand
597 * unchanged, and allows its reuse. these instructions
598 * usually terminated by ZEND_FREE that finally kills the value.
599 */
600 case ZEND_FETCH_LIST: {
601 zend_op *m = opline;
602
603 do {
604 if (m->opcode == ZEND_FETCH_LIST &&
605 m->op1_type == type &&
606 m->op1.var == var) {
607 zval v;
608 ZVAL_COPY_VALUE(&v, val);
609 zval_copy_ctor(&v);
610 if (Z_TYPE(v) == IS_STRING) {
611 zend_string_hash_val(Z_STR(v));
612 }
613 m->op1.constant = zend_optimizer_add_literal(op_array, &v);
614 m->op1_type = IS_CONST;
615 }
616 m++;
617 } while (m->opcode != ZEND_FREE || m->op1_type != type || m->op1.var != var);
618
619 ZEND_ASSERT(m->opcode == ZEND_FREE && m->op1_type == type && m->op1.var == var);
620 MAKE_NOP(m);
621 zval_ptr_dtor_nogc(val);
622 zend_optimizer_remove_live_range(op_array, var);
623 return 1;
624 }
625 case ZEND_SWITCH_LONG:
626 case ZEND_SWITCH_STRING:
627 case ZEND_CASE:
628 case ZEND_FREE: {
629 zend_op *m, *n;
630 int brk = op_array->last_live_range;
631 zend_bool in_switch = 0;
632 while (brk--) {
633 if (op_array->live_range[brk].start <= (uint32_t)(opline - op_array->opcodes) &&
634 op_array->live_range[brk].end > (uint32_t)(opline - op_array->opcodes)) {
635 in_switch = 1;
636 break;
637 }
638 }
639
640 if (!in_switch) {
641 ZEND_ASSERT(opline->opcode == ZEND_FREE);
642 MAKE_NOP(opline);
643 zval_ptr_dtor_nogc(val);
644 return 1;
645 }
646
647 m = opline;
648 n = op_array->opcodes + op_array->live_range[brk].end;
649 if (n->opcode == ZEND_FREE &&
650 !(n->extended_value & ZEND_FREE_ON_RETURN)) {
651 n++;
652 } else {
653 n = op_array->opcodes + op_array->last;
654 }
655
656 while (m < n) {
657 if (m->op1_type == type &&
658 m->op1.var == var) {
659 if (m->opcode == ZEND_CASE
660 || m->opcode == ZEND_SWITCH_LONG
661 || m->opcode == ZEND_SWITCH_STRING) {
662 zval v;
663 ZVAL_COPY_VALUE(&v, val);
664 zval_copy_ctor(&v);
665 if (Z_TYPE(v) == IS_STRING) {
666 zend_string_hash_val(Z_STR(v));
667 }
668 m->op1.constant = zend_optimizer_add_literal(op_array, &v);
669 m->op1_type = IS_CONST;
670 } else if (m->opcode == ZEND_FREE) {
671 MAKE_NOP(m);
672 } else {
673 ZEND_ASSERT(0);
674 }
675 }
676 m++;
677 }
678 zval_ptr_dtor_nogc(val);
679 zend_optimizer_remove_live_range(op_array, var);
680 return 1;
681 }
682 case ZEND_VERIFY_RETURN_TYPE: {
683 zend_arg_info *ret_info = op_array->arg_info - 1;
684 if (ZEND_TYPE_IS_CLASS(ret_info->type)
685 || ZEND_TYPE_CODE(ret_info->type) == IS_CALLABLE
686 || !ZEND_SAME_FAKE_TYPE(ZEND_TYPE_CODE(ret_info->type), Z_TYPE_P(val))
687 || (op_array->fn_flags & ZEND_ACC_RETURN_REFERENCE)) {
688 return 0;
689 }
690 MAKE_NOP(opline);
691
692 /* zend_handle_loops_and_finally may inserts other oplines */
693 do {
694 ++opline;
695 } while (opline->opcode != ZEND_RETURN && opline->opcode != ZEND_RETURN_BY_REF);
696 ZEND_ASSERT(opline->op1.var == var);
697
698 break;
699 }
700 default:
701 break;
702 }
703 if (zend_optimizer_update_op1_const(op_array, opline, val)) {
704 zend_optimizer_remove_live_range(op_array, var);
705 return 1;
706 }
707 return 0;
708 }
709
710 if (opline->op2_type == type &&
711 opline->op2.var == var) {
712 if (zend_optimizer_update_op2_const(op_array, opline, val)) {
713 zend_optimizer_remove_live_range(op_array, var);
714 return 1;
715 }
716 return 0;
717 }
718 opline++;
719 }
720
721 return 1;
722 }
723
724 /* 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)725 void zend_optimizer_migrate_jump(zend_op_array *op_array, zend_op *new_opline, zend_op *opline) {
726 switch (new_opline->opcode) {
727 case ZEND_JMP:
728 case ZEND_FAST_CALL:
729 ZEND_SET_OP_JMP_ADDR(new_opline, new_opline->op1, ZEND_OP1_JMP_ADDR(opline));
730 break;
731 case ZEND_JMPZNZ:
732 new_opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, new_opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value));
733 /* break missing intentionally */
734 case ZEND_JMPZ:
735 case ZEND_JMPNZ:
736 case ZEND_JMPZ_EX:
737 case ZEND_JMPNZ_EX:
738 case ZEND_FE_RESET_R:
739 case ZEND_FE_RESET_RW:
740 case ZEND_JMP_SET:
741 case ZEND_COALESCE:
742 case ZEND_ASSERT_CHECK:
743 ZEND_SET_OP_JMP_ADDR(new_opline, new_opline->op2, ZEND_OP2_JMP_ADDR(opline));
744 break;
745 case ZEND_CATCH:
746 if (!opline->result.num) {
747 new_opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, new_opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value));
748 }
749 break;
750 case ZEND_DECLARE_ANON_CLASS:
751 case ZEND_DECLARE_ANON_INHERITED_CLASS:
752 case ZEND_FE_FETCH_R:
753 case ZEND_FE_FETCH_RW:
754 new_opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, new_opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value));
755 break;
756 case ZEND_SWITCH_LONG:
757 case ZEND_SWITCH_STRING:
758 {
759 HashTable *jumptable = Z_ARRVAL(ZEND_OP2_LITERAL(opline));
760 zval *zv;
761 ZEND_HASH_FOREACH_VAL(jumptable, zv) {
762 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)));
763 } ZEND_HASH_FOREACH_END();
764 new_opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, new_opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value));
765 break;
766 }
767 }
768 }
769
770 /* Shift jump offsets based on shiftlist */
zend_optimizer_shift_jump(zend_op_array * op_array,zend_op * opline,uint32_t * shiftlist)771 void zend_optimizer_shift_jump(zend_op_array *op_array, zend_op *opline, uint32_t *shiftlist) {
772 switch (opline->opcode) {
773 case ZEND_JMP:
774 case ZEND_FAST_CALL:
775 ZEND_SET_OP_JMP_ADDR(opline, opline->op1, ZEND_OP1_JMP_ADDR(opline) - shiftlist[ZEND_OP1_JMP_ADDR(opline) - op_array->opcodes]);
776 break;
777 case ZEND_JMPZNZ:
778 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)]);
779 /* break missing intentionally */
780 case ZEND_JMPZ:
781 case ZEND_JMPNZ:
782 case ZEND_JMPZ_EX:
783 case ZEND_JMPNZ_EX:
784 case ZEND_FE_RESET_R:
785 case ZEND_FE_RESET_RW:
786 case ZEND_JMP_SET:
787 case ZEND_COALESCE:
788 case ZEND_ASSERT_CHECK:
789 ZEND_SET_OP_JMP_ADDR(opline, opline->op2, ZEND_OP2_JMP_ADDR(opline) - shiftlist[ZEND_OP2_JMP_ADDR(opline) - op_array->opcodes]);
790 break;
791 case ZEND_DECLARE_ANON_CLASS:
792 case ZEND_DECLARE_ANON_INHERITED_CLASS:
793 case ZEND_FE_FETCH_R:
794 case ZEND_FE_FETCH_RW:
795 case ZEND_CATCH:
796 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)]);
797 break;
798 case ZEND_SWITCH_LONG:
799 case ZEND_SWITCH_STRING:
800 {
801 HashTable *jumptable = Z_ARRVAL(ZEND_OP2_LITERAL(opline));
802 zval *zv;
803 ZEND_HASH_FOREACH_VAL(jumptable, zv) {
804 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))]);
805 } ZEND_HASH_FOREACH_END();
806 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)]);
807 break;
808 }
809 }
810 }
811
get_class_entry_from_op1(zend_script * script,zend_op_array * op_array,zend_op * opline,zend_bool rt_constants)812 static zend_class_entry *get_class_entry_from_op1(
813 zend_script *script, zend_op_array *op_array, zend_op *opline, zend_bool rt_constants) {
814 if (opline->op1_type == IS_CONST) {
815 zval *op1 = CRT_CONSTANT_EX(op_array, opline->op1, rt_constants);
816 if (Z_TYPE_P(op1) == IS_STRING) {
817 zend_string *class_name = Z_STR_P(op1 + 1);
818 zend_class_entry *ce;
819 if (script && (ce = zend_hash_find_ptr(&script->class_table, class_name))) {
820 return ce;
821 } else if ((ce = zend_hash_find_ptr(EG(class_table), class_name))) {
822 if (ce->type == ZEND_INTERNAL_CLASS) {
823 return ce;
824 } else if (ce->type == ZEND_USER_CLASS &&
825 ce->info.user.filename &&
826 ce->info.user.filename == op_array->filename) {
827 return ce;
828 }
829 }
830 }
831 } else if (opline->op1_type == IS_UNUSED && op_array->scope
832 && !(op_array->scope->ce_flags & ZEND_ACC_TRAIT)
833 && (opline->op1.num & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_SELF) {
834 return op_array->scope;
835 }
836 return NULL;
837 }
838
zend_optimizer_get_called_func(zend_script * script,zend_op_array * op_array,zend_op * opline,zend_bool rt_constants)839 zend_function *zend_optimizer_get_called_func(
840 zend_script *script, zend_op_array *op_array, zend_op *opline, zend_bool rt_constants)
841 {
842 #define GET_OP(op) CRT_CONSTANT_EX(op_array, opline->op, rt_constants)
843 switch (opline->opcode) {
844 case ZEND_INIT_FCALL:
845 {
846 zend_string *function_name = Z_STR_P(GET_OP(op2));
847 zend_function *func;
848 if (script && (func = zend_hash_find_ptr(&script->function_table, function_name)) != NULL) {
849 return func;
850 } else if ((func = zend_hash_find_ptr(EG(function_table), function_name)) != NULL) {
851 if (func->type == ZEND_INTERNAL_FUNCTION) {
852 return func;
853 } else if (func->type == ZEND_USER_FUNCTION &&
854 func->op_array.filename &&
855 func->op_array.filename == op_array->filename) {
856 return func;
857 }
858 }
859 break;
860 }
861 case ZEND_INIT_FCALL_BY_NAME:
862 case ZEND_INIT_NS_FCALL_BY_NAME:
863 if (opline->op2_type == IS_CONST && Z_TYPE_P(GET_OP(op2)) == IS_STRING) {
864 zval *function_name = GET_OP(op2) + 1;
865 zend_function *func;
866 if (script && (func = zend_hash_find_ptr(&script->function_table, Z_STR_P(function_name)))) {
867 return func;
868 } else if ((func = zend_hash_find_ptr(EG(function_table), Z_STR_P(function_name))) != NULL) {
869 if (func->type == ZEND_INTERNAL_FUNCTION) {
870 return func;
871 } else if (func->type == ZEND_USER_FUNCTION &&
872 func->op_array.filename &&
873 func->op_array.filename == op_array->filename) {
874 return func;
875 }
876 }
877 }
878 break;
879 case ZEND_INIT_STATIC_METHOD_CALL:
880 if (opline->op2_type == IS_CONST && Z_TYPE_P(GET_OP(op2)) == IS_STRING) {
881 zend_class_entry *ce = get_class_entry_from_op1(
882 script, op_array, opline, rt_constants);
883 if (ce) {
884 zend_string *func_name = Z_STR_P(GET_OP(op2) + 1);
885 return zend_hash_find_ptr(&ce->function_table, func_name);
886 }
887 }
888 break;
889 case ZEND_INIT_METHOD_CALL:
890 if (opline->op1_type == IS_UNUSED
891 && opline->op2_type == IS_CONST && Z_TYPE_P(GET_OP(op2)) == IS_STRING
892 && op_array->scope && !(op_array->scope->ce_flags & ZEND_ACC_TRAIT)) {
893 zend_string *method_name = Z_STR_P(GET_OP(op2) + 1);
894 zend_function *fbc = zend_hash_find_ptr(
895 &op_array->scope->function_table, method_name);
896 if (fbc) {
897 zend_bool is_private = (fbc->common.fn_flags & ZEND_ACC_PRIVATE) != 0;
898 zend_bool is_final = (fbc->common.fn_flags & ZEND_ACC_FINAL) != 0;
899 zend_bool same_scope = fbc->common.scope == op_array->scope;
900 if ((is_private && same_scope)
901 || (is_final && (!is_private || same_scope))) {
902 return fbc;
903 }
904 }
905 }
906 break;
907 case ZEND_NEW:
908 {
909 zend_class_entry *ce = get_class_entry_from_op1(
910 script, op_array, opline, rt_constants);
911 if (ce && ce->type == ZEND_USER_CLASS) {
912 return ce->constructor;
913 }
914 break;
915 }
916 }
917 return NULL;
918 #undef GET_OP
919 }
920
zend_optimizer_classify_function(zend_string * name,uint32_t num_args)921 uint32_t zend_optimizer_classify_function(zend_string *name, uint32_t num_args) {
922 if (zend_string_equals_literal(name, "extract")) {
923 return ZEND_FUNC_INDIRECT_VAR_ACCESS;
924 } else if (zend_string_equals_literal(name, "compact")) {
925 return ZEND_FUNC_INDIRECT_VAR_ACCESS;
926 } else if (zend_string_equals_literal(name, "parse_str") && num_args <= 1) {
927 return ZEND_FUNC_INDIRECT_VAR_ACCESS;
928 } else if (zend_string_equals_literal(name, "mb_parse_str") && num_args <= 1) {
929 return ZEND_FUNC_INDIRECT_VAR_ACCESS;
930 } else if (zend_string_equals_literal(name, "get_defined_vars")) {
931 return ZEND_FUNC_INDIRECT_VAR_ACCESS;
932 } else if (zend_string_equals_literal(name, "assert")) {
933 return ZEND_FUNC_INDIRECT_VAR_ACCESS;
934 } else if (zend_string_equals_literal(name, "func_num_args")) {
935 return ZEND_FUNC_VARARG;
936 } else if (zend_string_equals_literal(name, "func_get_arg")) {
937 return ZEND_FUNC_VARARG;
938 } else if (zend_string_equals_literal(name, "func_get_args")) {
939 return ZEND_FUNC_VARARG;
940 } else {
941 return 0;
942 }
943 }
944
zend_optimize(zend_op_array * op_array,zend_optimizer_ctx * ctx)945 static void zend_optimize(zend_op_array *op_array,
946 zend_optimizer_ctx *ctx)
947 {
948 if (op_array->type == ZEND_EVAL_CODE) {
949 return;
950 }
951
952 if (ctx->debug_level & ZEND_DUMP_BEFORE_OPTIMIZER) {
953 zend_dump_op_array(op_array, 0, "before optimizer", NULL);
954 }
955
956 /* pass 1
957 * - substitute persistent constants (true, false, null, etc)
958 * - perform compile-time evaluation of constant binary and unary operations
959 * - optimize series of ADD_STRING and/or ADD_CHAR
960 * - convert CAST(IS_BOOL,x) into BOOL(x)
961 * - pre-evaluate constant function calls
962 */
963 if (ZEND_OPTIMIZER_PASS_1 & ctx->optimization_level) {
964 zend_optimizer_pass1(op_array, ctx);
965 if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_1) {
966 zend_dump_op_array(op_array, 0, "after pass 1", NULL);
967 }
968 }
969
970 /* pass 2:
971 * - convert non-numeric constants to numeric constants in numeric operators
972 * - optimize constant conditional JMPs
973 */
974 if (ZEND_OPTIMIZER_PASS_2 & ctx->optimization_level) {
975 zend_optimizer_pass2(op_array);
976 if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_2) {
977 zend_dump_op_array(op_array, 0, "after pass 2", NULL);
978 }
979 }
980
981 /* pass 3:
982 * - optimize $i = $i+expr to $i+=expr
983 * - optimize series of JMPs
984 * - change $i++ to ++$i where possible
985 */
986 if (ZEND_OPTIMIZER_PASS_3 & ctx->optimization_level) {
987 zend_optimizer_pass3(op_array);
988 if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_3) {
989 zend_dump_op_array(op_array, 0, "after pass 3", NULL);
990 }
991 }
992
993 /* pass 4:
994 * - INIT_FCALL_BY_NAME -> DO_FCALL
995 */
996 if (ZEND_OPTIMIZER_PASS_4 & ctx->optimization_level) {
997 zend_optimize_func_calls(op_array, ctx);
998 if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_4) {
999 zend_dump_op_array(op_array, 0, "after pass 4", NULL);
1000 }
1001 }
1002
1003 /* pass 5:
1004 * - CFG optimization
1005 */
1006 if (ZEND_OPTIMIZER_PASS_5 & ctx->optimization_level) {
1007 zend_optimize_cfg(op_array, ctx);
1008 if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_5) {
1009 zend_dump_op_array(op_array, 0, "after pass 5", NULL);
1010 }
1011 }
1012
1013 #if HAVE_DFA_PASS
1014 /* pass 6:
1015 * - DFA optimization
1016 */
1017 if ((ZEND_OPTIMIZER_PASS_6 & ctx->optimization_level) &&
1018 !(ZEND_OPTIMIZER_PASS_7 & ctx->optimization_level)) {
1019 zend_optimize_dfa(op_array, ctx);
1020 if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_6) {
1021 zend_dump_op_array(op_array, 0, "after pass 6", NULL);
1022 }
1023 }
1024 #endif
1025
1026 /* pass 9:
1027 * - Optimize temp variables usage
1028 */
1029 if (ZEND_OPTIMIZER_PASS_9 & ctx->optimization_level) {
1030 zend_optimize_temporary_variables(op_array, ctx);
1031 if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_9) {
1032 zend_dump_op_array(op_array, 0, "after pass 9", NULL);
1033 }
1034 }
1035
1036 /* pass 10:
1037 * - remove NOPs
1038 */
1039 if (((ZEND_OPTIMIZER_PASS_10|ZEND_OPTIMIZER_PASS_5) & ctx->optimization_level) == ZEND_OPTIMIZER_PASS_10) {
1040 zend_optimizer_nop_removal(op_array);
1041 if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_10) {
1042 zend_dump_op_array(op_array, 0, "after pass 10", NULL);
1043 }
1044 }
1045
1046 /* pass 11:
1047 * - Compact literals table
1048 */
1049 if ((ZEND_OPTIMIZER_PASS_11 & ctx->optimization_level) &&
1050 (!(ZEND_OPTIMIZER_PASS_6 & ctx->optimization_level) ||
1051 !(ZEND_OPTIMIZER_PASS_7 & ctx->optimization_level))) {
1052 zend_optimizer_compact_literals(op_array, ctx);
1053 if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_11) {
1054 zend_dump_op_array(op_array, 0, "after pass 11", NULL);
1055 }
1056 }
1057
1058 if ((ZEND_OPTIMIZER_PASS_13 & ctx->optimization_level) &&
1059 (!(ZEND_OPTIMIZER_PASS_6 & ctx->optimization_level) ||
1060 !(ZEND_OPTIMIZER_PASS_7 & ctx->optimization_level))) {
1061 zend_optimizer_compact_vars(op_array);
1062 if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_13) {
1063 zend_dump_op_array(op_array, 0, "after pass 13", NULL);
1064 }
1065 }
1066
1067 if (ZEND_OPTIMIZER_PASS_7 & ctx->optimization_level) {
1068 return;
1069 }
1070
1071 if (ctx->debug_level & ZEND_DUMP_AFTER_OPTIMIZER) {
1072 zend_dump_op_array(op_array, 0, "after optimizer", NULL);
1073 }
1074 }
1075
zend_revert_pass_two(zend_op_array * op_array)1076 static void zend_revert_pass_two(zend_op_array *op_array)
1077 {
1078 zend_op *opline, *end;
1079
1080 opline = op_array->opcodes;
1081 end = opline + op_array->last;
1082 while (opline < end) {
1083 if (opline->op1_type == IS_CONST) {
1084 ZEND_PASS_TWO_UNDO_CONSTANT(op_array, opline->op1);
1085 }
1086 if (opline->op2_type == IS_CONST) {
1087 ZEND_PASS_TWO_UNDO_CONSTANT(op_array, opline->op2);
1088 }
1089 opline++;
1090 }
1091 }
1092
zend_redo_pass_two(zend_op_array * op_array)1093 static void zend_redo_pass_two(zend_op_array *op_array)
1094 {
1095 zend_op *opline, *end;
1096
1097 opline = op_array->opcodes;
1098 end = opline + op_array->last;
1099 while (opline < end) {
1100 if (opline->op1_type == IS_CONST) {
1101 ZEND_PASS_TWO_UPDATE_CONSTANT(op_array, opline->op1);
1102 }
1103 if (opline->op2_type == IS_CONST) {
1104 ZEND_PASS_TWO_UPDATE_CONSTANT(op_array, opline->op2);
1105 }
1106 ZEND_VM_SET_OPCODE_HANDLER(opline);
1107 opline++;
1108 }
1109 }
1110
1111 #if HAVE_DFA_PASS
zend_redo_pass_two_ex(zend_op_array * op_array,zend_ssa * ssa)1112 static void zend_redo_pass_two_ex(zend_op_array *op_array, zend_ssa *ssa)
1113 {
1114 zend_op *opline, *end;
1115
1116 opline = op_array->opcodes;
1117 end = opline + op_array->last;
1118 while (opline < end) {
1119 zend_vm_set_opcode_handler_ex(opline,
1120 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)),
1121 opline->op2_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)),
1122 (opline->opcode == ZEND_PRE_INC ||
1123 opline->opcode == ZEND_PRE_DEC ||
1124 opline->opcode == ZEND_POST_INC ||
1125 opline->opcode == ZEND_POST_DEC) ?
1126 ((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) :
1127 (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))));
1128 if (opline->op1_type == IS_CONST) {
1129 ZEND_PASS_TWO_UPDATE_CONSTANT(op_array, opline->op1);
1130 }
1131 if (opline->op2_type == IS_CONST) {
1132 ZEND_PASS_TWO_UPDATE_CONSTANT(op_array, opline->op2);
1133 }
1134 opline++;
1135 }
1136 }
1137 #endif
1138
zend_optimize_op_array(zend_op_array * op_array,zend_optimizer_ctx * ctx)1139 static void zend_optimize_op_array(zend_op_array *op_array,
1140 zend_optimizer_ctx *ctx)
1141 {
1142 /* Revert pass_two() */
1143 zend_revert_pass_two(op_array);
1144
1145 /* Do actual optimizations */
1146 zend_optimize(op_array, ctx);
1147
1148 /* Redo pass_two() */
1149 zend_redo_pass_two(op_array);
1150 }
1151
zend_adjust_fcall_stack_size(zend_op_array * op_array,zend_optimizer_ctx * ctx)1152 static void zend_adjust_fcall_stack_size(zend_op_array *op_array, zend_optimizer_ctx *ctx)
1153 {
1154 zend_function *func;
1155 zend_op *opline, *end;
1156
1157 opline = op_array->opcodes;
1158 end = opline + op_array->last;
1159 while (opline < end) {
1160 if (opline->opcode == ZEND_INIT_FCALL) {
1161 func = zend_hash_find_ptr(
1162 &ctx->script->function_table,
1163 Z_STR_P(RT_CONSTANT(op_array, opline->op2)));
1164 if (func) {
1165 opline->op1.num = zend_vm_calc_used_stack(opline->extended_value, func);
1166 }
1167 }
1168 opline++;
1169 }
1170 }
1171
1172 #if HAVE_DFA_PASS
zend_adjust_fcall_stack_size_graph(zend_op_array * op_array)1173 static void zend_adjust_fcall_stack_size_graph(zend_op_array *op_array)
1174 {
1175 zend_func_info *func_info = ZEND_FUNC_INFO(op_array);
1176
1177 if (func_info) {
1178 zend_call_info *call_info =func_info->callee_info;
1179
1180 while (call_info) {
1181 zend_op *opline = call_info->caller_init_opline;
1182
1183 if (opline && call_info->callee_func && opline->opcode == ZEND_INIT_FCALL) {
1184 opline->op1.num = zend_vm_calc_used_stack(opline->extended_value, call_info->callee_func);
1185 }
1186 call_info = call_info->next_callee;
1187 }
1188 }
1189 }
1190 #endif
1191
zend_optimize_script(zend_script * script,zend_long optimization_level,zend_long debug_level)1192 int zend_optimize_script(zend_script *script, zend_long optimization_level, zend_long debug_level)
1193 {
1194 zend_class_entry *ce;
1195 zend_op_array *op_array;
1196 zend_string *name;
1197 zend_optimizer_ctx ctx;
1198 #if HAVE_DFA_PASS
1199 zend_call_graph call_graph;
1200 #endif
1201
1202 ctx.arena = zend_arena_create(64 * 1024);
1203 ctx.script = script;
1204 ctx.constants = NULL;
1205 ctx.optimization_level = optimization_level;
1206 ctx.debug_level = debug_level;
1207
1208 zend_optimize_op_array(&script->main_op_array, &ctx);
1209
1210 ZEND_HASH_FOREACH_PTR(&script->function_table, op_array) {
1211 zend_optimize_op_array(op_array, &ctx);
1212 } ZEND_HASH_FOREACH_END();
1213
1214 ZEND_HASH_FOREACH_PTR(&script->class_table, ce) {
1215 ZEND_HASH_FOREACH_STR_KEY_PTR(&ce->function_table, name, op_array) {
1216 if (op_array->scope == ce) {
1217 zend_optimize_op_array(op_array, &ctx);
1218 } else if (op_array->type == ZEND_USER_FUNCTION) {
1219 zend_op_array *orig_op_array;
1220 if ((orig_op_array = zend_hash_find_ptr(&op_array->scope->function_table, name)) != NULL) {
1221 HashTable *ht = op_array->static_variables;
1222 *op_array = *orig_op_array;
1223 op_array->static_variables = ht;
1224 }
1225 }
1226 } ZEND_HASH_FOREACH_END();
1227 } ZEND_HASH_FOREACH_END();
1228
1229 #if HAVE_DFA_PASS
1230 if ((ZEND_OPTIMIZER_PASS_6 & optimization_level) &&
1231 (ZEND_OPTIMIZER_PASS_7 & optimization_level) &&
1232 zend_build_call_graph(&ctx.arena, script, ZEND_RT_CONSTANTS, &call_graph) == SUCCESS) {
1233 /* Optimize using call-graph */
1234 void *checkpoint = zend_arena_checkpoint(ctx.arena);
1235 int i;
1236 zend_func_info *func_info;
1237
1238 for (i = 0; i < call_graph.op_arrays_count; i++) {
1239 zend_revert_pass_two(call_graph.op_arrays[i]);
1240 }
1241
1242 for (i = 0; i < call_graph.op_arrays_count; i++) {
1243 func_info = ZEND_FUNC_INFO(call_graph.op_arrays[i]);
1244 if (func_info) {
1245 func_info->call_map = zend_build_call_map(&ctx.arena, func_info, call_graph.op_arrays[i]);
1246 if (call_graph.op_arrays[i]->fn_flags & ZEND_ACC_HAS_RETURN_TYPE) {
1247 zend_init_func_return_info(call_graph.op_arrays[i], script, &func_info->return_info);
1248 }
1249 }
1250 }
1251
1252 for (i = 0; i < call_graph.op_arrays_count; i++) {
1253 func_info = ZEND_FUNC_INFO(call_graph.op_arrays[i]);
1254 if (func_info) {
1255 zend_dfa_analyze_op_array(call_graph.op_arrays[i], &ctx, &func_info->ssa, &func_info->flags);
1256 }
1257 }
1258
1259 //TODO: perform inner-script inference???
1260 for (i = 0; i < call_graph.op_arrays_count; i++) {
1261 func_info = ZEND_FUNC_INFO(call_graph.op_arrays[i]);
1262 if (func_info) {
1263 zend_dfa_optimize_op_array(call_graph.op_arrays[i], &ctx, &func_info->ssa, func_info->call_map);
1264 }
1265 }
1266
1267 if (debug_level & ZEND_DUMP_AFTER_PASS_7) {
1268 for (i = 0; i < call_graph.op_arrays_count; i++) {
1269 zend_dump_op_array(call_graph.op_arrays[i], 0, "after pass 7", NULL);
1270 }
1271 }
1272
1273 if (ZEND_OPTIMIZER_PASS_11 & optimization_level) {
1274 for (i = 0; i < call_graph.op_arrays_count; i++) {
1275 zend_optimizer_compact_literals(call_graph.op_arrays[i], &ctx);
1276 if (debug_level & ZEND_DUMP_AFTER_PASS_11) {
1277 zend_dump_op_array(call_graph.op_arrays[i], 0, "after pass 11", NULL);
1278 }
1279 }
1280 }
1281
1282 if (ZEND_OPTIMIZER_PASS_13 & optimization_level) {
1283 for (i = 0; i < call_graph.op_arrays_count; i++) {
1284 zend_optimizer_compact_vars(call_graph.op_arrays[i]);
1285 if (debug_level & ZEND_DUMP_AFTER_PASS_13) {
1286 zend_dump_op_array(call_graph.op_arrays[i], 0, "after pass 13", NULL);
1287 }
1288 }
1289 }
1290
1291 if (ZEND_OPTIMIZER_PASS_12 & optimization_level) {
1292 for (i = 0; i < call_graph.op_arrays_count; i++) {
1293 zend_adjust_fcall_stack_size_graph(call_graph.op_arrays[i]);
1294 }
1295 }
1296
1297 for (i = 0; i < call_graph.op_arrays_count; i++) {
1298 func_info = ZEND_FUNC_INFO(call_graph.op_arrays[i]);
1299 if (func_info && func_info->ssa.var_info) {
1300 zend_redo_pass_two_ex(call_graph.op_arrays[i], &func_info->ssa);
1301 } else {
1302 zend_redo_pass_two(call_graph.op_arrays[i]);
1303 }
1304 }
1305
1306 for (i = 0; i < call_graph.op_arrays_count; i++) {
1307 ZEND_SET_FUNC_INFO(call_graph.op_arrays[i], NULL);
1308 }
1309
1310 ZEND_HASH_FOREACH_PTR(&script->class_table, ce) {
1311 ZEND_HASH_FOREACH_STR_KEY_PTR(&ce->function_table, name, op_array) {
1312 if (op_array->scope != ce) {
1313 zend_op_array *orig_op_array;
1314 if ((orig_op_array = zend_hash_find_ptr(&op_array->scope->function_table, name)) != NULL) {
1315 HashTable *ht = op_array->static_variables;
1316 *op_array = *orig_op_array;
1317 op_array->static_variables = ht;
1318 }
1319 }
1320 } ZEND_HASH_FOREACH_END();
1321 } ZEND_HASH_FOREACH_END();
1322
1323 zend_arena_release(&ctx.arena, checkpoint);
1324 } else
1325 #endif
1326
1327 if (ZEND_OPTIMIZER_PASS_12 & optimization_level) {
1328 zend_adjust_fcall_stack_size(&script->main_op_array, &ctx);
1329
1330 ZEND_HASH_FOREACH_PTR(&script->function_table, op_array) {
1331 zend_adjust_fcall_stack_size(op_array, &ctx);
1332 } ZEND_HASH_FOREACH_END();
1333
1334 ZEND_HASH_FOREACH_PTR(&script->class_table, ce) {
1335 ZEND_HASH_FOREACH_STR_KEY_PTR(&ce->function_table, name, op_array) {
1336 if (op_array->scope == ce) {
1337 zend_adjust_fcall_stack_size(op_array, &ctx);
1338 } else if (op_array->type == ZEND_USER_FUNCTION) {
1339 zend_op_array *orig_op_array;
1340 if ((orig_op_array = zend_hash_find_ptr(&op_array->scope->function_table, name)) != NULL) {
1341 HashTable *ht = op_array->static_variables;
1342 *op_array = *orig_op_array;
1343 op_array->static_variables = ht;
1344 }
1345 }
1346 } ZEND_HASH_FOREACH_END();
1347 } ZEND_HASH_FOREACH_END();
1348 }
1349
1350 if ((debug_level & ZEND_DUMP_AFTER_OPTIMIZER) &&
1351 (ZEND_OPTIMIZER_PASS_7 & optimization_level)) {
1352 zend_dump_op_array(&script->main_op_array, ZEND_DUMP_RT_CONSTANTS, "after optimizer", NULL);
1353
1354 ZEND_HASH_FOREACH_PTR(&script->function_table, op_array) {
1355 zend_dump_op_array(op_array, ZEND_DUMP_RT_CONSTANTS, "after optimizer", NULL);
1356 } ZEND_HASH_FOREACH_END();
1357
1358 ZEND_HASH_FOREACH_PTR(&script->class_table, ce) {
1359 ZEND_HASH_FOREACH_STR_KEY_PTR(&ce->function_table, name, op_array) {
1360 if (op_array->scope == ce) {
1361 zend_dump_op_array(op_array, ZEND_DUMP_RT_CONSTANTS, "after optimizer", NULL);
1362 }
1363 } ZEND_HASH_FOREACH_END();
1364 } ZEND_HASH_FOREACH_END();
1365 }
1366
1367 if (ctx.constants) {
1368 zend_hash_destroy(ctx.constants);
1369 }
1370 zend_arena_destroy(ctx.arena);
1371
1372 return 1;
1373 }
1374
zend_optimizer_startup(void)1375 int zend_optimizer_startup(void)
1376 {
1377 return zend_func_info_startup();
1378 }
1379
zend_optimizer_shutdown(void)1380 int zend_optimizer_shutdown(void)
1381 {
1382 return zend_func_info_shutdown();
1383 }
1384
1385 /*
1386 * Local variables:
1387 * tab-width: 4
1388 * c-basic-offset: 4
1389 * indent-tabs-mode: t
1390 * End:
1391 */
1392