root/ext/opcache/Optimizer/block_pass.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. zend_get_persistent_constant
  2. print_block
  3. find_code_blocks
  4. replace_source
  5. del_source
  6. delete_code_block
  7. zend_access_path
  8. zend_rebuild_access_path
  9. strip_nop
  10. zend_optimize_block
  11. assemble_code_blocks
  12. zend_jmp_optimization
  13. zend_t_usage
  14. zend_block_optimization

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

/* [<][>][^][v][top][bottom][index][help] */