1 /*
2 +----------------------------------------------------------------------+
3 | Zend OPcache |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 1998-2017 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_bitset.h"
30
31 #define DEBUG_BLOCKPASS 0
32
33 /* Checks if a constant (like "true") may be replaced by its value */
zend_optimizer_get_persistent_constant(zend_string * name,zval * result,int copy)34 int zend_optimizer_get_persistent_constant(zend_string *name, zval *result, int copy)
35 {
36 zend_constant *c;
37 char *lookup_name;
38 int retval = 1;
39 ALLOCA_FLAG(use_heap);
40
41 if ((c = zend_hash_find_ptr(EG(zend_constants), name)) == NULL) {
42 lookup_name = DO_ALLOCA(ZSTR_LEN(name) + 1);
43 memcpy(lookup_name, ZSTR_VAL(name), ZSTR_LEN(name) + 1);
44 zend_str_tolower(lookup_name, ZSTR_LEN(name));
45
46 if ((c = zend_hash_str_find_ptr(EG(zend_constants), lookup_name, ZSTR_LEN(name))) != NULL) {
47 if (!(c->flags & CONST_CT_SUBST) || (c->flags & CONST_CS)) {
48 retval = 0;
49 }
50 } else {
51 retval = 0;
52 }
53 FREE_ALLOCA(lookup_name);
54 }
55
56 if (retval) {
57 if (c->flags & CONST_PERSISTENT) {
58 ZVAL_COPY_VALUE(result, &c->value);
59 if (copy) {
60 zval_copy_ctor(result);
61 }
62 } else {
63 retval = 0;
64 }
65 }
66
67 return retval;
68 }
69
70 #if DEBUG_BLOCKPASS
71 # define BLOCK_REF(b) b?op_array->opcodes-b->start_opline:-1
72
print_block(zend_code_block * block,zend_op * opcodes,char * txt)73 static inline void print_block(zend_code_block *block, zend_op *opcodes, char *txt)
74 {
75 fprintf(stderr, "%sBlock: %d-%d (%d)", txt, block->start_opline - opcodes, block->start_opline - opcodes + block->len - 1, block->len);
76 if (!block->access) {
77 fprintf(stderr, " unused");
78 }
79 if (block->op1_to) {
80 fprintf(stderr, " 1: %d", block->op1_to->start_opline - opcodes);
81 }
82 if (block->op2_to) {
83 fprintf(stderr, " 2: %d", block->op2_to->start_opline - opcodes);
84 }
85 if (block->ext_to) {
86 fprintf(stderr, " e: %d", block->ext_to->start_opline - opcodes);
87 }
88 if (block->follow_to) {
89 fprintf(stderr, " f: %d", block->follow_to->start_opline - opcodes);
90 }
91
92 if (block->sources) {
93 zend_block_source *bs = block->sources;
94 fprintf(stderr, " s:");
95 while (bs) {
96 fprintf(stderr, " %d", bs->from->start_opline - opcodes);
97 bs = bs->next;
98 }
99 }
100
101 fprintf(stderr, "\n");
102 fflush(stderr);
103 }
104 #else
105 #define print_block(a,b,c)
106 #endif
107
108 #define START_BLOCK_OP(opno) blocks[opno].start_opline = &op_array->opcodes[opno]; blocks[opno].start_opline_no = opno; blocks[opno].access = 1
109
110 /* find code blocks in op_array
111 code block is a set of opcodes with single flow of control, i.e. without jmps,
112 branches, etc. */
find_code_blocks(zend_op_array * op_array,zend_cfg * cfg,zend_optimizer_ctx * ctx)113 static int find_code_blocks(zend_op_array *op_array, zend_cfg *cfg, zend_optimizer_ctx *ctx)
114 {
115 zend_op *opline;
116 zend_op *end = op_array->opcodes + op_array->last;
117 zend_code_block *blocks, *cur_block;
118 uint32_t opno = 0;
119
120 memset(cfg, 0, sizeof(zend_cfg));
121 blocks = cfg->blocks = zend_arena_calloc(&ctx->arena, op_array->last + 2, sizeof(zend_code_block));
122 opline = op_array->opcodes;
123 blocks[0].start_opline = opline;
124 blocks[0].start_opline_no = 0;
125 while (opline < end) {
126 switch((unsigned)opline->opcode) {
127 case ZEND_FAST_CALL:
128 START_BLOCK_OP(ZEND_OP1(opline).opline_num);
129 if (opline->extended_value) {
130 START_BLOCK_OP(ZEND_OP2(opline).opline_num);
131 }
132 START_BLOCK_OP(opno + 1);
133 break;
134 case ZEND_FAST_RET:
135 if (opline->extended_value) {
136 START_BLOCK_OP(ZEND_OP2(opline).opline_num);
137 }
138 START_BLOCK_OP(opno + 1);
139 break;
140 case ZEND_JMP:
141 case ZEND_DECLARE_ANON_CLASS:
142 case ZEND_DECLARE_ANON_INHERITED_CLASS:
143 START_BLOCK_OP(ZEND_OP1(opline).opline_num);
144 /* break missing intentionally */
145 case ZEND_RETURN:
146 case ZEND_RETURN_BY_REF:
147 case ZEND_GENERATOR_RETURN:
148 case ZEND_EXIT:
149 case ZEND_THROW:
150 /* start new block from this+1 */
151 START_BLOCK_OP(opno + 1);
152 break;
153 /* TODO: if conditional jmp depends on constant,
154 don't start block that won't be executed */
155 case ZEND_CATCH:
156 START_BLOCK_OP(opline->extended_value);
157 START_BLOCK_OP(opno + 1);
158 break;
159 case ZEND_JMPZNZ:
160 START_BLOCK_OP(opline->extended_value);
161 case ZEND_JMPZ:
162 case ZEND_JMPNZ:
163 case ZEND_JMPZ_EX:
164 case ZEND_JMPNZ_EX:
165 case ZEND_FE_RESET_R:
166 case ZEND_FE_RESET_RW:
167 case ZEND_NEW:
168 case ZEND_JMP_SET:
169 case ZEND_COALESCE:
170 case ZEND_ASSERT_CHECK:
171 START_BLOCK_OP(ZEND_OP2(opline).opline_num);
172 START_BLOCK_OP(opno + 1);
173 break;
174 case ZEND_FE_FETCH_R:
175 case ZEND_FE_FETCH_RW:
176 START_BLOCK_OP(opline->extended_value);
177 START_BLOCK_OP(opno + 1);
178 break;
179 }
180 opno++;
181 opline++;
182 }
183
184 /* first find block start points */
185 if (op_array->last_try_catch) {
186 int i;
187 cfg->try = zend_arena_calloc(&ctx->arena, op_array->last_try_catch, sizeof(zend_code_block *));
188 cfg->catch = zend_arena_calloc(&ctx->arena, op_array->last_try_catch, sizeof(zend_code_block *));
189 for (i = 0; i< op_array->last_try_catch; i++) {
190 cfg->try[i] = &blocks[op_array->try_catch_array[i].try_op];
191 cfg->catch[i] = &blocks[op_array->try_catch_array[i].catch_op];
192 START_BLOCK_OP(op_array->try_catch_array[i].try_op);
193 START_BLOCK_OP(op_array->try_catch_array[i].catch_op);
194 blocks[op_array->try_catch_array[i].try_op].protected = 1;
195 }
196 }
197 /* Currently, we don't optimize op_arrays with BRK/CONT/GOTO opcodes,
198 * but, we have to keep brk_cont_array to avoid memory leaks during
199 * exception handling */
200 if (op_array->last_brk_cont) {
201 int i, j;
202
203 j = 0;
204 for (i = 0; i< op_array->last_brk_cont; i++) {
205 if (op_array->brk_cont_array[i].start >= 0 &&
206 (op_array->opcodes[op_array->brk_cont_array[i].brk].opcode == ZEND_FREE ||
207 op_array->opcodes[op_array->brk_cont_array[i].brk].opcode == ZEND_FE_FREE ||
208 op_array->opcodes[op_array->brk_cont_array[i].brk].opcode == ZEND_ROPE_END ||
209 op_array->opcodes[op_array->brk_cont_array[i].brk].opcode == ZEND_END_SILENCE)) {
210 int parent = op_array->brk_cont_array[i].parent;
211
212 while (parent >= 0 &&
213 op_array->brk_cont_array[parent].start < 0 &&
214 (op_array->opcodes[op_array->brk_cont_array[parent].brk].opcode != ZEND_FREE ||
215 op_array->opcodes[op_array->brk_cont_array[parent].brk].opcode != ZEND_FE_FREE ||
216 op_array->opcodes[op_array->brk_cont_array[i].brk].opcode != ZEND_ROPE_END ||
217 op_array->opcodes[op_array->brk_cont_array[parent].brk].opcode != ZEND_END_SILENCE)) {
218 parent = op_array->brk_cont_array[parent].parent;
219 }
220 op_array->brk_cont_array[i].parent = parent;
221 j++;
222 }
223 }
224 if (j) {
225 cfg->loop_start = zend_arena_calloc(&ctx->arena, op_array->last_brk_cont, sizeof(zend_code_block *));
226 cfg->loop_cont = zend_arena_calloc(&ctx->arena, op_array->last_brk_cont, sizeof(zend_code_block *));
227 cfg->loop_brk = zend_arena_calloc(&ctx->arena, op_array->last_brk_cont, sizeof(zend_code_block *));
228 j = 0;
229 for (i = 0; i< op_array->last_brk_cont; i++) {
230 if (op_array->brk_cont_array[i].start >= 0 &&
231 (op_array->opcodes[op_array->brk_cont_array[i].brk].opcode == ZEND_FREE ||
232 op_array->opcodes[op_array->brk_cont_array[i].brk].opcode == ZEND_FE_FREE ||
233 op_array->opcodes[op_array->brk_cont_array[i].brk].opcode == ZEND_ROPE_END ||
234 op_array->opcodes[op_array->brk_cont_array[i].brk].opcode == ZEND_END_SILENCE)) {
235 if (i != j) {
236 op_array->brk_cont_array[j] = op_array->brk_cont_array[i];
237 }
238 cfg->loop_start[j] = &blocks[op_array->brk_cont_array[j].start];
239 cfg->loop_cont[j] = &blocks[op_array->brk_cont_array[j].cont];
240 cfg->loop_brk[j] = &blocks[op_array->brk_cont_array[j].brk];
241 START_BLOCK_OP(op_array->brk_cont_array[j].start);
242 START_BLOCK_OP(op_array->brk_cont_array[j].cont);
243 START_BLOCK_OP(op_array->brk_cont_array[j].brk);
244 blocks[op_array->brk_cont_array[j].start].protected = 1;
245 blocks[op_array->brk_cont_array[j].brk].protected = 1;
246 j++;
247 }
248 }
249 op_array->last_brk_cont = j;
250 } else {
251 efree(op_array->brk_cont_array);
252 op_array->brk_cont_array = NULL;
253 op_array->last_brk_cont = 0;
254 }
255 }
256
257 /* Build CFG (Control Flow Graph) */
258 cur_block = blocks;
259 for (opno = 1; opno < op_array->last; opno++) {
260 if (blocks[opno].start_opline) {
261 /* found new block start */
262 cur_block->len = blocks[opno].start_opline - cur_block->start_opline;
263 cur_block->next = &blocks[opno];
264 /* what is the last OP of previous block? */
265 opline = blocks[opno].start_opline - 1;
266 if (opline->opcode == ZEND_OP_DATA) {
267 opline--;
268 }
269 switch((unsigned)opline->opcode) {
270 case ZEND_RETURN:
271 case ZEND_RETURN_BY_REF:
272 case ZEND_GENERATOR_RETURN:
273 case ZEND_EXIT:
274 case ZEND_THROW:
275 break;
276 case ZEND_FAST_CALL:
277 if (opline->extended_value) {
278 cur_block->op2_to = &blocks[ZEND_OP2(opline).opline_num];
279 }
280 cur_block->op1_to = &blocks[ZEND_OP1(opline).opline_num];
281 break;
282 case ZEND_FAST_RET:
283 if (opline->extended_value) {
284 cur_block->op2_to = &blocks[ZEND_OP2(opline).opline_num];
285 }
286 break;
287 case ZEND_JMP:
288 cur_block->op1_to = &blocks[ZEND_OP1(opline).opline_num];
289 break;
290 case ZEND_DECLARE_ANON_CLASS:
291 case ZEND_DECLARE_ANON_INHERITED_CLASS:
292 cur_block->op1_to = &blocks[ZEND_OP1(opline).opline_num];
293 cur_block->follow_to = &blocks[opno];
294 break;
295 case ZEND_JMPZNZ:
296 cur_block->op2_to = &blocks[ZEND_OP2(opline).opline_num];
297 cur_block->ext_to = &blocks[opline->extended_value];
298 break;
299 case ZEND_CATCH:
300 cur_block->ext_to = &blocks[opline->extended_value];
301 cur_block->follow_to = &blocks[opno];
302 break;
303 case ZEND_FE_FETCH_R:
304 case ZEND_FE_FETCH_RW:
305 cur_block->ext_to = &blocks[opline->extended_value];
306 cur_block->follow_to = &blocks[opno];
307 break;
308 case ZEND_JMPZ:
309 case ZEND_JMPNZ:
310 case ZEND_JMPZ_EX:
311 case ZEND_JMPNZ_EX:
312 case ZEND_FE_RESET_R:
313 case ZEND_FE_RESET_RW:
314 case ZEND_NEW:
315 case ZEND_JMP_SET:
316 case ZEND_COALESCE:
317 case ZEND_ASSERT_CHECK:
318 cur_block->op2_to = &blocks[ZEND_OP2(opline).opline_num];
319 /* break missing intentionally */
320 default:
321 /* next block follows this */
322 cur_block->follow_to = &blocks[opno];
323 break;
324 }
325 print_block(cur_block, op_array->opcodes, "");
326 cur_block = cur_block->next;
327 }
328 }
329 cur_block->len = end - cur_block->start_opline;
330 cur_block->next = &blocks[op_array->last + 1];
331 print_block(cur_block, op_array->opcodes, "");
332
333 return 1;
334 }
335
336 /* CFG back references management */
337
338 #define ADD_SOURCE(fromb, tob) { \
339 zend_block_source *__s = tob->sources; \
340 while (__s && __s->from != fromb) __s = __s->next; \
341 if (__s == NULL) { \
342 zend_block_source *__t = zend_arena_alloc(&ctx->arena, sizeof(zend_block_source)); \
343 __t->next = tob->sources; \
344 tob->sources = __t; \
345 __t->from = fromb; \
346 } \
347 }
348
349 #define DEL_SOURCE(cs) do { \
350 *(cs) = (*(cs))->next; \
351 } while (0)
352
353
replace_source(zend_block_source * list,zend_code_block * old,zend_code_block * new)354 static inline void replace_source(zend_block_source *list, zend_code_block *old, zend_code_block *new)
355 {
356 /* replace all references to 'old' in 'list' with 'new' */
357 zend_block_source **cs;
358 int found = 0;
359
360 for (cs = &list; *cs; cs = &((*cs)->next)) {
361 if ((*cs)->from == new) {
362 if (found) {
363 DEL_SOURCE(cs);
364 } else {
365 found = 1;
366 }
367 }
368
369 if ((*cs)->from == old) {
370 if (found) {
371 DEL_SOURCE(cs);
372 } else {
373 (*cs)->from = new;
374 found = 1;
375 }
376 }
377 }
378 }
379
del_source(zend_code_block * from,zend_code_block * to)380 static inline void del_source(zend_code_block *from, zend_code_block *to)
381 {
382 /* delete source 'from' from 'to'-s sources list */
383 zend_block_source **cs = &to->sources;
384
385 if (to->sources == NULL) {
386 to->access = 0;
387 return;
388 }
389
390 if (from == to) {
391 return;
392 }
393
394 while (*cs) {
395 if ((*cs)->from == from) {
396 DEL_SOURCE(cs);
397 break;
398 }
399 cs = &((*cs)->next);
400 }
401
402 if (to->sources == NULL) {
403 /* 'to' has no more sources - it's unused, will be stripped */
404 to->access = 0;
405 return;
406 }
407
408 if (!to->protected && to->sources->next == NULL) {
409 /* source to only one block */
410 zend_code_block *from_block = to->sources->from;
411
412 if (from_block->access && from_block->follow_to == to &&
413 from_block->op1_to == NULL &&
414 from_block->op2_to == NULL &&
415 from_block->ext_to == NULL) {
416 /* this block follows it's only predecessor - we can join them */
417 zend_op *new_to = from_block->start_opline + from_block->len;
418 if (new_to != to->start_opline) {
419 /* move block to new location */
420 memmove(new_to, to->start_opline, sizeof(zend_op)*to->len);
421 }
422 /* join blocks' lengths */
423 from_block->len += to->len;
424 /* move 'to'`s references to 'from' */
425 to->start_opline = NULL;
426 to->access = 0;
427 to->sources = NULL;
428 from_block->follow_to = to->follow_to;
429 if (to->op1_to) {
430 from_block->op1_to = to->op1_to;
431 replace_source(to->op1_to->sources, to, from_block);
432 }
433 if (to->op2_to) {
434 from_block->op2_to = to->op2_to;
435 replace_source(to->op2_to->sources, to, from_block);
436 }
437 if (to->ext_to) {
438 from_block->ext_to = to->ext_to;
439 replace_source(to->ext_to->sources, to, from_block);
440 }
441 if (to->follow_to) {
442 replace_source(to->follow_to->sources, to, from_block);
443 }
444 /* remove "to" from list */
445 }
446 }
447 }
448
delete_code_block(zend_code_block * block,zend_optimizer_ctx * ctx)449 static void delete_code_block(zend_code_block *block, zend_optimizer_ctx *ctx)
450 {
451 if (block->protected) {
452 return;
453 }
454 if (block->follow_to) {
455 zend_block_source *bs = block->sources;
456 while (bs) {
457 zend_code_block *from_block = bs->from;
458 zend_code_block *to = block->follow_to;
459 if (from_block->op1_to == block) {
460 from_block->op1_to = to;
461 ADD_SOURCE(from_block, to);
462 }
463 if (from_block->op2_to == block) {
464 from_block->op2_to = to;
465 ADD_SOURCE(from_block, to);
466 }
467 if (from_block->ext_to == block) {
468 from_block->ext_to = to;
469 ADD_SOURCE(from_block, to);
470 }
471 if (from_block->follow_to == block) {
472 from_block->follow_to = to;
473 ADD_SOURCE(from_block, to);
474 }
475 bs = bs->next;
476 }
477 }
478 block->access = 0;
479 }
480
zend_access_path(zend_code_block * block,zend_optimizer_ctx * ctx)481 static void zend_access_path(zend_code_block *block, zend_optimizer_ctx *ctx)
482 {
483 if (block->access) {
484 return;
485 }
486
487 block->access = 1;
488 if (block->op1_to) {
489 zend_access_path(block->op1_to, ctx);
490 ADD_SOURCE(block, block->op1_to);
491 }
492 if (block->op2_to) {
493 zend_access_path(block->op2_to, ctx);
494 ADD_SOURCE(block, block->op2_to);
495 }
496 if (block->ext_to) {
497 zend_access_path(block->ext_to, ctx);
498 ADD_SOURCE(block, block->ext_to);
499 }
500 if (block->follow_to) {
501 zend_access_path(block->follow_to, ctx);
502 ADD_SOURCE(block, block->follow_to);
503 }
504 }
505
506 /* Traverse CFG, mark reachable basic blocks and build back references */
zend_rebuild_access_path(zend_cfg * cfg,zend_op_array * op_array,int find_start,zend_optimizer_ctx * ctx)507 static void zend_rebuild_access_path(zend_cfg *cfg, zend_op_array *op_array, int find_start, zend_optimizer_ctx *ctx)
508 {
509 zend_code_block *blocks = cfg->blocks;
510 zend_code_block *start = find_start? NULL : blocks;
511 zend_code_block *b;
512
513 /* Mark all blocks as unaccessible and destroy back references */
514 b = blocks;
515 while (b != NULL) {
516 if (!start && b->access) {
517 start = b;
518 }
519 b->access = 0;
520 b->sources = NULL;
521 b = b->next;
522 }
523
524 /* Walk thorough all paths */
525 zend_access_path(start, ctx);
526
527 /* Add brk/cont paths */
528 if (op_array->last_brk_cont) {
529 int i;
530 for (i=0; i< op_array->last_brk_cont; i++) {
531 zend_access_path(cfg->loop_start[i], ctx);
532 zend_access_path(cfg->loop_cont[i], ctx);
533 zend_access_path(cfg->loop_brk[i], ctx);
534 }
535 }
536
537 /* Add exception paths */
538 if (op_array->last_try_catch) {
539 int i;
540 for (i=0; i< op_array->last_try_catch; i++) {
541 if (!cfg->catch[i]->access) {
542 zend_access_path(cfg->catch[i], ctx);
543 }
544 }
545 }
546 }
547
548 /* Data dependencies macros */
549
550 #define VAR_NUM_EX(op) VAR_NUM((op).var)
551
552 #define VAR_SOURCE(op) Tsource[VAR_NUM(op.var)]
553 #define SET_VAR_SOURCE(opline) Tsource[VAR_NUM(opline->result.var)] = opline
554
555 #define VAR_UNSET(op) do { if (op ## _type & (IS_TMP_VAR|IS_VAR)) {VAR_SOURCE(op) = NULL;}} while (0)
556
557 #define convert_to_string_safe(v) \
558 if (Z_TYPE_P((v)) == IS_NULL) { \
559 ZVAL_STRINGL((v), "", 0); \
560 } else { \
561 convert_to_string((v)); \
562 }
563
is_predecessor_smart_branch(zend_op * start,zend_op * predecessor)564 static int is_predecessor_smart_branch(zend_op *start, zend_op *predecessor) {
565 do {
566 if (predecessor == start) {
567 return 0;
568 }
569 predecessor--;
570 } while (predecessor->opcode == ZEND_NOP);
571
572 return zend_is_smart_branch(predecessor);
573 }
574
strip_nop(zend_code_block * block,zend_op_array * op_array,zend_optimizer_ctx * ctx)575 static void strip_nop(zend_code_block *block, zend_op_array *op_array, zend_optimizer_ctx *ctx)
576 {
577 zend_op *opline = block->start_opline;
578 zend_op *end, *new_end;
579
580 /* remove leading NOPs */
581 while (block->len > 0 && block->start_opline->opcode == ZEND_NOP) {
582 if (block->len == 1) {
583 /* this block is all NOPs, join with following block */
584 if (block->follow_to) {
585 delete_code_block(block, ctx);
586 }
587 return;
588 }
589 if (block->len == 2
590 && ((block->start_opline + 1)->opcode == ZEND_JMPZ
591 || (block->start_opline + 1)->opcode == ZEND_JMPNZ)
592 && (block->start_opline + 1)->op1_type & (IS_CV|IS_CONST)
593 && block->start_opline > op_array->opcodes
594 && zend_is_smart_branch(block->start_opline - 1)) {
595 break;
596 }
597 block->start_opline++;
598 block->start_opline_no++;
599 block->len--;
600 }
601
602 /* strip the inside NOPs */
603 opline = new_end = block->start_opline;
604 end = opline + block->len;
605
606 while (opline < end) {
607 zend_op *src;
608 int len = 0;
609
610 src = opline;
611 while (opline < end && opline->opcode == ZEND_NOP) {
612 if (opline + 1 < end
613 && ((opline + 1)->opcode == ZEND_JMPZ
614 || (opline + 1)->opcode == ZEND_JMPNZ)
615 && (opline + 1)->op1_type & (IS_CV|IS_CONST)
616 && is_predecessor_smart_branch(op_array->opcodes, opline)) {
617 /* don't remove NOP, that splits incorrect smart branch */
618 opline++;
619 break;
620 }
621 src++;
622 opline++;
623 }
624
625 while (opline < end && opline->opcode != ZEND_NOP) {
626 opline++;
627 }
628 len = opline - src;
629
630 /* move up non-NOP opcodes */
631 memmove(new_end, src, len*sizeof(zend_op));
632
633 new_end += len;
634 }
635 block->len = new_end - block->start_opline;
636 }
637
zend_optimize_block(zend_code_block * block,zend_op_array * op_array,zend_bitset used_ext,zend_cfg * cfg,zend_optimizer_ctx * ctx)638 static void zend_optimize_block(zend_code_block *block, zend_op_array *op_array, zend_bitset used_ext, zend_cfg *cfg, zend_optimizer_ctx *ctx)
639 {
640 zend_op *opline = block->start_opline;
641 zend_op *end, *last_op = NULL;
642 zend_op **Tsource = cfg->Tsource;
643
644 print_block(block, op_array->opcodes, "Opt ");
645
646 /* remove leading NOPs */
647 while (block->len > 0 && block->start_opline->opcode == ZEND_NOP) {
648 if (block->len == 1) {
649 /* this block is all NOPs, join with following block */
650 if (block->follow_to) {
651 delete_code_block(block, ctx);
652 }
653 if (block->len == 2
654 && ((block->start_opline + 1)->opcode == ZEND_JMPZ
655 || (block->start_opline + 1)->opcode == ZEND_JMPNZ)
656 && (block->start_opline + 1)->op1_type & (IS_CV|IS_CONST)
657 && block->start_opline > op_array->opcodes
658 && zend_is_smart_branch(block->start_opline - 1)) {
659 break;
660 }
661 return;
662 }
663 block->start_opline++;
664 block->start_opline_no++;
665 block->len--;
666 }
667
668 /* we track data dependencies only insight a single basic block */
669 memset(Tsource, 0, (op_array->last_var + op_array->T) * sizeof(zend_op *));
670 opline = block->start_opline;
671 end = opline + block->len;
672 while ((op_array->T) && (opline < end)) {
673 /* strip X = QM_ASSIGN(const) */
674 if ((ZEND_OP1_TYPE(opline) & (IS_TMP_VAR|IS_VAR)) &&
675 VAR_SOURCE(opline->op1) &&
676 VAR_SOURCE(opline->op1)->opcode == ZEND_QM_ASSIGN &&
677 ZEND_OP1_TYPE(VAR_SOURCE(opline->op1)) == IS_CONST &&
678 opline->opcode != ZEND_CASE && /* CASE _always_ expects variable */
679 opline->opcode != ZEND_FETCH_LIST &&
680 (opline->opcode != ZEND_FE_RESET_R || opline->opcode != ZEND_FE_RESET_RW) &&
681 opline->opcode != ZEND_FREE
682 ) {
683 znode_op op1 = opline->op1;
684 zend_op *src = VAR_SOURCE(op1);
685 zval c = ZEND_OP1_LITERAL(src);
686 zval_copy_ctor(&c);
687 if (zend_optimizer_update_op1_const(op_array, opline, &c)) {
688 VAR_SOURCE(op1) = NULL;
689 literal_dtor(&ZEND_OP1_LITERAL(src));
690 MAKE_NOP(src);
691 }
692 }
693
694 /* T = QM_ASSIGN(C), F(T) => NOP, F(C) */
695 if ((ZEND_OP2_TYPE(opline) & (IS_TMP_VAR|IS_VAR)) &&
696 VAR_SOURCE(opline->op2) &&
697 VAR_SOURCE(opline->op2)->opcode == ZEND_QM_ASSIGN &&
698 ZEND_OP1_TYPE(VAR_SOURCE(opline->op2)) == IS_CONST) {
699 znode_op op2 = opline->op2;
700 zend_op *src = VAR_SOURCE(op2);
701 zval c = ZEND_OP1_LITERAL(src);
702 zval_copy_ctor(&c);
703 if (zend_optimizer_update_op2_const(op_array, opline, &c)) {
704 VAR_SOURCE(op2) = NULL;
705 literal_dtor(&ZEND_OP1_LITERAL(src));
706 MAKE_NOP(src);
707 }
708 }
709
710 /* T = CAST(X, String), ECHO(T) => NOP, ECHO(X) */
711 if (opline->opcode == ZEND_ECHO &&
712 ZEND_OP1_TYPE(opline) & (IS_TMP_VAR|IS_VAR) &&
713 VAR_SOURCE(opline->op1) &&
714 VAR_SOURCE(opline->op1)->opcode == ZEND_CAST &&
715 VAR_SOURCE(opline->op1)->extended_value == IS_STRING) {
716 zend_op *src = VAR_SOURCE(opline->op1);
717 COPY_NODE(opline->op1, src->op1);
718 MAKE_NOP(src);
719 }
720
721 #if 0
722 /* This pattern is unnecessary for PHP7,
723 * since compiler won't generate ZEND_FREE for ZEND_BOOL anymore */
724 /* T = BOOL(X), FREE(T) => NOP */
725 if (opline->opcode == ZEND_FREE &&
726 ZEND_OP1_TYPE(opline) == IS_TMP_VAR &&
727 VAR_SOURCE(opline->op1)) {
728 zend_op *src = VAR_SOURCE(opline->op1);
729 if (src->opcode == ZEND_BOOL) {
730 if (ZEND_OP1_TYPE(src) == IS_CONST) {
731 literal_dtor(&ZEND_OP1_LITERAL(src));
732 } else if (ZEND_OP1_TYPE(src) == IS_TMP_VAR) {
733 src->opcode = ZEND_FREE;
734 } else {
735 MAKE_NOP(src);
736 }
737 MAKE_NOP(opline);
738 }
739 }
740
741 /* pre-evaluate functions:
742 constant(x)
743 defined(x)
744 function_exists(x)
745 extension_loaded(x)
746 BAD: interacts badly with Accelerator
747 */
748 if((ZEND_OP1_TYPE(opline) & IS_VAR) &&
749 VAR_SOURCE(opline->op1) && VAR_SOURCE(opline->op1)->opcode == ZEND_DO_CF_FCALL &&
750 VAR_SOURCE(opline->op1)->extended_value == 1) {
751 zend_op *fcall = VAR_SOURCE(opline->op1);
752 zend_op *sv = fcall-1;
753 if(sv >= block->start_opline && sv->opcode == ZEND_SEND_VAL &&
754 ZEND_OP1_TYPE(sv) == IS_CONST && Z_TYPE(OPLINE_OP1_LITERAL(sv)) == IS_STRING &&
755 Z_LVAL(OPLINE_OP2_LITERAL(sv)) == 1
756 ) {
757 zval *arg = &OPLINE_OP1_LITERAL(sv);
758 char *fname = FUNCTION_CACHE->funcs[Z_LVAL(ZEND_OP1_LITERAL(fcall))].function_name;
759 int flen = FUNCTION_CACHE->funcs[Z_LVAL(ZEND_OP1_LITERAL(fcall))].name_len;
760 if(flen == sizeof("defined")-1 && zend_binary_strcasecmp(fname, flen, "defined", sizeof("defined")-1) == 0) {
761 zval c;
762 if(zend_optimizer_get_persistent_constant(Z_STR_P(arg), &c, 0 ELS_CC) != 0) {
763 literal_dtor(arg);
764 MAKE_NOP(sv);
765 MAKE_NOP(fcall);
766 LITERAL_BOOL(opline->op1, 1);
767 ZEND_OP1_TYPE(opline) = IS_CONST;
768 }
769 } else if((flen == sizeof("function_exists")-1 && zend_binary_strcasecmp(fname, flen, "function_exists", sizeof("function_exists")-1) == 0) ||
770 (flen == sizeof("is_callable")-1 && zend_binary_strcasecmp(fname, flen, "is_callable", sizeof("is_callable")-1) == 0)
771 ) {
772 zend_function *function;
773 if((function = zend_hash_find_ptr(EG(function_table), Z_STR_P(arg))) != NULL) {
774 literal_dtor(arg);
775 MAKE_NOP(sv);
776 MAKE_NOP(fcall);
777 LITERAL_BOOL(opline->op1, 1);
778 ZEND_OP1_TYPE(opline) = IS_CONST;
779 }
780 } else if(flen == sizeof("constant")-1 && zend_binary_strcasecmp(fname, flen, "constant", sizeof("constant")-1) == 0) {
781 zval c;
782 if(zend_optimizer_get_persistent_constant(Z_STR_P(arg), &c, 1 ELS_CC) != 0) {
783 literal_dtor(arg);
784 MAKE_NOP(sv);
785 MAKE_NOP(fcall);
786 ZEND_OP1_LITERAL(opline) = zend_optimizer_add_literal(op_array, &c);
787 /* no copy ctor - get already copied it */
788 ZEND_OP1_TYPE(opline) = IS_CONST;
789 }
790 } else if(flen == sizeof("extension_loaded")-1 && zend_binary_strcasecmp(fname, flen, "extension_loaded", sizeof("extension_loaded")-1) == 0) {
791 if(zend_hash_exists(&module_registry, Z_STR_P(arg))) {
792 literal_dtor(arg);
793 MAKE_NOP(sv);
794 MAKE_NOP(fcall);
795 LITERAL_BOOL(opline->op1, 1);
796 ZEND_OP1_TYPE(opline) = IS_CONST;
797 }
798 }
799 }
800 }
801 #endif
802
803 /* IS_EQ(TRUE, X) => BOOL(X)
804 * IS_EQ(FALSE, X) => BOOL_NOT(X)
805 * IS_NOT_EQ(TRUE, X) => BOOL_NOT(X)
806 * IS_NOT_EQ(FALSE, X) => BOOL(X)
807 * CASE(TRUE, X) => BOOL(X)
808 * CASE(FALSE, X) => BOOL_NOT(X)
809 */
810 if (opline->opcode == ZEND_IS_EQUAL ||
811 opline->opcode == ZEND_IS_NOT_EQUAL ||
812 /* CASE variable will be deleted later by FREE, so we can't optimize it */
813 (opline->opcode == ZEND_CASE && (ZEND_OP1_TYPE(opline) & (IS_CONST|IS_CV)))) {
814 if (ZEND_OP1_TYPE(opline) == IS_CONST &&
815 (Z_TYPE(ZEND_OP1_LITERAL(opline)) == IS_FALSE ||
816 Z_TYPE(ZEND_OP1_LITERAL(opline)) == IS_TRUE)) {
817 /* T = IS_EQUAL(TRUE, X) => T = BOOL(X) */
818 /* T = IS_EQUAL(FALSE, X) => T = BOOL_NOT(X) */
819 /* T = IS_NOT_EQUAL(TRUE, X) => T = BOOL_NOT(X) */
820 /* T = IS_NOT_EQUAL(FALSE, X) => T = BOOL(X) */
821 /* Optimization of comparison with "null" is not safe,
822 * because ("0" == null) is not equal to !("0")
823 */
824 opline->opcode =
825 ((opline->opcode != ZEND_IS_NOT_EQUAL) == ((Z_TYPE(ZEND_OP1_LITERAL(opline))) == IS_TRUE)) ?
826 ZEND_BOOL : ZEND_BOOL_NOT;
827 COPY_NODE(opline->op1, opline->op2);
828 SET_UNUSED(opline->op2);
829 } else if (ZEND_OP2_TYPE(opline) == IS_CONST &&
830 (Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_FALSE ||
831 Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_TRUE)) {
832 /* T = IS_EQUAL(X, TRUE) => T = BOOL(X) */
833 /* T = IS_EQUAL(X, FALSE) => T = BOOL_NOT(X) */
834 /* T = IS_NOT_EQUAL(X, TRUE) => T = BOOL_NOT(X) */
835 /* T = IS_NOT_EQUAL(X, FALSE) => T = BOOL(X) */
836 /* Optimization of comparison with "null" is not safe,
837 * because ("0" == null) is not equal to !("0")
838 */
839 opline->opcode =
840 ((opline->opcode != ZEND_IS_NOT_EQUAL) == ((Z_TYPE(ZEND_OP2_LITERAL(opline))) == IS_TRUE)) ?
841 ZEND_BOOL : ZEND_BOOL_NOT;
842 SET_UNUSED(opline->op2);
843 }
844 }
845
846 if ((opline->opcode == ZEND_BOOL ||
847 opline->opcode == ZEND_BOOL_NOT ||
848 opline->opcode == ZEND_JMPZ ||
849 opline->opcode == ZEND_JMPNZ ||
850 opline->opcode == ZEND_JMPZNZ) &&
851 ZEND_OP1_TYPE(opline) == IS_TMP_VAR &&
852 VAR_SOURCE(opline->op1) != NULL &&
853 !zend_bitset_in(used_ext, VAR_NUM(ZEND_OP1(opline).var)) &&
854 VAR_SOURCE(opline->op1)->opcode == ZEND_BOOL_NOT) {
855 /* T = BOOL_NOT(X) + JMPZ(T) -> NOP, JMPNZ(X) */
856 zend_op *src = VAR_SOURCE(opline->op1);
857
858 COPY_NODE(opline->op1, src->op1);
859
860 switch (opline->opcode) {
861 case ZEND_BOOL:
862 /* T = BOOL_NOT(X) + BOOL(T) -> NOP, BOOL_NOT(X) */
863 opline->opcode = ZEND_BOOL_NOT;
864 break;
865 case ZEND_BOOL_NOT:
866 /* T = BOOL_NOT(X) + BOOL_BOOL(T) -> NOP, BOOL(X) */
867 opline->opcode = ZEND_BOOL;
868 break;
869 case ZEND_JMPZ:
870 /* T = BOOL_NOT(X) + JMPZ(T,L) -> NOP, JMPNZ(X,L) */
871 opline->opcode = ZEND_JMPNZ;
872 break;
873 case ZEND_JMPNZ:
874 /* T = BOOL_NOT(X) + JMPNZ(T,L) -> NOP, JMPZ(X,L) */
875 opline->opcode = ZEND_JMPZ;
876 break;
877 case ZEND_JMPZNZ:
878 {
879 /* T = BOOL_NOT(X) + JMPZNZ(T,L1,L2) -> NOP, JMPZNZ(X,L2,L1) */
880 int op_t;
881 zend_code_block *op_b;
882
883 op_t = opline->extended_value;
884 opline->extended_value = ZEND_OP2(opline).opline_num;
885 ZEND_OP2(opline).opline_num = op_t;
886
887 op_b = block->ext_to;
888 block->ext_to = block->op2_to;
889 block->op2_to = op_b;
890 }
891 break;
892 }
893
894 VAR_UNSET(opline->op1);
895 MAKE_NOP(src);
896 continue;
897 } else
898 #if 0
899 /* T = BOOL_NOT(X) + T = JMPZ_EX(T, X) -> T = BOOL_NOT(X), JMPNZ(X) */
900 if(0 && (opline->opcode == ZEND_JMPZ_EX ||
901 opline->opcode == ZEND_JMPNZ_EX) &&
902 ZEND_OP1_TYPE(opline) == IS_TMP_VAR &&
903 VAR_SOURCE(opline->op1) != NULL &&
904 VAR_SOURCE(opline->op1)->opcode == ZEND_BOOL_NOT &&
905 ZEND_OP1(opline).var == ZEND_RESULT(opline).var
906 ) {
907 zend_op *src = VAR_SOURCE(opline->op1);
908 if(opline->opcode == ZEND_JMPZ_EX) {
909 opline->opcode = ZEND_JMPNZ;
910 } else {
911 opline->opcode = ZEND_JMPZ;
912 }
913 COPY_NODE(opline->op1, src->op1);
914 SET_UNUSED(opline->result);
915 continue;
916 } else
917 #endif
918 /* T = BOOL(X) + JMPZ(T) -> NOP, JMPZ(X) */
919 if ((opline->opcode == ZEND_BOOL ||
920 opline->opcode == ZEND_BOOL_NOT ||
921 opline->opcode == ZEND_JMPZ ||
922 opline->opcode == ZEND_JMPZ_EX ||
923 opline->opcode == ZEND_JMPNZ_EX ||
924 opline->opcode == ZEND_JMPNZ ||
925 opline->opcode == ZEND_JMPZNZ) &&
926 (ZEND_OP1_TYPE(opline) & (IS_TMP_VAR|IS_VAR)) &&
927 VAR_SOURCE(opline->op1) != NULL &&
928 (!zend_bitset_in(used_ext, VAR_NUM(ZEND_OP1(opline).var)) ||
929 ((ZEND_RESULT_TYPE(opline) & (IS_TMP_VAR|IS_VAR)) &&
930 ZEND_RESULT(opline).var == ZEND_OP1(opline).var)) &&
931 (VAR_SOURCE(opline->op1)->opcode == ZEND_BOOL ||
932 VAR_SOURCE(opline->op1)->opcode == ZEND_QM_ASSIGN)) {
933 zend_op *src = VAR_SOURCE(opline->op1);
934 COPY_NODE(opline->op1, src->op1);
935
936 VAR_UNSET(opline->op1);
937 MAKE_NOP(src);
938 continue;
939 } else if (last_op && opline->opcode == ZEND_ECHO &&
940 last_op->opcode == ZEND_ECHO &&
941 ZEND_OP1_TYPE(opline) == IS_CONST &&
942 Z_TYPE(ZEND_OP1_LITERAL(opline)) != IS_DOUBLE &&
943 ZEND_OP1_TYPE(last_op) == IS_CONST &&
944 Z_TYPE(ZEND_OP1_LITERAL(last_op)) != IS_DOUBLE) {
945 /* compress consecutive ECHO's.
946 * Float to string conversion may be affected by current
947 * locale setting.
948 */
949 int l, old_len;
950
951 if (Z_TYPE(ZEND_OP1_LITERAL(opline)) != IS_STRING) {
952 convert_to_string_safe(&ZEND_OP1_LITERAL(opline));
953 }
954 if (Z_TYPE(ZEND_OP1_LITERAL(last_op)) != IS_STRING) {
955 convert_to_string_safe(&ZEND_OP1_LITERAL(last_op));
956 }
957 old_len = Z_STRLEN(ZEND_OP1_LITERAL(last_op));
958 l = old_len + Z_STRLEN(ZEND_OP1_LITERAL(opline));
959 if (!Z_REFCOUNTED(ZEND_OP1_LITERAL(last_op))) {
960 zend_string *tmp = zend_string_alloc(l, 0);
961 memcpy(ZSTR_VAL(tmp), Z_STRVAL(ZEND_OP1_LITERAL(last_op)), old_len);
962 Z_STR(ZEND_OP1_LITERAL(last_op)) = tmp;
963 } else {
964 Z_STR(ZEND_OP1_LITERAL(last_op)) = zend_string_extend(Z_STR(ZEND_OP1_LITERAL(last_op)), l, 0);
965 }
966 Z_TYPE_INFO(ZEND_OP1_LITERAL(last_op)) = IS_STRING_EX;
967 memcpy(Z_STRVAL(ZEND_OP1_LITERAL(last_op)) + old_len, Z_STRVAL(ZEND_OP1_LITERAL(opline)), Z_STRLEN(ZEND_OP1_LITERAL(opline)));
968 Z_STRVAL(ZEND_OP1_LITERAL(last_op))[l] = '\0';
969 zval_dtor(&ZEND_OP1_LITERAL(opline));
970 ZVAL_STR(&ZEND_OP1_LITERAL(opline), zend_new_interned_string(Z_STR(ZEND_OP1_LITERAL(last_op))));
971 ZVAL_NULL(&ZEND_OP1_LITERAL(last_op));
972 MAKE_NOP(last_op);
973 } else if ((opline->opcode == ZEND_CONCAT) &&
974 ZEND_OP2_TYPE(opline) == IS_CONST &&
975 ZEND_OP1_TYPE(opline) == IS_TMP_VAR &&
976 VAR_SOURCE(opline->op1) &&
977 (VAR_SOURCE(opline->op1)->opcode == ZEND_CONCAT ||
978 VAR_SOURCE(opline->op1)->opcode == ZEND_FAST_CONCAT) &&
979 ZEND_OP2_TYPE(VAR_SOURCE(opline->op1)) == IS_CONST &&
980 ZEND_RESULT(VAR_SOURCE(opline->op1)).var == ZEND_OP1(opline).var) {
981 /* compress consecutive CONCAT/ADD_STRING/ADD_CHARs */
982 zend_op *src = VAR_SOURCE(opline->op1);
983 int l, old_len;
984
985 if (Z_TYPE(ZEND_OP2_LITERAL(opline)) != IS_STRING) {
986 convert_to_string_safe(&ZEND_OP2_LITERAL(opline));
987 }
988 if (Z_TYPE(ZEND_OP2_LITERAL(src)) != IS_STRING) {
989 convert_to_string_safe(&ZEND_OP2_LITERAL(src));
990 }
991
992 VAR_UNSET(opline->op1);
993 COPY_NODE(opline->op1, src->op1);
994 old_len = Z_STRLEN(ZEND_OP2_LITERAL(src));
995 l = old_len + Z_STRLEN(ZEND_OP2_LITERAL(opline));
996 if (!Z_REFCOUNTED(ZEND_OP2_LITERAL(src))) {
997 zend_string *tmp = zend_string_alloc(l, 0);
998 memcpy(ZSTR_VAL(tmp), Z_STRVAL(ZEND_OP2_LITERAL(src)), old_len);
999 Z_STR(ZEND_OP2_LITERAL(last_op)) = tmp;
1000 } else {
1001 Z_STR(ZEND_OP2_LITERAL(src)) = zend_string_extend(Z_STR(ZEND_OP2_LITERAL(src)), l, 0);
1002 }
1003 Z_TYPE_INFO(ZEND_OP2_LITERAL(last_op)) = IS_STRING_EX;
1004 memcpy(Z_STRVAL(ZEND_OP2_LITERAL(src)) + old_len, Z_STRVAL(ZEND_OP2_LITERAL(opline)), Z_STRLEN(ZEND_OP2_LITERAL(opline)));
1005 Z_STRVAL(ZEND_OP2_LITERAL(src))[l] = '\0';
1006 zend_string_release(Z_STR(ZEND_OP2_LITERAL(opline)));
1007 ZVAL_STR(&ZEND_OP2_LITERAL(opline), zend_new_interned_string(Z_STR(ZEND_OP2_LITERAL(src))));
1008 ZVAL_NULL(&ZEND_OP2_LITERAL(src));
1009 MAKE_NOP(src);
1010 } else if ((opline->opcode == ZEND_ADD ||
1011 opline->opcode == ZEND_SUB ||
1012 opline->opcode == ZEND_MUL ||
1013 opline->opcode == ZEND_DIV ||
1014 opline->opcode == ZEND_MOD ||
1015 opline->opcode == ZEND_SL ||
1016 opline->opcode == ZEND_SR ||
1017 opline->opcode == ZEND_CONCAT ||
1018 opline->opcode == ZEND_FAST_CONCAT ||
1019 opline->opcode == ZEND_IS_EQUAL ||
1020 opline->opcode == ZEND_IS_NOT_EQUAL ||
1021 opline->opcode == ZEND_IS_SMALLER ||
1022 opline->opcode == ZEND_IS_SMALLER_OR_EQUAL ||
1023 opline->opcode == ZEND_IS_IDENTICAL ||
1024 opline->opcode == ZEND_IS_NOT_IDENTICAL ||
1025 opline->opcode == ZEND_BOOL_XOR ||
1026 opline->opcode == ZEND_BW_OR ||
1027 opline->opcode == ZEND_BW_AND ||
1028 opline->opcode == ZEND_BW_XOR) &&
1029 ZEND_OP1_TYPE(opline)==IS_CONST &&
1030 ZEND_OP2_TYPE(opline)==IS_CONST) {
1031 /* evaluate constant expressions */
1032 binary_op_type binary_op = get_binary_op(opline->opcode);
1033 zval result;
1034 int er;
1035
1036 if ((opline->opcode == ZEND_DIV || opline->opcode == ZEND_MOD) &&
1037 zval_get_long(&ZEND_OP2_LITERAL(opline)) == 0) {
1038 if (RESULT_USED(opline)) {
1039 SET_VAR_SOURCE(opline);
1040 }
1041 opline++;
1042 continue;
1043 } else if ((opline->opcode == ZEND_SL || opline->opcode == ZEND_SR) &&
1044 zval_get_long(&ZEND_OP2_LITERAL(opline)) < 0) {
1045 if (RESULT_USED(opline)) {
1046 SET_VAR_SOURCE(opline);
1047 }
1048 opline++;
1049 continue;
1050 }
1051 er = EG(error_reporting);
1052 EG(error_reporting) = 0;
1053 if (binary_op(&result, &ZEND_OP1_LITERAL(opline), &ZEND_OP2_LITERAL(opline)) == SUCCESS) {
1054 literal_dtor(&ZEND_OP1_LITERAL(opline));
1055 literal_dtor(&ZEND_OP2_LITERAL(opline));
1056 opline->opcode = ZEND_QM_ASSIGN;
1057 SET_UNUSED(opline->op2);
1058 zend_optimizer_update_op1_const(op_array, opline, &result);
1059 }
1060 EG(error_reporting) = er;
1061 } else if ((opline->opcode == ZEND_BOOL ||
1062 opline->opcode == ZEND_BOOL_NOT ||
1063 opline->opcode == ZEND_BW_NOT) && ZEND_OP1_TYPE(opline) == IS_CONST) {
1064 /* evaluate constant unary ops */
1065 unary_op_type unary_op = get_unary_op(opline->opcode);
1066 zval result;
1067
1068 if (unary_op) {
1069 unary_op(&result, &ZEND_OP1_LITERAL(opline));
1070 literal_dtor(&ZEND_OP1_LITERAL(opline));
1071 } else {
1072 /* BOOL */
1073 ZVAL_COPY_VALUE(&result, &ZEND_OP1_LITERAL(opline));
1074 convert_to_boolean(&result);
1075 ZVAL_NULL(&ZEND_OP1_LITERAL(opline));
1076 }
1077 opline->opcode = ZEND_QM_ASSIGN;
1078 zend_optimizer_update_op1_const(op_array, opline, &result);
1079 } else if ((opline->opcode == ZEND_RETURN || opline->opcode == ZEND_EXIT) &&
1080 (ZEND_OP1_TYPE(opline) & (IS_TMP_VAR|IS_VAR)) &&
1081 VAR_SOURCE(opline->op1) &&
1082 VAR_SOURCE(opline->op1)->opcode == ZEND_QM_ASSIGN) {
1083 /* T = QM_ASSIGN(X), RETURN(T) to RETURN(X) */
1084 zend_op *src = VAR_SOURCE(opline->op1);
1085 VAR_UNSET(opline->op1);
1086 COPY_NODE(opline->op1, src->op1);
1087 MAKE_NOP(src);
1088 } else if (opline->opcode == ZEND_CONCAT || opline->opcode == ZEND_FAST_CONCAT) {
1089 if ((ZEND_OP1_TYPE(opline) & (IS_TMP_VAR|IS_VAR)) &&
1090 VAR_SOURCE(opline->op1) &&
1091 VAR_SOURCE(opline->op1)->opcode == ZEND_CAST &&
1092 VAR_SOURCE(opline->op1)->extended_value == IS_STRING) {
1093 /* convert T1 = CAST(STRING, X), T2 = CONCAT(T1, Y) to T2 = CONCAT(X,Y) */
1094 zend_op *src = VAR_SOURCE(opline->op1);
1095 VAR_UNSET(opline->op1);
1096 COPY_NODE(opline->op1, src->op1);
1097 MAKE_NOP(src);
1098 }
1099 if ((ZEND_OP2_TYPE(opline) & (IS_TMP_VAR|IS_VAR)) &&
1100 VAR_SOURCE(opline->op2) &&
1101 VAR_SOURCE(opline->op2)->opcode == ZEND_CAST &&
1102 VAR_SOURCE(opline->op2)->extended_value == IS_STRING) {
1103 /* convert T1 = CAST(STRING, X), T2 = CONCAT(Y, T1) to T2 = CONCAT(Y,X) */
1104 zend_op *src = VAR_SOURCE(opline->op2);
1105 VAR_UNSET(opline->op2);
1106 COPY_NODE(opline->op2, src->op1);
1107 MAKE_NOP(src);
1108 }
1109 if (ZEND_OP1_TYPE(opline) == IS_CONST &&
1110 Z_TYPE(ZEND_OP1_LITERAL(opline)) == IS_STRING &&
1111 Z_STRLEN(ZEND_OP1_LITERAL(opline)) == 0) {
1112 /* convert CONCAT('', X) => CAST(STRING, X) */
1113 literal_dtor(&ZEND_OP1_LITERAL(opline));
1114 opline->opcode = ZEND_CAST;
1115 opline->extended_value = IS_STRING;
1116 COPY_NODE(opline->op1, opline->op2);
1117 opline->op2_type = IS_UNUSED;
1118 opline->op2.var = 0;
1119 } else if (ZEND_OP2_TYPE(opline) == IS_CONST &&
1120 Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_STRING &&
1121 Z_STRLEN(ZEND_OP2_LITERAL(opline)) == 0) {
1122 /* convert CONCAT(X, '') => CAST(STRING, X) */
1123 literal_dtor(&ZEND_OP2_LITERAL(opline));
1124 opline->opcode = ZEND_CAST;
1125 opline->extended_value = IS_STRING;
1126 opline->op2_type = IS_UNUSED;
1127 opline->op2.var = 0;
1128 } else if (opline->opcode == ZEND_CONCAT &&
1129 (opline->op1_type == IS_CONST ||
1130 (opline->op1_type == IS_TMP_VAR &&
1131 VAR_SOURCE(opline->op1) &&
1132 (VAR_SOURCE(opline->op1)->opcode == ZEND_FAST_CONCAT ||
1133 VAR_SOURCE(opline->op1)->opcode == ZEND_ROPE_END ||
1134 VAR_SOURCE(opline->op1)->opcode == ZEND_FETCH_CONSTANT))) &&
1135 (opline->op2_type == IS_CONST ||
1136 (opline->op2_type == IS_TMP_VAR &&
1137 VAR_SOURCE(opline->op2) &&
1138 (VAR_SOURCE(opline->op2)->opcode == ZEND_FAST_CONCAT ||
1139 VAR_SOURCE(opline->op2)->opcode == ZEND_ROPE_END ||
1140 VAR_SOURCE(opline->op2)->opcode == ZEND_FETCH_CONSTANT)))) {
1141 opline->opcode = ZEND_FAST_CONCAT;
1142 }
1143 } else if (opline->opcode == ZEND_QM_ASSIGN &&
1144 ZEND_OP1_TYPE(opline) == ZEND_RESULT_TYPE(opline) &&
1145 ZEND_OP1(opline).var == ZEND_RESULT(opline).var) {
1146 /* strip T = QM_ASSIGN(T) */
1147 MAKE_NOP(opline);
1148 } else if (opline->opcode == ZEND_BOOL &&
1149 ZEND_OP1_TYPE(opline) == IS_TMP_VAR &&
1150 VAR_SOURCE(opline->op1) &&
1151 (VAR_SOURCE(opline->op1)->opcode == ZEND_IS_EQUAL ||
1152 VAR_SOURCE(opline->op1)->opcode == ZEND_IS_NOT_EQUAL ||
1153 VAR_SOURCE(opline->op1)->opcode == ZEND_IS_SMALLER ||
1154 VAR_SOURCE(opline->op1)->opcode == ZEND_IS_SMALLER_OR_EQUAL ||
1155 VAR_SOURCE(opline->op1)->opcode == ZEND_BOOL ||
1156 VAR_SOURCE(opline->op1)->opcode == ZEND_IS_IDENTICAL ||
1157 VAR_SOURCE(opline->op1)->opcode == ZEND_IS_NOT_IDENTICAL ||
1158 VAR_SOURCE(opline->op1)->opcode == ZEND_ISSET_ISEMPTY_VAR ||
1159 VAR_SOURCE(opline->op1)->opcode == ZEND_ISSET_ISEMPTY_DIM_OBJ) &&
1160 !zend_bitset_in(used_ext, VAR_NUM(ZEND_OP1(opline).var))) {
1161 /* T = IS_SMALLER(X, Y), T1 = BOOL(T) => T = IS_SMALLER(X, Y), T1 = QM_ASSIGN(T) */
1162 zend_op *src = VAR_SOURCE(opline->op1);
1163 COPY_NODE(src->result, opline->result);
1164 SET_VAR_SOURCE(src);
1165 MAKE_NOP(opline);
1166 }
1167 /* get variable source */
1168 if (RESULT_USED(opline)) {
1169 SET_VAR_SOURCE(opline);
1170 }
1171 if (opline->opcode != ZEND_NOP) {
1172 last_op = opline;
1173 }
1174 opline++;
1175 }
1176
1177 strip_nop(block, op_array, ctx);
1178 }
1179
1180 /* Rebuild plain (optimized) op_array from CFG */
assemble_code_blocks(zend_cfg * cfg,zend_op_array * op_array)1181 static void assemble_code_blocks(zend_cfg *cfg, zend_op_array *op_array)
1182 {
1183 zend_code_block *blocks = cfg->blocks;
1184 zend_op *new_opcodes = emalloc(op_array->last * sizeof(zend_op));
1185 zend_op *opline = new_opcodes;
1186 zend_code_block *cur_block = blocks;
1187
1188 /* Copy code of reachable blocks into a single buffer */
1189 while (cur_block) {
1190 if (cur_block->access) {
1191 memcpy(opline, cur_block->start_opline, cur_block->len * sizeof(zend_op));
1192 cur_block->start_opline = opline;
1193 opline += cur_block->len;
1194 if ((opline - 1)->opcode == ZEND_JMP) {
1195 zend_code_block *next;
1196 next = cur_block->next;
1197 while (next && !next->access) {
1198 next = next->next;
1199 }
1200 if (next && next == cur_block->op1_to) {
1201 /* JMP to the next block - strip it */
1202 cur_block->follow_to = cur_block->op1_to;
1203 cur_block->op1_to = NULL;
1204 MAKE_NOP((opline - 1));
1205 opline--;
1206 cur_block->len--;
1207 }
1208 }
1209 } else {
1210 /* this block will not be used, delete all constants there */
1211 zend_op *_opl;
1212 zend_op *end = cur_block->start_opline + cur_block->len;
1213 for (_opl = cur_block->start_opline; _opl && _opl < end; _opl++) {
1214 if (ZEND_OP1_TYPE(_opl) == IS_CONST) {
1215 literal_dtor(&ZEND_OP1_LITERAL(_opl));
1216 }
1217 if (ZEND_OP2_TYPE(_opl) == IS_CONST) {
1218 literal_dtor(&ZEND_OP2_LITERAL(_opl));
1219 }
1220 }
1221 }
1222 cur_block = cur_block->next;
1223 }
1224
1225 op_array->last = opline-new_opcodes;
1226
1227 /* adjust exception jump targets */
1228 if (op_array->last_try_catch) {
1229 int i, j;
1230 for (i = 0, j = 0; i< op_array->last_try_catch; i++) {
1231 if (cfg->try[i]->access) {
1232 op_array->try_catch_array[j].try_op = cfg->try[i]->start_opline - new_opcodes;
1233 op_array->try_catch_array[j].catch_op = cfg->catch[i]->start_opline - new_opcodes;
1234 j++;
1235 }
1236 }
1237 op_array->last_try_catch = j;
1238 }
1239
1240 /* adjust loop jump targets */
1241 if (op_array->last_brk_cont) {
1242 int i;
1243 for (i = 0; i< op_array->last_brk_cont; i++) {
1244 op_array->brk_cont_array[i].start = cfg->loop_start[i]->start_opline - new_opcodes;
1245 op_array->brk_cont_array[i].cont = cfg->loop_cont[i]->start_opline - new_opcodes;
1246 op_array->brk_cont_array[i].brk = cfg->loop_brk[i]->start_opline - new_opcodes;
1247 }
1248 }
1249
1250 /* adjust jump targets */
1251 for (cur_block = blocks; cur_block; cur_block = cur_block->next) {
1252 if (!cur_block->access) {
1253 continue;
1254 }
1255 opline = cur_block->start_opline + cur_block->len - 1;
1256 if (opline->opcode == ZEND_OP_DATA) {
1257 opline--;
1258 }
1259 if (cur_block->op1_to) {
1260 ZEND_OP1(opline).opline_num = cur_block->op1_to->start_opline - new_opcodes;
1261 }
1262 if (cur_block->op2_to) {
1263 ZEND_OP2(opline).opline_num = cur_block->op2_to->start_opline - new_opcodes;
1264 }
1265 if (cur_block->ext_to) {
1266 opline->extended_value = cur_block->ext_to->start_opline - new_opcodes;
1267 }
1268 print_block(cur_block, new_opcodes, "Out ");
1269 }
1270 efree(op_array->opcodes);
1271 op_array->opcodes = erealloc(new_opcodes, op_array->last * sizeof(zend_op));
1272
1273 /* adjust early binding list */
1274 if (op_array->early_binding != (uint32_t)-1) {
1275 uint32_t *opline_num = &op_array->early_binding;
1276 zend_op *end;
1277
1278 opline = op_array->opcodes;
1279 end = opline + op_array->last;
1280 while (opline < end) {
1281 if (opline->opcode == ZEND_DECLARE_INHERITED_CLASS_DELAYED) {
1282 *opline_num = opline - op_array->opcodes;
1283 opline_num = &ZEND_RESULT(opline).opline_num;
1284 }
1285 ++opline;
1286 }
1287 *opline_num = -1;
1288 }
1289 }
1290
zend_jmp_optimization(zend_code_block * block,zend_op_array * op_array,zend_code_block * blocks,zend_cfg * cfg,zend_optimizer_ctx * ctx)1291 static void zend_jmp_optimization(zend_code_block *block, zend_op_array *op_array, zend_code_block *blocks, zend_cfg *cfg, zend_optimizer_ctx *ctx)
1292 {
1293 /* last_op is the last opcode of the current block */
1294 zend_op *last_op = (block->start_opline + block->len - 1);
1295
1296 if (!block->len) {
1297 return;
1298 }
1299 switch (last_op->opcode) {
1300 case ZEND_JMP:
1301 {
1302 zend_op *target = block->op1_to->start_opline;
1303 zend_code_block *next = block->next;
1304
1305 while (next && !next->access) {
1306 /* find used one */
1307 next = next->next;
1308 }
1309
1310 /* JMP(next) -> NOP */
1311 if (block->op1_to == next) {
1312 block->follow_to = block->op1_to;
1313 block->op1_to = NULL;
1314 MAKE_NOP(last_op);
1315 block->len--;
1316 if (block->len == 0) {
1317 /* this block is nothing but NOP now */
1318 delete_code_block(block, ctx);
1319 }
1320 break;
1321 }
1322
1323 if (((target->opcode == ZEND_JMP &&
1324 block->op1_to != block->op1_to->op1_to) ||
1325 target->opcode == ZEND_JMPZNZ) &&
1326 !block->op1_to->protected) {
1327 /* JMP L, L: JMP L1 -> JMP L1 */
1328 /* JMP L, L: JMPZNZ L1,L2 -> JMPZNZ L1,L2 */
1329 *last_op = *target;
1330 if (ZEND_OP1_TYPE(last_op) == IS_CONST) {
1331 zval zv = ZEND_OP1_LITERAL(last_op);
1332 zval_copy_ctor(&zv);
1333 last_op->op1.constant = zend_optimizer_add_literal(op_array, &zv);
1334 }
1335 del_source(block, block->op1_to);
1336 if (block->op1_to->op2_to) {
1337 block->op2_to = block->op1_to->op2_to;
1338 ADD_SOURCE(block, block->op2_to);
1339 }
1340 if (block->op1_to->ext_to) {
1341 block->ext_to = block->op1_to->ext_to;
1342 ADD_SOURCE(block, block->ext_to);
1343 }
1344 if (block->op1_to->op1_to) {
1345 block->op1_to = block->op1_to->op1_to;
1346 ADD_SOURCE(block, block->op1_to);
1347 } else {
1348 block->op1_to = NULL;
1349 }
1350 } else if (target->opcode == ZEND_RETURN ||
1351 target->opcode == ZEND_RETURN_BY_REF ||
1352 target->opcode == ZEND_FAST_RET ||
1353 target->opcode == ZEND_EXIT) {
1354 /* JMP L, L: RETURN to immediate RETURN */
1355 *last_op = *target;
1356 if (ZEND_OP1_TYPE(last_op) == IS_CONST) {
1357 zval zv = ZEND_OP1_LITERAL(last_op);
1358 zval_copy_ctor(&zv);
1359 last_op->op1.constant = zend_optimizer_add_literal(op_array, &zv);
1360 }
1361 del_source(block, block->op1_to);
1362 block->op1_to = NULL;
1363 #if 0
1364 /* Temporarily disabled - see bug #0025274 */
1365 } else if (0&& block->op1_to != block &&
1366 block->op1_to != blocks &&
1367 op_array->last_try_catch == 0 &&
1368 target->opcode != ZEND_FREE) {
1369 /* Block Reordering (saves one JMP on each "for" loop iteration)
1370 * It is disabled for some cases (ZEND_FREE)
1371 * which may break register allocation.
1372 */
1373 zend_bool can_reorder = 0;
1374 zend_block_source *cs = block->op1_to->sources;
1375
1376 /* the "target" block doesn't had any followed block */
1377 while(cs) {
1378 if (cs->from->follow_to == block->op1_to) {
1379 can_reorder = 0;
1380 break;
1381 }
1382 cs = cs->next;
1383 }
1384 if (can_reorder) {
1385 next = block->op1_to;
1386 /* the "target" block is not followed by current "block" */
1387 while (next->follow_to != NULL) {
1388 if (next->follow_to == block) {
1389 can_reorder = 0;
1390 break;
1391 }
1392 next = next->follow_to;
1393 }
1394 if (can_reorder) {
1395 zend_code_block *prev = blocks;
1396
1397 while (prev->next != block->op1_to) {
1398 prev = prev->next;
1399 }
1400 prev->next = next->next;
1401 next->next = block->next;
1402 block->next = block->op1_to;
1403
1404 block->follow_to = block->op1_to;
1405 block->op1_to = NULL;
1406 MAKE_NOP(last_op);
1407 block->len--;
1408 if(block->len == 0) {
1409 /* this block is nothing but NOP now */
1410 delete_code_block(block, ctx);
1411 }
1412 break;
1413 }
1414 }
1415 #endif
1416 }
1417 }
1418 break;
1419
1420 case ZEND_JMPZ:
1421 case ZEND_JMPNZ:
1422 /* constant conditional JMPs */
1423 if (ZEND_OP1_TYPE(last_op) == IS_CONST) {
1424 int should_jmp = zend_is_true(&ZEND_OP1_LITERAL(last_op));
1425
1426 if (last_op->opcode == ZEND_JMPZ) {
1427 should_jmp = !should_jmp;
1428 }
1429 literal_dtor(&ZEND_OP1_LITERAL(last_op));
1430 ZEND_OP1_TYPE(last_op) = IS_UNUSED;
1431 if (should_jmp) {
1432 /* JMPNZ(true) -> JMP */
1433 last_op->opcode = ZEND_JMP;
1434 COPY_NODE(last_op->op1, last_op->op2);
1435 block->op1_to = block->op2_to;
1436 del_source(block, block->follow_to);
1437 block->op2_to = NULL;
1438 block->follow_to = NULL;
1439 } else {
1440 /* JMPNZ(false) -> NOP */
1441 MAKE_NOP(last_op);
1442 del_source(block, block->op2_to);
1443 block->op2_to = NULL;
1444 }
1445 break;
1446 }
1447
1448 if (block->op2_to == block->follow_to) {
1449 /* L: JMPZ(X, L+1) -> NOP or FREE(X) */
1450
1451 if (last_op->op1_type == IS_VAR) {
1452 zend_op **Tsource = cfg->Tsource;
1453 zend_op *src = VAR_SOURCE(last_op->op1);
1454
1455 if (src &&
1456 src->opcode != ZEND_FETCH_R &&
1457 src->opcode != ZEND_FETCH_DIM_R &&
1458 src->opcode != ZEND_FETCH_OBJ_R) {
1459 ZEND_RESULT_TYPE(src) |= EXT_TYPE_UNUSED;
1460 MAKE_NOP(last_op);
1461 block->op2_to = NULL;
1462 break;
1463 }
1464 }
1465 if (last_op->op1_type == IS_CV) {
1466 break;
1467 } else if (last_op->op1_type & (IS_VAR|IS_TMP_VAR)) {
1468 last_op->opcode = ZEND_FREE;
1469 last_op->op2.num = 0;
1470 block->op2_to = NULL;
1471 } else {
1472 MAKE_NOP(last_op);
1473 block->op2_to = NULL;
1474 }
1475 break;
1476 }
1477
1478 if (block->op2_to) {
1479 zend_uchar same_type = ZEND_OP1_TYPE(last_op);
1480 uint32_t same_var = VAR_NUM_EX(last_op->op1);
1481 zend_op *target;
1482 zend_op *target_end;
1483 zend_code_block *target_block = block->op2_to;;
1484
1485 next_target:
1486 target = target_block->start_opline;
1487 target_end = target_block->start_opline + target_block->len;
1488 while (target < target_end && target->opcode == ZEND_NOP) {
1489 target++;
1490 }
1491
1492 /* next block is only NOP's */
1493 if (target == target_end) {
1494 target_block = target_block->follow_to;
1495 goto next_target;
1496 } else if (target->opcode == INV_COND(last_op->opcode) &&
1497 /* JMPZ(X, L), L: JMPNZ(X, L2) -> JMPZ(X, L+1) */
1498 (ZEND_OP1_TYPE(target) & (IS_TMP_VAR|IS_CV)) &&
1499 same_type == ZEND_OP1_TYPE(target) &&
1500 same_var == VAR_NUM_EX(target->op1) &&
1501 target_block->follow_to &&
1502 !target_block->protected
1503 ) {
1504 del_source(block, block->op2_to);
1505 block->op2_to = target_block->follow_to;
1506 ADD_SOURCE(block, block->op2_to);
1507 } else if (target->opcode == INV_COND_EX(last_op->opcode) &&
1508 (ZEND_OP1_TYPE(target) & (IS_TMP_VAR|IS_CV)) &&
1509 same_type == ZEND_OP1_TYPE(target) &&
1510 same_var == VAR_NUM_EX(target->op1) &&
1511 target_block->follow_to &&
1512 !target_block->protected) {
1513 /* JMPZ(X, L), L: T = JMPNZ_EX(X, L2) -> T = JMPZ_EX(X, L+1) */
1514 last_op->opcode += 3;
1515 COPY_NODE(last_op->result, target->result);
1516 del_source(block, block->op2_to);
1517 block->op2_to = target_block->follow_to;
1518 ADD_SOURCE(block, block->op2_to);
1519 } else if (target_block->op2_to &&
1520 target->opcode == last_op->opcode &&
1521 (ZEND_OP1_TYPE(target) & (IS_TMP_VAR|IS_CV)) &&
1522 same_type == ZEND_OP1_TYPE(target) &&
1523 same_var == VAR_NUM_EX(target->op1) &&
1524 !target_block->protected) {
1525 /* JMPZ(X, L), L: JMPZ(X, L2) -> JMPZ(X, L2) */
1526 del_source(block, block->op2_to);
1527 block->op2_to = target_block->op2_to;
1528 ADD_SOURCE(block, block->op2_to);
1529 } else if (target_block->op1_to &&
1530 target->opcode == ZEND_JMP &&
1531 !target_block->protected) {
1532 /* JMPZ(X, L), L: JMP(L2) -> JMPZ(X, L2) */
1533 del_source(block, block->op2_to);
1534 block->op2_to = target_block->op1_to;
1535 ADD_SOURCE(block, block->op2_to);
1536 } else if (target_block->op2_to &&
1537 target_block->ext_to &&
1538 target->opcode == ZEND_JMPZNZ &&
1539 (ZEND_OP1_TYPE(target) & (IS_TMP_VAR|IS_CV)) &&
1540 same_type == ZEND_OP1_TYPE(target) &&
1541 same_var == VAR_NUM_EX(target->op1) &&
1542 !target_block->protected) {
1543 /* JMPZ(X, L), L: JMPZNZ(X, L2, L3) -> JMPZ(X, L2) */
1544 del_source(block, block->op2_to);
1545 if (last_op->opcode == ZEND_JMPZ) {
1546 block->op2_to = target_block->op2_to;
1547 } else {
1548 block->op2_to = target_block->ext_to;
1549 }
1550 ADD_SOURCE(block, block->op2_to);
1551 }
1552 }
1553
1554 if (block->follow_to &&
1555 (last_op->opcode == ZEND_JMPZ || last_op->opcode == ZEND_JMPNZ)) {
1556 zend_op *target;
1557 zend_op *target_end;
1558
1559 while (1) {
1560 target = block->follow_to->start_opline;
1561 target_end = block->follow_to->start_opline + block->follow_to->len;
1562 while (target < target_end && target->opcode == ZEND_NOP) {
1563 target++;
1564 }
1565
1566 /* next block is only NOP's */
1567 if (target == target_end && ! block->follow_to->protected) {
1568 del_source(block, block->follow_to);
1569 block->follow_to = block->follow_to->follow_to;
1570 ADD_SOURCE(block, block->follow_to);
1571 } else {
1572 break;
1573 }
1574 }
1575 /* JMPZ(X,L1), JMP(L2) -> JMPZNZ(X,L1,L2) */
1576 if (target->opcode == ZEND_JMP &&
1577 block->follow_to->op1_to &&
1578 !block->follow_to->protected) {
1579 del_source(block, block->follow_to);
1580 if (last_op->opcode == ZEND_JMPZ) {
1581 block->ext_to = block->follow_to->op1_to;
1582 ADD_SOURCE(block, block->ext_to);
1583 } else {
1584 block->ext_to = block->op2_to;
1585 block->op2_to = block->follow_to->op1_to;
1586 ADD_SOURCE(block, block->op2_to);
1587 }
1588 block->follow_to = NULL;
1589 last_op->opcode = ZEND_JMPZNZ;
1590 }
1591 }
1592 break;
1593
1594 case ZEND_JMPNZ_EX:
1595 case ZEND_JMPZ_EX:
1596 /* constant conditional JMPs */
1597 if (ZEND_OP1_TYPE(last_op) == IS_CONST) {
1598 int should_jmp = zend_is_true(&ZEND_OP1_LITERAL(last_op));
1599
1600 if (last_op->opcode == ZEND_JMPZ_EX) {
1601 should_jmp = !should_jmp;
1602 }
1603 if (!should_jmp) {
1604 /* T = JMPZ_EX(true,L) -> T = QM_ASSIGN(true)
1605 * T = JMPNZ_EX(false,L) -> T = QM_ASSIGN(false)
1606 */
1607 last_op->opcode = ZEND_QM_ASSIGN;
1608 SET_UNUSED(last_op->op2);
1609 del_source(block, block->op2_to);
1610 block->op2_to = NULL;
1611 }
1612 break;
1613 }
1614
1615 if (block->op2_to) {
1616 zend_op *target, *target_end;
1617 char *same_t=NULL;
1618 zend_code_block *target_block;
1619 int var_num = op_array->last_var + op_array->T;
1620
1621 if (var_num <= 0) {
1622 return;
1623 }
1624 same_t = cfg->same_t;
1625 memset(same_t, 0, var_num);
1626 same_t[VAR_NUM_EX(last_op->op1)] |= ZEND_OP1_TYPE(last_op);
1627 same_t[VAR_NUM_EX(last_op->result)] |= ZEND_RESULT_TYPE(last_op);
1628 target_block = block->op2_to;
1629 next_target_ex:
1630 target = target_block->start_opline;
1631 target_end = target_block->start_opline + target_block->len;
1632 while (target < target_end && target->opcode == ZEND_NOP) {
1633 target++;
1634 }
1635 /* next block is only NOP's */
1636 if (target == target_end) {
1637 target_block = target_block->follow_to;
1638 goto next_target_ex;
1639 } else if (target_block->op2_to &&
1640 target->opcode == last_op->opcode-3 &&
1641 (ZEND_OP1_TYPE(target) & (IS_TMP_VAR|IS_CV)) &&
1642 (same_t[VAR_NUM_EX(target->op1)] & ZEND_OP1_TYPE(target)) != 0 &&
1643 !target_block->protected) {
1644 /* T = JMPZ_EX(X, L1), L1: JMPZ({X|T}, L2) -> T = JMPZ_EX(X, L2) */
1645 del_source(block, block->op2_to);
1646 block->op2_to = target_block->op2_to;
1647 ADD_SOURCE(block, block->op2_to);
1648 } else if (target_block->op2_to &&
1649 target->opcode == INV_EX_COND(last_op->opcode) &&
1650 (ZEND_OP1_TYPE(target) & (IS_TMP_VAR|IS_CV)) &&
1651 (same_t[VAR_NUM_EX(target->op1)] & ZEND_OP1_TYPE(target)) != 0 &&
1652 !target_block->protected) {
1653 /* T = JMPZ_EX(X, L1), L1: JMPNZ({X|T1}, L2) -> T = JMPZ_EX(X, L1+1) */
1654 del_source(block, block->op2_to);
1655 block->op2_to = target_block->follow_to;
1656 ADD_SOURCE(block, block->op2_to);
1657 } else if (target_block->op2_to &&
1658 target->opcode == INV_EX_COND_EX(last_op->opcode) &&
1659 (ZEND_OP1_TYPE(target) & (IS_TMP_VAR|IS_CV)) &&
1660 (same_t[VAR_NUM_EX(target->op1)] & ZEND_OP1_TYPE(target)) != 0 &&
1661 (same_t[VAR_NUM_EX(target->result)] & ZEND_RESULT_TYPE(target)) != 0 &&
1662 !target_block->protected) {
1663 /* T = JMPZ_EX(X, L1), L1: T = JMPNZ_EX(T, L2) -> T = JMPZ_EX(X, L1+1) */
1664 del_source(block, block->op2_to);
1665 block->op2_to = target_block->follow_to;
1666 ADD_SOURCE(block, block->op2_to);
1667 } else if (target_block->op2_to &&
1668 target->opcode == last_op->opcode &&
1669 (ZEND_OP1_TYPE(target) & (IS_TMP_VAR|IS_CV)) &&
1670 (same_t[VAR_NUM_EX(target->op1)] & ZEND_OP1_TYPE(target)) != 0 &&
1671 (same_t[VAR_NUM_EX(target->result)] & ZEND_RESULT_TYPE(target)) != 0 &&
1672 !target_block->protected) {
1673 /* T = JMPZ_EX(X, L1), L1: T = JMPZ({X|T}, L2) -> T = JMPZ_EX(X, L2) */
1674 del_source(block, block->op2_to);
1675 block->op2_to = target_block->op2_to;
1676 ADD_SOURCE(block, block->op2_to);
1677 } else if (target_block->op1_to &&
1678 target->opcode == ZEND_JMP &&
1679 !target_block->protected) {
1680 /* T = JMPZ_EX(X, L), L: JMP(L2) -> T = JMPZ(X, L2) */
1681 del_source(block, block->op2_to);
1682 block->op2_to = target_block->op1_to;
1683 ADD_SOURCE(block, block->op2_to);
1684 } else if (target_block->op2_to &&
1685 target_block->ext_to &&
1686 target->opcode == ZEND_JMPZNZ &&
1687 (ZEND_OP1_TYPE(target) & (IS_TMP_VAR|IS_CV)) &&
1688 (same_t[VAR_NUM_EX(target->op1)] & ZEND_OP1_TYPE(target)) != 0 &&
1689 !target_block->protected) {
1690 /* T = JMPZ_EX(X, L), L: JMPZNZ({X|T}, L2, L3) -> T = JMPZ_EX(X, L2) */
1691 del_source(block, block->op2_to);
1692 if (last_op->opcode == ZEND_JMPZ_EX) {
1693 block->op2_to = target_block->op2_to;
1694 } else {
1695 block->op2_to = target_block->ext_to;
1696 }
1697 ADD_SOURCE(block, block->op2_to);
1698 }
1699 }
1700 break;
1701
1702 case ZEND_JMPZNZ: {
1703 zend_code_block *next = block->next;
1704
1705 while (next && !next->access) {
1706 /* find first accessed one */
1707 next = next->next;
1708 }
1709
1710 if (ZEND_OP1_TYPE(last_op) == IS_CONST) {
1711 if (!zend_is_true(&ZEND_OP1_LITERAL(last_op))) {
1712 /* JMPZNZ(false,L1,L2) -> JMP(L1) */
1713 zend_code_block *todel;
1714
1715 literal_dtor(&ZEND_OP1_LITERAL(last_op));
1716 last_op->opcode = ZEND_JMP;
1717 SET_UNUSED(last_op->op1);
1718 SET_UNUSED(last_op->op2);
1719 block->op1_to = block->op2_to;
1720 todel = block->ext_to;
1721 block->op2_to = NULL;
1722 block->ext_to = NULL;
1723 del_source(block, todel);
1724 } else {
1725 /* JMPZNZ(true,L1,L2) -> JMP(L2) */
1726 zend_code_block *todel;
1727
1728 literal_dtor(&ZEND_OP1_LITERAL(last_op));
1729 last_op->opcode = ZEND_JMP;
1730 SET_UNUSED(last_op->op1);
1731 SET_UNUSED(last_op->op2);
1732 block->op1_to = block->ext_to;
1733 todel = block->op2_to;
1734 block->op2_to = NULL;
1735 block->ext_to = NULL;
1736 del_source(block, todel);
1737 }
1738 } else if (block->op2_to == block->ext_to) {
1739 /* both goto the same one - it's JMP */
1740 if (!(last_op->op1_type & (IS_VAR|IS_TMP_VAR))) {
1741 /* JMPZNZ(?,L,L) -> JMP(L) */
1742 last_op->opcode = ZEND_JMP;
1743 SET_UNUSED(last_op->op1);
1744 SET_UNUSED(last_op->op2);
1745 block->op1_to = block->op2_to;
1746 block->op2_to = NULL;
1747 block->ext_to = NULL;
1748 }
1749 } else if (block->op2_to == next) {
1750 /* jumping to next on Z - can follow to it and jump only on NZ */
1751 /* JMPZNZ(X,L1,L2) L1: -> JMPNZ(X,L2) */
1752 last_op->opcode = ZEND_JMPNZ;
1753 block->op2_to = block->ext_to;
1754 block->follow_to = next;
1755 block->ext_to = NULL;
1756 /* no need to add source - it's block->op2_to */
1757 } else if (block->ext_to == next) {
1758 /* jumping to next on NZ - can follow to it and jump only on Z */
1759 /* JMPZNZ(X,L1,L2) L2: -> JMPZ(X,L1) */
1760 last_op->opcode = ZEND_JMPZ;
1761 block->follow_to = next;
1762 block->ext_to = NULL;
1763 /* no need to add source - it's block->ext_to */
1764 }
1765
1766 if (last_op->opcode == ZEND_JMPZNZ && block->op2_to) {
1767 zend_uchar same_type = ZEND_OP1_TYPE(last_op);
1768 zend_uchar same_var = VAR_NUM_EX(last_op->op1);
1769 zend_op *target;
1770 zend_op *target_end;
1771 zend_code_block *target_block = block->op2_to;
1772
1773 next_target_znz:
1774 target = target_block->start_opline;
1775 target_end = target_block->start_opline + target_block->len;
1776 while (target < target_end && target->opcode == ZEND_NOP) {
1777 target++;
1778 }
1779 /* next block is only NOP's */
1780 if (target == target_end) {
1781 target_block = target_block->follow_to;
1782 goto next_target_znz;
1783 } else if (target_block->op2_to &&
1784 (target->opcode == ZEND_JMPZ || target->opcode == ZEND_JMPZNZ) &&
1785 (ZEND_OP1_TYPE(target) & (IS_TMP_VAR|IS_CV)) &&
1786 same_type == ZEND_OP1_TYPE(target) &&
1787 same_var == VAR_NUM_EX(target->op1) &&
1788 !target_block->protected) {
1789 /* JMPZNZ(X, L1, L2), L1: JMPZ(X, L3) -> JMPZNZ(X, L3, L2) */
1790 del_source(block, block->op2_to);
1791 block->op2_to = target_block->op2_to;
1792 ADD_SOURCE(block, block->op2_to);
1793 } else if (target->opcode == ZEND_JMPNZ &&
1794 (ZEND_OP1_TYPE(target) & (IS_TMP_VAR|IS_CV)) &&
1795 same_type == ZEND_OP1_TYPE(target) &&
1796 same_var == VAR_NUM_EX(target->op1) &&
1797 target_block->follow_to &&
1798 !target_block->protected) {
1799 /* JMPZNZ(X, L1, L2), L1: X = JMPNZ(X, L3) -> JMPZNZ(X, L1+1, L2) */
1800 del_source(block, block->op2_to);
1801 block->op2_to = target_block->follow_to;
1802 ADD_SOURCE(block, block->op2_to);
1803 } else if (target_block->op1_to &&
1804 target->opcode == ZEND_JMP &&
1805 !target_block->protected) {
1806 /* JMPZNZ(X, L1, L2), L1: JMP(L3) -> JMPZNZ(X, L3, L2) */
1807 del_source(block, block->op2_to);
1808 block->op2_to = target_block->op1_to;
1809 ADD_SOURCE(block, block->op2_to);
1810 }
1811 }
1812 break;
1813 }
1814 }
1815 }
1816
1817 /* Global data dependencies */
1818
1819 #define T_USAGE(op) do { \
1820 if ((op ## _type & (IS_VAR | IS_TMP_VAR)) && \
1821 !zend_bitset_in(defined_here, VAR_NUM(op.var)) && !zend_bitset_in(used_ext, VAR_NUM(op.var))) { \
1822 zend_bitset_incl(used_ext, VAR_NUM(op.var)); \
1823 } \
1824 } while (0)
1825
1826 #define NEVER_USED(op) ((op ## _type & (IS_VAR | IS_TMP_VAR)) && !zend_bitset_in(usage, VAR_NUM(op.var))) /* !zend_bitset_in(used_ext, op.var) && */
1827 #define RES_NEVER_USED(opline) (opline->result_type == IS_UNUSED || NEVER_USED(opline->result))
1828
1829 /* Find a set of variables which are used outside of the block where they are
1830 * defined. We won't apply some optimization patterns for such variables. */
zend_t_usage(zend_code_block * block,zend_op_array * op_array,zend_bitset used_ext,zend_optimizer_ctx * ctx)1831 static void zend_t_usage(zend_code_block *block, zend_op_array *op_array, zend_bitset used_ext, zend_optimizer_ctx *ctx)
1832 {
1833 zend_code_block *next_block = block->next;
1834 uint32_t bitset_len;
1835 zend_bitset usage;
1836 zend_bitset defined_here;
1837 void *checkpoint;
1838
1839 if (op_array->T == 0) {
1840 /* shortcut - if no Ts, nothing to do */
1841 return;
1842 }
1843
1844 checkpoint = zend_arena_checkpoint(ctx->arena);
1845 bitset_len = zend_bitset_len(op_array->last_var + op_array->T);
1846 usage = zend_arena_alloc(&ctx->arena, bitset_len * ZEND_BITSET_ELM_SIZE);
1847 zend_bitset_clear(usage, bitset_len);
1848 defined_here = zend_arena_alloc(&ctx->arena, bitset_len * ZEND_BITSET_ELM_SIZE);
1849
1850 while (next_block) {
1851 zend_op *opline = next_block->start_opline;
1852 zend_op *end = opline + next_block->len;
1853
1854 if (!next_block->access) {
1855 next_block = next_block->next;
1856 continue;
1857 }
1858 zend_bitset_clear(defined_here, bitset_len);
1859
1860 while (opline<end) {
1861 T_USAGE(opline->op1);
1862 if (opline->op2_type & (IS_VAR | IS_TMP_VAR)) {
1863 if (opline->opcode == ZEND_FE_FETCH_R || opline->opcode == ZEND_FE_FETCH_RW) {
1864 /* these opcode use the op2 as result */
1865 zend_bitset_incl(defined_here, VAR_NUM(ZEND_OP2(opline).var));
1866 } else {
1867 T_USAGE(opline->op2);
1868 }
1869 }
1870
1871 if (RESULT_USED(opline)) {
1872 if (!zend_bitset_in(defined_here, VAR_NUM(ZEND_RESULT(opline).var)) && !zend_bitset_in(used_ext, VAR_NUM(ZEND_RESULT(opline).var)) &&
1873 opline->opcode == ZEND_ADD_ARRAY_ELEMENT) {
1874 /* these opcode use the result as argument */
1875 zend_bitset_incl(used_ext, VAR_NUM(ZEND_RESULT(opline).var));
1876 }
1877 zend_bitset_incl(defined_here, VAR_NUM(ZEND_RESULT(opline).var));
1878 }
1879 opline++;
1880 }
1881 next_block = next_block->next;
1882 }
1883
1884 #if DEBUG_BLOCKPASS
1885 {
1886 int i;
1887 for (i = op_array->last_var; i< op_array->T; i++) {
1888 fprintf(stderr, "T%d: %c\n", i, zend_bitset_in(used_ext, i) + '0');
1889 }
1890 }
1891 #endif
1892
1893 while (block) {
1894 zend_op *opline = block->start_opline + block->len - 1;
1895
1896 if (!block->access) {
1897 block = block->next;
1898 continue;
1899 }
1900
1901 zend_bitset_copy(usage, used_ext, bitset_len);
1902
1903 while (opline >= block->start_opline) {
1904 /* usage checks */
1905 if (RES_NEVER_USED(opline)) {
1906 switch (opline->opcode) {
1907 case ZEND_ASSIGN_ADD:
1908 case ZEND_ASSIGN_SUB:
1909 case ZEND_ASSIGN_MUL:
1910 case ZEND_ASSIGN_DIV:
1911 case ZEND_ASSIGN_POW:
1912 case ZEND_ASSIGN_MOD:
1913 case ZEND_ASSIGN_SL:
1914 case ZEND_ASSIGN_SR:
1915 case ZEND_ASSIGN_CONCAT:
1916 case ZEND_ASSIGN_BW_OR:
1917 case ZEND_ASSIGN_BW_AND:
1918 case ZEND_ASSIGN_BW_XOR:
1919 case ZEND_PRE_INC:
1920 case ZEND_PRE_DEC:
1921 case ZEND_POST_INC:
1922 case ZEND_POST_DEC:
1923 case ZEND_ASSIGN:
1924 case ZEND_ASSIGN_REF:
1925 case ZEND_DO_FCALL:
1926 case ZEND_DO_ICALL:
1927 case ZEND_DO_UCALL:
1928 case ZEND_DO_FCALL_BY_NAME:
1929 if (ZEND_RESULT_TYPE(opline) == IS_VAR) {
1930 ZEND_RESULT_TYPE(opline) |= EXT_TYPE_UNUSED;
1931 }
1932 break;
1933 case ZEND_QM_ASSIGN:
1934 case ZEND_BOOL:
1935 case ZEND_BOOL_NOT:
1936 if (ZEND_OP1_TYPE(opline) == IS_TMP_VAR) {
1937 opline->opcode = ZEND_FREE;
1938 } else {
1939 if (ZEND_OP1_TYPE(opline) == IS_CONST) {
1940 literal_dtor(&ZEND_OP1_LITERAL(opline));
1941 }
1942 MAKE_NOP(opline);
1943 }
1944 break;
1945 case ZEND_JMPZ_EX:
1946 case ZEND_JMPNZ_EX:
1947 opline->opcode -= 3;
1948 SET_UNUSED(opline->result);
1949 break;
1950 }
1951 }
1952
1953 if (opline->opcode == ZEND_ADD_ARRAY_ELEMENT) {
1954 if (ZEND_OP1_TYPE(opline) == IS_VAR || ZEND_OP1_TYPE(opline) == IS_TMP_VAR) {
1955 zend_bitset_incl(usage, VAR_NUM(ZEND_RESULT(opline).var));
1956 }
1957 } else {
1958 if (RESULT_USED(opline)) {
1959 zend_bitset_excl(usage, VAR_NUM(ZEND_RESULT(opline).var));
1960 }
1961 }
1962
1963 if (ZEND_OP1_TYPE(opline) == IS_VAR || ZEND_OP1_TYPE(opline) == IS_TMP_VAR) {
1964 zend_bitset_incl(usage, VAR_NUM(ZEND_OP1(opline).var));
1965 }
1966
1967 if (ZEND_OP2_TYPE(opline) == IS_VAR || ZEND_OP2_TYPE(opline) == IS_TMP_VAR) {
1968 zend_bitset_incl(usage, VAR_NUM(ZEND_OP2(opline).var));
1969 }
1970
1971 if ((ZEND_RESULT_TYPE(opline) & IS_VAR) &&
1972 (ZEND_RESULT_TYPE(opline) & EXT_TYPE_UNUSED) &&
1973 zend_bitset_in(usage, VAR_NUM(ZEND_RESULT(opline).var))) {
1974 ZEND_RESULT_TYPE(opline) &= ~EXT_TYPE_UNUSED;
1975 }
1976
1977 opline--;
1978 }
1979 block = block->next;
1980 } /* end blocks */
1981
1982 zend_arena_release(&ctx->arena, checkpoint);
1983 }
1984
1985 #define PASSES 3
1986
optimize_cfg(zend_op_array * op_array,zend_optimizer_ctx * ctx)1987 void optimize_cfg(zend_op_array *op_array, zend_optimizer_ctx *ctx)
1988 {
1989 zend_cfg cfg;
1990 zend_code_block *cur_block;
1991 int pass;
1992 uint32_t bitset_len;
1993 zend_bitset usage;
1994 void *checkpoint;
1995
1996 #if DEBUG_BLOCKPASS
1997 fprintf(stderr, "File %s func %s\n", op_array->filename, op_array->function_name? op_array->function_name : "main");
1998 fflush(stderr);
1999 #endif
2000
2001 if (op_array->fn_flags & ZEND_ACC_HAS_FINALLY_BLOCK) {
2002 return;
2003 }
2004
2005 if ((uint64_t) op_array->last * (op_array->last_var + op_array->T) > 512 * 1024 * 1024) {
2006 return;
2007 }
2008
2009 /* Build CFG */
2010 checkpoint = zend_arena_checkpoint(ctx->arena);
2011 if (!find_code_blocks(op_array, &cfg, ctx)) {
2012 zend_arena_release(&ctx->arena, checkpoint);
2013 return;
2014 }
2015
2016 zend_rebuild_access_path(&cfg, op_array, 0, ctx);
2017 /* full rebuild here to produce correct sources! */
2018 if (op_array->last_var || op_array->T) {
2019 bitset_len = zend_bitset_len(op_array->last_var + op_array->T);
2020 cfg.Tsource = zend_arena_calloc(&ctx->arena, op_array->last_var + op_array->T, sizeof(zend_op *));
2021 cfg.same_t = zend_arena_alloc(&ctx->arena, op_array->last_var + op_array->T);
2022 usage = zend_arena_alloc(&ctx->arena, bitset_len * ZEND_BITSET_ELM_SIZE);
2023 } else {
2024 bitset_len = 0;
2025 cfg.Tsource = NULL;
2026 cfg.same_t = NULL;
2027 usage = NULL;
2028 }
2029 for (pass = 0; pass < PASSES; pass++) {
2030 /* Compute data dependencies */
2031 zend_bitset_clear(usage, bitset_len);
2032 zend_t_usage(cfg.blocks, op_array, usage, ctx);
2033
2034 /* optimize each basic block separately */
2035 for (cur_block = cfg.blocks; cur_block; cur_block = cur_block->next) {
2036 if (!cur_block->access) {
2037 continue;
2038 }
2039 zend_optimize_block(cur_block, op_array, usage, &cfg, ctx);
2040 }
2041
2042 /* Jump optimization for each block */
2043 for (cur_block = cfg.blocks; cur_block; cur_block = cur_block->next) {
2044 if (!cur_block->access) {
2045 continue;
2046 }
2047 zend_jmp_optimization(cur_block, op_array, cfg.blocks, &cfg, ctx);
2048 }
2049
2050 /* Eliminate unreachable basic blocks */
2051 zend_rebuild_access_path(&cfg, op_array, 1, ctx);
2052 }
2053
2054 zend_bitset_clear(usage, bitset_len);
2055 zend_t_usage(cfg.blocks, op_array, usage, ctx);
2056 assemble_code_blocks(&cfg, op_array);
2057
2058 /* Destroy CFG */
2059 zend_arena_release(&ctx->arena, checkpoint);
2060 }
2061