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