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