/* +----------------------------------------------------------------------+ | eAccelerator project | +----------------------------------------------------------------------+ | Copyright (c) 2004 - 2006 eAccelerator | | http://eaccelerator.net | +----------------------------------------------------------------------+ | This program is free software; you can redistribute it and/or | | modify it under the terms of the GNU General Public License | | as published by the Free Software Foundation; either version 2 | | of the License, or (at your option) any later version. | | | | This program is distributed in the hope that it will be useful, | | but WITHOUT ANY WARRANTY; without even the implied warranty of | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | | GNU General Public License for more details. | | | | You should have received a copy of the GNU General Public License | | along with this program; if not, write to the Free Software | | Foundation, Inc., 59 Temple Place - Suite 330, Boston, | | MA 02111-1307, USA. | | | | A copy is availble at http://www.gnu.org/copyleft/gpl.txt | +----------------------------------------------------------------------+ $Id: optimize.c 176 2006-03-05 12:18:54Z bart $ */ #include "eaccelerator.h" #ifdef HAVE_EACCELERATOR #ifdef WITH_EACCELERATOR_OPTIMIZER #include "zend.h" #include "zend_API.h" #include "zend_constants.h" #include "opcodes.h" typedef unsigned int* set; struct _BBlink; typedef struct _BB { zend_op* start; int len; int used; /* * HOESH: To protect merging. Primary * it abblies to try & catch blocks. * ZEND_ENGINE_2 specific, but can take place */ int protect_merge; struct _BB* jmp_1; struct _BB* jmp_2; struct _BB* jmp_ext; struct _BB* follow; struct _BBlink* pred; // Gonna be a chain of BBs struct _BB* next; } BB; typedef struct _BBlink { struct _BB* bb; struct _BBlink* next; } BBlink; #if 0 static void dump_bb(BB* bb, zend_op_array *op_array) { BB* p = bb; BBlink *q; zend_printf("
%s:%s\n", op_array->filename, op_array->function_name);
  while (p != NULL) {
    zend_printf("  bb%u start=%u len=%d used=%d\n",
                 (unsigned int)(p-bb),
                 (unsigned int)(p->start-op_array->opcodes),
                 p->len,
                 p->used);
    if (p->jmp_1) {
      zend_printf("    jmp_1 bb%u start=%u  len=%d used=%d\n",
                  (unsigned int)(p->jmp_1-bb),
                  (unsigned int)(p->jmp_1->start-op_array->opcodes),
                  p->jmp_1->len,
                  p->jmp_1->used);
    }
    if (p->jmp_2) {
      zend_printf("    jmp_2 bb%u start=%u  len=%d used=%d\n",
                  (unsigned int)(p->jmp_2-bb),
                  (unsigned int)(p->jmp_2->start-op_array->opcodes),
                  p->jmp_2->len,
                  p->jmp_2->used);
    }
    if (p->jmp_ext) {
      zend_printf("    jmp_ext bb%u start=%u  len=%d used=%d\n",
                  (unsigned int)(p->jmp_ext-bb),
                  (unsigned int)(p->jmp_ext->start-op_array->opcodes),
                  p->jmp_ext->len,
                  p->jmp_ext->used);
    }
    if (p->follow) {
      zend_printf("    follow bb%u start=%u  len=%d used=%d\n",
                  (unsigned int)(p->follow-bb),
                  (unsigned int)(p->follow->start-op_array->opcodes),
                  p->follow->len,
                  p->follow->used);
    }
    q = p->pred;
    while (q != NULL) {
      zend_printf("    pred bb%u start=%u  len=%d used=%d (",
                  (unsigned int)(q->bb-bb),
                  (unsigned int)(q->bb->start-op_array->opcodes),
                  q->bb->len,
                  q->bb->used);
      if (q->bb->jmp_1 == p) {
        zend_printf("jmp_1 ");
      }
      if (q->bb->jmp_2 == p) {
        zend_printf("jmp_2 ");
      }
      if (q->bb->jmp_ext == p) {
        zend_printf("jmp_ext ");
      }
      if (q->bb->follow == p) {
        zend_printf("follow ");
      }
      zend_printf(")\n");
      q = q->next;
    }
    p = p->next;
  }
  zend_printf("

\n"); fflush(stdout); } static void dump_array(int nb,void *pos,char type) { int j; switch(type) { case 'i': { int *ptr=pos; for (j=0;j=32 && *ptr<128) zend_printf("%d:%c",j,*ptr); else if (*ptr>=128) zend_printf("%d:%2x",j,*ptr); else if (*ptr<16) zend_printf("%d:&%1x",j,*ptr); else zend_printf("%d:$%1x",j,(*ptr)-16); */ zend_printf("%d:%1x ",j,*ptr); ptr++; } } break; default: for (j=0;j\n"); } #endif #define SET_TO_NOP(op) \ (op)->opcode = ZEND_NOP; \ (op)->op1.op_type = IS_UNUSED; \ (op)->op2.op_type = IS_UNUSED; \ (op)->result.op_type = IS_UNUSED; static void compute_live_var(BB* bb, zend_op_array* op_array, char* global) { BB* p = bb; memset(global, 0, op_array->T * sizeof(char)); if (p != NULL && p->next != NULL) { int bb_count = 0; char *def = do_alloca(op_array->T * sizeof(char)); #if 0 zend_printf("
%s::%s
", op_array->filename, op_array->function_name); #endif while (p != NULL) { zend_op* op = p->start; zend_op* end = op + p->len; memset(def, 0, op_array->T * sizeof(char)); while (op < end) { if ((op->op1.op_type == IS_VAR || op->op1.op_type == IS_TMP_VAR) && !def[VAR_NUM(op->op1.u.var)] && !global[VAR_NUM(op->op1.u.var)]) { global[VAR_NUM(op->op1.u.var)] = 1; } if ((op->op2.op_type == IS_VAR || op->op2.op_type == IS_TMP_VAR) && !def[VAR_NUM(op->op2.u.var)] && !global[VAR_NUM(op->op2.u.var)]) { #ifdef ZEND_ENGINE_2 if (op->opcode != ZEND_OP_DATA) { global[VAR_NUM(op->op2.u.var)] = 1; } #else global[VAR_NUM(op->op2.u.var)] = 1; #endif } #ifdef ZEND_ENGINE_2 if (op->opcode == ZEND_DECLARE_INHERITED_CLASS && !def[VAR_NUM(op->extended_value)] && !global[VAR_NUM(op->extended_value)]) { global[VAR_NUM(op->extended_value)] = 1; } #endif if ((op->result.op_type == IS_VAR && #ifdef ZEND_ENGINE_2 (op->opcode == ZEND_RECV || op->opcode == ZEND_RECV_INIT || (op->result.u.EA.type & EXT_TYPE_UNUSED) == 0)) || #else (op->result.u.EA.type & EXT_TYPE_UNUSED) == 0) || #endif (op->result.op_type == IS_TMP_VAR)) { if (!def[VAR_NUM(op->result.u.var)] && !global[VAR_NUM(op->result.u.var)]) { switch (op->opcode) { case ZEND_RECV: case ZEND_RECV_INIT: case ZEND_ADD_ARRAY_ELEMENT: global[VAR_NUM(op->result.u.var)] = 1; } } def[VAR_NUM(op->result.u.var)] = 1; } op++; } p = p->next; bb_count++; } free_alloca(def); } { char *used = do_alloca(op_array->T * sizeof(char)); p = bb; while (p != NULL) { zend_op* op = p->start; zend_op* end = op + p->len; memset(used, 0, op_array->T * sizeof(char)); while (op < end) { end--; if (((end->result.op_type == IS_VAR && #ifdef ZEND_ENGINE_2 (end->opcode == ZEND_RECV || end->opcode == ZEND_RECV_INIT || (end->result.u.EA.type & EXT_TYPE_UNUSED) == 0)) || #else (end->result.u.EA.type & EXT_TYPE_UNUSED) == 0) || #endif (end->result.op_type == IS_TMP_VAR)) && !global[VAR_NUM(end->result.u.var)] && !used[VAR_NUM(end->result.u.var)]) { switch(end->opcode) { case ZEND_JMPZ_EX: end->opcode = ZEND_JMPZ; end->result.op_type = IS_UNUSED; break; case ZEND_JMPNZ_EX: end->opcode = ZEND_JMPNZ; end->result.op_type = IS_UNUSED; break; case ZEND_ASSIGN_ADD: case ZEND_ASSIGN_SUB: case ZEND_ASSIGN_MUL: case ZEND_ASSIGN_DIV: case ZEND_ASSIGN_MOD: case ZEND_ASSIGN_SL: case ZEND_ASSIGN_SR: case ZEND_ASSIGN_CONCAT: case ZEND_ASSIGN_BW_OR: case ZEND_ASSIGN_BW_AND: case ZEND_ASSIGN_BW_XOR: case ZEND_PRE_INC: case ZEND_PRE_DEC: case ZEND_POST_INC: case ZEND_POST_DEC: case ZEND_ASSIGN: case ZEND_ASSIGN_REF: case ZEND_DO_FCALL: case ZEND_DO_FCALL_BY_NAME: if (end->result.op_type == IS_VAR) { end->result.u.EA.type |= EXT_TYPE_UNUSED; } break; case ZEND_UNSET_VAR: #ifndef ZEND_ENGINE_2_1 /* Pre-PHP 5.1 only */ case ZEND_UNSET_DIM_OBJ: end->result.op_type = IS_UNUSED; break; #else case ZEND_UNSET_DIM: case ZEND_UNSET_OBJ: end->result.op_type = IS_UNUSED; break; #endif case ZEND_RECV: case ZEND_RECV_INIT: /*case ZEND_ADD_ARRAY_ELEMENT:*/ case ZEND_INCLUDE_OR_EVAL: #ifndef ZEND_ENGINE_2_1 /* Pre-PHP 5.1 only */ case ZEND_JMP_NO_CTOR: #else case ZEND_NEW: #endif case ZEND_FE_FETCH: case ZEND_PRINT: #ifdef ZEND_ENGINE_2 case ZEND_INIT_METHOD_CALL: case ZEND_INIT_STATIC_METHOD_CALL: case ZEND_ASSIGN_DIM: case ZEND_ASSIGN_OBJ: case ZEND_DECLARE_CLASS: case ZEND_DECLARE_INHERITED_CLASS: #endif break; default: if (end->op1.op_type == IS_CONST) { zval_dtor(&end->op1.u.constant); } if (end->op2.op_type == IS_CONST) { zval_dtor(&end->op2.u.constant); } SET_TO_NOP(end); } } else if (end->result.op_type == IS_VAR && (end->result.u.EA.type & EXT_TYPE_UNUSED) != 0 && #ifdef ZEND_ENGINE_2 end->opcode != ZEND_RECV && end->opcode != ZEND_RECV_INIT && #endif used[VAR_NUM(end->result.u.var)]) { end->result.u.EA.type &= ~EXT_TYPE_UNUSED; } if ((end->result.op_type == IS_VAR && #ifdef ZEND_ENGINE_2 (end->opcode == ZEND_RECV || end->opcode == ZEND_RECV_INIT || (end->result.u.EA.type & EXT_TYPE_UNUSED) == 0)) || #else (end->result.u.EA.type & EXT_TYPE_UNUSED) == 0) || #endif (end->result.op_type == IS_TMP_VAR)) { switch (end->opcode) { case ZEND_RECV: case ZEND_RECV_INIT: case ZEND_ADD_ARRAY_ELEMENT: used[VAR_NUM(end->result.u.var)] = 1; break; default: used[VAR_NUM(end->result.u.var)] = 0; } } if (end->op1.op_type == IS_VAR || end->op1.op_type == IS_TMP_VAR) { used[VAR_NUM(end->op1.u.var)] = 1; } if (end->op2.op_type == IS_VAR || end->op2.op_type == IS_TMP_VAR) { used[VAR_NUM(end->op2.u.var)] = 1; } #ifdef ZEND_ENGINE_2 if (end->opcode == ZEND_DECLARE_INHERITED_CLASS) { used[VAR_NUM(end->extended_value)] = 1; } #endif } p = p->next; } free_alloca(used); } /* while (1) { int change = 0; p = bb; while (p != NULL) { int n = SET_SIZE(op_array->T); while (n-- > 0) { unsigned int old_in = p->in[n]; p->out[n] = 0; if (p->jmp_1 != NULL) p->out[n] |= p->jmp_1->in[n]; if (p->jmp_2 != NULL) p->out[n] |= p->jmp_2->in[n]; if (p->jmp_ext != NULL) p->out[n] |= p->jmp_ext->in[n]; if (p->follow != NULL) p->out[n] |= p->follow->in[n]; p->in[n] = p->use[n] | (p->out[n] & ~p->def[n]); if (old_in != p->in[n]) change = 1; } p = p->next; } if (!change) break; } */ } /* Adds FROM as predcessor of TO */ #define BB_ADD_PRED(TO,FROM) { \ BBlink *q = (TO)->pred; \ while (q != NULL) { \ if (q->bb == (FROM)) break; \ q = q->next; \ } \ if (q == NULL) { \ q = emalloc(sizeof(*q)); \ q->bb = (FROM); \ q->next = (TO)->pred; \ (TO)->pred = q; \ } \ } /* Removes FROM from predcessors of TO */ #define BB_DEL_PRED(TO,FROM) { \ BBlink *q = (TO)->pred; \ if (q != NULL) { \ if (q->bb == (FROM)) { \ (TO)->pred = q->next; \ efree(q); \ } else { \ while (q->next != NULL) { \ if (q->next->bb == (FROM)) { \ BBlink *r = q->next; \ q->next = q->next->next; \ efree(r); \ break; \ } \ q = q->next; \ } \ } \ } \ } #define RM_BB(p) do {if (p->pred == NULL && p != bb) rm_bb(p);} while (0) static void mark_used_bb(BB* bb) { if (bb->used) return; bb->used = 1; if (bb->jmp_1 != NULL) { mark_used_bb(bb->jmp_1); BB_ADD_PRED(bb->jmp_1, bb); } if (bb->jmp_2 != NULL) { mark_used_bb(bb->jmp_2); BB_ADD_PRED(bb->jmp_2, bb); } if (bb->jmp_ext != NULL) { mark_used_bb(bb->jmp_ext); BB_ADD_PRED(bb->jmp_ext, bb); } if (bb->follow != NULL) { mark_used_bb(bb->follow); BB_ADD_PRED(bb->follow, bb); } } static void mark_used_bb2(BB* bb) { if (bb->used) return; bb->used = 1; if (bb->jmp_1 != NULL) { mark_used_bb2(bb->jmp_1); } if (bb->jmp_2 != NULL) { mark_used_bb2(bb->jmp_2); } if (bb->jmp_ext != NULL) { mark_used_bb2(bb->jmp_ext); } if (bb->follow != NULL) { mark_used_bb2(bb->follow); } } static void rm_bb(BB* bb) { if (bb->used == 0) { return; } bb->used = 0; if (bb->jmp_1 != NULL) { BB_DEL_PRED(bb->jmp_1, bb); } if (bb->jmp_2 != NULL) { BB_DEL_PRED(bb->jmp_2, bb); } if (bb->jmp_ext != NULL) { BB_DEL_PRED(bb->jmp_ext, bb); } if (bb->follow != NULL) { BB_DEL_PRED(bb->follow, bb); } } static void del_bb(BB* bb) { zend_op* op = bb->start; zend_op* end = op + bb->len; rm_bb(bb); while (op < end) { --end; if (end->op1.op_type == IS_CONST) { zval_dtor(&end->op1.u.constant); } if (end->op2.op_type == IS_CONST) { zval_dtor(&end->op2.u.constant); } SET_TO_NOP(end); } bb->len = 0; bb->used = 0; } static void replace_bb(BB* src, BB* dst) { BBlink* p = src->pred; while (p != NULL) { BBlink* q = p->next; if (p->bb->jmp_1 == src) { p->bb->jmp_1 = dst; BB_ADD_PRED(dst,p->bb); } if (p->bb->jmp_2 == src) { p->bb->jmp_2 = dst; BB_ADD_PRED(dst,p->bb); } if (p->bb->jmp_ext == src) { p->bb->jmp_ext = dst; BB_ADD_PRED(dst,p->bb); } if (p->bb->follow == src) { p->bb->follow = dst; BB_ADD_PRED(dst,p->bb); } efree(p); p = q; } src->pred = NULL; } static void optimize_jmp(BB* bb, zend_op_array* op_array) { BB* p; while(1) { int ok = 1; /* Remove Unused Basic Blocks */ p = bb; while (p->next != NULL) { if (p->next->used && p->next->pred) { p = p->next; } else { del_bb(p->next); p->next = p->next->next; ok = 0; } } /* Clear leading NOP(s) */ p = bb; while (p != NULL) { if (p->used) { while (p->len > 0 && p->start->opcode ==ZEND_NOP) { p->start++; p->len--; } if (p->len == 0 && p != bb) { if (p->follow) { replace_bb(p, p->follow); } rm_bb(p); p->used = 0; ok = 0; } } p = p->next; } /* JMP optimization */ p = bb; while (p != NULL) { while (p->next != NULL && (!p->next->used || p->next->pred == NULL)) { del_bb(p->next); p->next = p->next->next; ok = 0; } if (p->used && p->len > 0) { zend_op* op = &p->start[p->len-1]; switch (op->opcode) { case ZEND_JMP: jmp: /* L1: JMP L1+1 => NOP */ if (p->jmp_1 == p->next) { p->follow = p->jmp_1; p->jmp_1 = NULL; SET_TO_NOP(op); --(p->len); ok = 0; break; } /* JMP L1 => JMP L2 ... ... L1: JMP L2 JMP L2 */ while (p->jmp_1->len == 1 && p->jmp_1->start->opcode == ZEND_JMP && p->jmp_1 != p) { BB* x_p = p->jmp_1; BB_DEL_PRED(p->jmp_1, p); RM_BB(x_p); p->jmp_1 = x_p->jmp_1; BB_ADD_PRED(p->jmp_1, p); ok = 0; } break; case ZEND_JMPZNZ: jmp_znz: /* JMPZNZ ?,L1,L1 => JMP L1 */ if (p->jmp_ext == p->jmp_2) { op->opcode = ZEND_JMP; op->extended_value = 0; op->op1.op_type = IS_UNUSED; op->op2.op_type = IS_UNUSED; p->jmp_1 = p->jmp_2; p->jmp_2 = NULL; p->jmp_ext = NULL; ok = 0; goto jmp; } else if (op->op1.op_type == IS_CONST) { /* JMPZNZ 0,L1,L2 => JMP L1 */ if (!zend_is_true(&op->op1.u.constant)) { op->opcode = ZEND_JMP; op->extended_value = 0; op->op1.op_type = IS_UNUSED; op->op2.op_type = IS_UNUSED; if (p->jmp_ext != p->jmp_2) { BB_DEL_PRED(p->jmp_ext, p); RM_BB(p->jmp_ext); } p->jmp_1 = p->jmp_2; p->jmp_2 = NULL; p->jmp_ext = NULL; p->follow = NULL; ok = 0; goto jmp; /* JMPZNZ 1,L1,L2 => JMP L2 */ } else { op->opcode = ZEND_JMP; op->extended_value = 0; op->op1.op_type = IS_UNUSED; op->op2.op_type = IS_UNUSED; if (p->jmp_ext != p->jmp_2) { BB_DEL_PRED(p->jmp_2, p); RM_BB(p->jmp_2); } p->jmp_1 = p->jmp_ext; p->jmp_2 = NULL; p->jmp_ext = NULL; p->follow = NULL; ok = 0; goto jmp; } /* L1: JMPZNZ ?,L2,L1+1 => JMPZ ?,L2 */ } else if (p->jmp_ext == p->next) { op->opcode = ZEND_JMPZ; op->extended_value = 0; p->follow = p->jmp_ext; p->jmp_ext = NULL; ok = 0; goto jmp_z; /* L1: JMPZNZ ?,L1+1,L2 => JMPNZ ?,L2 */ } else if (p->jmp_2 == p->next) { op->opcode = ZEND_JMPNZ; op->extended_value = 0; p->follow = p->jmp_2; p->jmp_2 = p->jmp_ext; p->jmp_ext = NULL; ok = 0; goto jmp_nz; } else if (p->jmp_2->len == 1 && op->op1.op_type == IS_TMP_VAR) { /* JMPZNZ $x,L1,L2 => JMPZNZ $x,L3,L2 ... ... L1: JMPZ $x,L3 JMPZ $x,L3 */ /* JMPZNZ $x,L1,L2 => JMPZNZ $x,L3,L2 ... ... L1: JMPZNZ $x,L3,L4 JMPZNZ $x,L3,L4 */ if ((p->jmp_2->start->opcode == ZEND_JMPZ || p->jmp_2->start->opcode == ZEND_JMPZNZ) && p->jmp_2->start->op1.op_type == IS_TMP_VAR && op->op1.u.var == p->jmp_2->start->op1.u.var) { if (p->jmp_2 != p->jmp_ext) { BB_DEL_PRED(p->jmp_2, p); RM_BB(p->jmp_2); } p->jmp_2 = p->jmp_2->jmp_2; BB_ADD_PRED(p->jmp_2, p); ok = 0; goto jmp_znz; /* JMPZNZ $x,L1,L2 => JMPZNZ $x,L1+1,L2 ... ... L1: JMPNZ $x,L3 JMPNZ $x,L3 */ } else if (p->jmp_2->start->opcode == ZEND_JMPNZ && p->jmp_2->start->op1.op_type == IS_TMP_VAR && op->op1.u.var == p->jmp_2->start->op1.u.var) { if (p->jmp_2 != p->jmp_ext) { BB_DEL_PRED(p->jmp_2, p); RM_BB(p->jmp_2); } p->jmp_2 = p->jmp_2->follow; BB_ADD_PRED(p->jmp_2, p); ok = 0; goto jmp_znz; /* JMPZNZ $x,L1,L2 => JMPZNZ $x,L1,L3 ... ... L2: JMPNZ $x,L3 JMPNZ $x,L3 */ } else if (p->jmp_ext->start->opcode == ZEND_JMPNZ && p->jmp_ext->start->op1.op_type == IS_TMP_VAR && op->op1.u.var == p->jmp_ext->start->op1.u.var) { if (p->jmp_2 != p->jmp_ext) { BB_DEL_PRED(p->jmp_ext, p); RM_BB(p->jmp_ext); } p->jmp_ext = p->jmp_ext->jmp_2; BB_ADD_PRED(p->jmp_ext, p); ok = 0; goto jmp_znz; /* JMPZNZ $x,L1,L2 => JMPZNZ $x,L1,L4 ... ... L2: JMPZNZ $x,L3,L4 JMPZNZ $x,L3,L4 */ } else if (p->jmp_ext->start->opcode == ZEND_JMPZNZ && p->jmp_ext->start->op1.op_type == IS_TMP_VAR && op->op1.u.var == p->jmp_ext->start->op1.u.var) { if (p->jmp_2 != p->jmp_ext) { BB_DEL_PRED(p->jmp_ext, p); RM_BB(p->jmp_ext); } p->jmp_ext = p->jmp_ext->jmp_ext; BB_ADD_PRED(p->jmp_ext, p); ok = 0; goto jmp_znz; /* JMPZNZ $x,L1,L2 => JMPZNZ $x,L1,L2+1 ... ... L2: JMPZ $x,L3 JMPZ $x,L3 */ } else if (p->jmp_ext->start->opcode == ZEND_JMPZ && p->jmp_ext->start->op1.op_type == IS_TMP_VAR && op->op1.u.var == p->jmp_ext->start->op1.u.var) { if (p->jmp_2 != p->jmp_ext) { BB_DEL_PRED(p->jmp_ext, p); RM_BB(p->jmp_ext); } p->jmp_ext = p->jmp_ext->follow; BB_ADD_PRED(p->jmp_ext, p); ok = 0; goto jmp_znz; } } while (p->jmp_2->len == 1 && p->jmp_2->start->opcode == ZEND_JMP) { BB* x_p = p->jmp_2; if (p->jmp_2 != p->jmp_ext) { BB_DEL_PRED(p->jmp_2, p); RM_BB(x_p); } p->jmp_2 = x_p->jmp_1; BB_ADD_PRED(p->jmp_2, p); ok = 0; } while (p->jmp_ext->len == 1 && p->jmp_ext->start->opcode == ZEND_JMP) { BB* x_p = p->jmp_ext; if (p->jmp_2 != p->jmp_ext) { BB_DEL_PRED(p->jmp_ext, p); RM_BB(x_p); } p->jmp_ext = x_p->jmp_1; BB_ADD_PRED(p->jmp_ext, p); ok = 0; } break; case ZEND_JMPZ: jmp_z: /* L1: JMPZ ?,L1+1 => NOP */ if (p->follow == p->jmp_2) { p->jmp_2 = NULL; SET_TO_NOP(op); --(p->len); ok = 0; break; } else if (op->op1.op_type == IS_CONST) { /* JMPZ 0,L1 => JMP L1 */ if (!zend_is_true(&op->op1.u.constant)) { op->opcode = ZEND_JMP; op->op1.op_type = IS_UNUSED; op->op2.op_type = IS_UNUSED; if (p->follow != p->jmp_2) { BB_DEL_PRED(p->follow, p); RM_BB(p->follow); } p->jmp_1 = p->jmp_2; p->jmp_2 = NULL; p->follow = NULL; ok = 0; goto jmp; /* JMPZ 1,L1 => NOP */ } else { if (p->follow != p->jmp_2) { BB_DEL_PRED(p->jmp_2, p); RM_BB(p->jmp_2); } p->jmp_2 = NULL; SET_TO_NOP(op); --(p->len); ok = 0; break; } /* JMPZ ?,L1 => JMPZNZ ?,L1,L2 JMP L2 JMP L2 */ } else if (p->follow->len == 1 && p->follow->start->opcode == ZEND_JMP) { BB* x_p = p->follow; op->opcode = ZEND_JMPZNZ; if (p->jmp_2 != p->follow) { BB_DEL_PRED(p->follow, p); RM_BB(x_p); } p->follow = NULL; p->jmp_ext = x_p->jmp_1; BB_ADD_PRED(p->jmp_ext, p); ok = 0; goto jmp_znz; } else if (p->jmp_2->len == 1 && op->op1.op_type == IS_TMP_VAR) { /* JMPZ $x,L1 => JMPZ $x,L2 ... ... L1: JMPZ $x,L2 JMPZ $x,L2 ---------------------------------------- JMPZ $x,L1 => JMPZ $x,L2 ... ... L1: JMPZNZ $x,L2,L3 JMPZNZ $x,L2,L3 */ if ((p->jmp_2->start->opcode == ZEND_JMPZ || p->jmp_2->start->opcode == ZEND_JMPZNZ) && p->jmp_2->start->op1.op_type == IS_TMP_VAR && op->op1.u.var == p->jmp_2->start->op1.u.var) { if (p->jmp_2 != p->follow) { BB_DEL_PRED(p->jmp_2, p); RM_BB(p->jmp_2); } p->jmp_2 = p->jmp_2->jmp_2; BB_ADD_PRED(p->jmp_2, p); ok = 0; goto jmp_z; /* JMPZ $x,L1 => JMPZ $x,L1+1 ... ... L1: JMPNZ $x,L2 JMPNZ $x,L2 */ } else if (p->jmp_2->start->opcode == ZEND_JMPNZ && p->jmp_2->start->op1.op_type == IS_TMP_VAR && op->op1.u.var == p->jmp_2->start->op1.u.var) { if (p->jmp_2 != p->follow) { BB_DEL_PRED(p->jmp_2, p); RM_BB(p->jmp_2); } p->jmp_2 = p->jmp_2->follow; BB_ADD_PRED(p->jmp_2, p); ok = 0; goto jmp_z; } } goto jmp_2; case ZEND_JMPNZ: jmp_nz: /* L1: JMPNZ ?,L1+1 => NOP */ if (p->follow == p->jmp_2) { p->jmp_2 = NULL; SET_TO_NOP(op); --(p->len); ok = 0; break; } else if (op->op1.op_type == IS_CONST) { /* JMPNZ 1,L1 => JMP L1 */ if (zend_is_true(&op->op1.u.constant)) { op->opcode = ZEND_JMP; op->op1.op_type = IS_UNUSED; op->op2.op_type = IS_UNUSED; if (p->follow != p->jmp_2) { BB_DEL_PRED(p->follow, p); RM_BB(p->follow); } p->jmp_1 = p->jmp_2; p->jmp_2 = NULL; p->follow = NULL; ok = 0; goto jmp; /* JMPNZ 0,L1 => NOP */ } else { if (p->follow != p->jmp_2) { BB_DEL_PRED(p->jmp_2, p); RM_BB(p->jmp_2); } p->jmp_2 = NULL; SET_TO_NOP(op); --(p->len); ok = 0; break; } /* JMPNZ ?,L1 => JMPZNZ ?,L2,L1 JMP L2 JMP L2 */ } else if (p->follow->len == 1 && p->follow->start->opcode == ZEND_JMP) { BB* x_p = p->follow; op->opcode = ZEND_JMPZNZ; if (p->jmp_2 != p->follow) { BB_DEL_PRED(p->follow, p); RM_BB(p->follow); } p->follow = NULL; p->jmp_ext = p->jmp_2; p->jmp_2 = x_p->jmp_1; BB_ADD_PRED(p->jmp_2, p); ok = 0; goto jmp_znz; /* JMPNZ $x,L1 => JMPNZ $x,L2 ... ... L1: JMPNZ $x,L2 JMPNZ $x,L2 */ } else if (p->jmp_2->len == 1 && op->op1.op_type == IS_TMP_VAR) { if (p->jmp_2->start->opcode == ZEND_JMPNZ && p->jmp_2->start->op1.op_type == IS_TMP_VAR && op->op1.u.var == p->jmp_2->start->op1.u.var) { if (p->jmp_2 != p->follow) { BB_DEL_PRED(p->jmp_2, p); RM_BB(p->jmp_2); } p->jmp_2 = p->jmp_2->jmp_2; BB_ADD_PRED(p->jmp_2, p); ok = 0; goto jmp_nz; /* JMPNZ $x,L1 => JMPNZ $x,L1+1 ... ... L1: JMPZ $x,L2 JMPZ $x,L2 */ } else if (p->jmp_2->start->opcode == ZEND_JMPZ && p->jmp_2->start->op1.op_type == IS_TMP_VAR && op->op1.u.var == p->jmp_2->start->op1.u.var) { if (p->jmp_2 != p->follow) { BB_DEL_PRED(p->jmp_2, p); RM_BB(p->jmp_2); } p->jmp_2 = p->jmp_2->follow; BB_ADD_PRED(p->jmp_2, p); ok = 0; goto jmp_nz; /* JMPNZ $x,L1 => JMPNZ $x,L3 ... ... L1: JMPZNZ $x,L2,L3 JMPZNZ $x,L2,L3 */ } else if (p->jmp_2->start->opcode == ZEND_JMPZNZ && p->jmp_2->start->op1.op_type == IS_TMP_VAR && op->op1.u.var == p->jmp_2->start->op1.u.var) { if (p->jmp_2 != p->follow) { BB_DEL_PRED(p->jmp_2, p); RM_BB(p->jmp_2); } p->jmp_2 = p->jmp_2->jmp_ext; BB_ADD_PRED(p->jmp_2, p); ok = 0; goto jmp_nz; } } goto jmp_2; case ZEND_JMPZ_EX: jmp_z_ex: /* L1: JMPZ_EX $x,L1+1,$x => NOP */ if (p->follow == p->jmp_2 && op->op1.op_type == IS_TMP_VAR && op->result.op_type == IS_TMP_VAR && op->op1.u.var == op->result.u.var) { p->jmp_2 = NULL; SET_TO_NOP(op); --(p->len); ok = 0; break; /* L1: JMPZ_EX $x,L1+1,$y => BOOL $x,$y */ } else if (p->follow == p->jmp_2) { p->jmp_2 = NULL; op->opcode = ZEND_BOOL; op->op2.op_type = IS_UNUSED; ok = 0; break; } else if (p->jmp_2->len == 1 && op->result.op_type == IS_TMP_VAR) { /* JMPZ_EX ?,L1,$x => JMPZ_EX ?,L2,$x ... ... L1: JMPZ $x,L2 JMPZ $x,L2 ------------------------------------------ JMPZ_EX ?,L1,$x => JMPZ_EX ?,L2,$x ... ... L1: JMPZNZ $x,L2,L3 JMPZNZ $x,L2,L3 ------------------------------------------ JMPZ_EX ?,L1,$x => JMPZ_EX ?,L2,$x ... ... L1: JMPZ_EX $x,L2,$x JMPZ_EX $x,L2,$x */ if (((p->jmp_2->start->opcode == ZEND_JMPZ || p->jmp_2->start->opcode == ZEND_JMPZNZ) && p->jmp_2->start->op1.op_type == IS_TMP_VAR && op->result.u.var == p->jmp_2->start->op1.u.var) || (p->jmp_2->start->opcode == ZEND_JMPZ_EX && p->jmp_2->start->op1.op_type == IS_TMP_VAR && p->jmp_2->start->result.op_type == IS_TMP_VAR && op->result.u.var == p->jmp_2->start->op1.u.var && op->result.u.var == p->jmp_2->start->result.u.var)) { if (p->jmp_2 != p->follow) { BB_DEL_PRED(p->jmp_2, p); RM_BB(p->jmp_2); } p->jmp_2 = p->jmp_2->jmp_2; BB_ADD_PRED(p->jmp_2, p); ok = 0; goto jmp_z_ex; /* JMPZ_EX ?,L1,$x => JMPZ_EX ?,L2+1,$x ... ... L1: JMPNZ $x,L2 JMPNZ $x,L2 ------------------------------------------ JMPZ_EX ?,L1,$x => JMPZ_EX ?,L2+1,$x ... ... L1: JMPNZ_EX $x,L2,$x JMPNZ_EX $x,L2,$x */ } else if ((p->jmp_2->start->opcode == ZEND_JMPNZ && p->jmp_2->start->op1.op_type == IS_TMP_VAR && op->result.u.var == p->jmp_2->start->op1.u.var) || (p->jmp_2->start->opcode == ZEND_JMPNZ_EX && p->jmp_2->start->op1.op_type == IS_TMP_VAR && p->jmp_2->start->result.op_type == IS_TMP_VAR && op->result.u.var == p->jmp_2->start->op1.u.var && op->result.u.var == p->jmp_2->start->result.u.var)) { if (p->jmp_2 != p->follow) { BB_DEL_PRED(p->jmp_2, p); RM_BB(p->jmp_2); } p->jmp_2 = p->jmp_2->follow; BB_ADD_PRED(p->jmp_2, p); ok = 0; goto jmp_z_ex; /* JMPZ_EX ?,L1,$x => JMPZ_EX ?,L1+1,$y ... ... L1: BOOL $x,$y BOOL $x,$y */ } else if (p->jmp_2->start->opcode == ZEND_BOOL && p->jmp_2->start->op1.op_type == IS_TMP_VAR && op->result.u.var == p->jmp_2->start->op1.u.var) { memcpy(&op->result, &p->jmp_2->start->result, sizeof(zval)); if (p->jmp_2 != p->follow) { BB_DEL_PRED(p->jmp_2, p); RM_BB(p->jmp_2); } p->jmp_2 = p->jmp_2->follow; BB_ADD_PRED(p->jmp_2, p); ok = 0; goto jmp_z_ex; /* JMPZ_EX ?,L1,$x => JMPZ ?,L1+1 ... ... L1: FREE $x FREE $x */ } else if (p->jmp_2->start->opcode == ZEND_FREE && p->jmp_2->start->op1.op_type == IS_TMP_VAR && op->result.u.var == p->jmp_2->start->op1.u.var) { op->opcode = ZEND_JMPZ; op->result.op_type = IS_UNUSED; if (p->jmp_2 != p->follow) { BB_DEL_PRED(p->jmp_2, p); RM_BB(p->jmp_2); } p->jmp_2 = p->jmp_2->follow; BB_ADD_PRED(p->jmp_2, p); ok = 0; goto jmp_z; } /* JMPZ_EX ?,L1,$x => JMPZ ?,L1+1 ... ... L1: FREE $x FREE $x */ } else if (op->result.op_type == IS_TMP_VAR && p->jmp_2->start->opcode == ZEND_FREE && p->jmp_2->start->op1.op_type == IS_TMP_VAR && op->result.u.var == p->jmp_2->start->op1.u.var) { if (p->jmp_2->len > 1) { /* splitting */ BB* new_bb = (p->jmp_2+1); new_bb->used = 1; new_bb->start = p->jmp_2->start+1; new_bb->len = p->jmp_2->len-1; p->jmp_2->len = 1; new_bb->next = p->jmp_2->next; p->jmp_2->next = new_bb; new_bb->pred = NULL; if (p->jmp_2->jmp_1) { new_bb->jmp_1 = p->jmp_2->jmp_1; BB_ADD_PRED(new_bb->jmp_1, new_bb); BB_DEL_PRED(new_bb->jmp_1, p->jmp_2); p->jmp_2->jmp_1 = NULL; } if (p->jmp_2->jmp_2) { new_bb->jmp_2 = p->jmp_2->jmp_2; BB_ADD_PRED(new_bb->jmp_2, new_bb); BB_DEL_PRED(new_bb->jmp_2, p->jmp_2); p->jmp_2->jmp_2 = NULL; } if (p->jmp_2->jmp_ext) { new_bb->jmp_ext = p->jmp_2->jmp_ext; BB_ADD_PRED(new_bb->jmp_ext, new_bb); BB_DEL_PRED(new_bb->jmp_ext, p->jmp_2); p->jmp_2->jmp_ext = NULL; } op->opcode = ZEND_JMPZ; op->result.op_type = IS_UNUSED; if (p->jmp_2->follow) { new_bb->follow = p->jmp_2->follow; BB_ADD_PRED(new_bb->follow, new_bb); BB_DEL_PRED(new_bb->follow, p->jmp_2); p->jmp_2->follow = NULL; } p->jmp_2->follow = new_bb; BB_ADD_PRED(p->jmp_2->follow, p->jmp_2); } if (p->jmp_2 != p->follow) { BB_DEL_PRED(p->jmp_2, p); RM_BB(p->jmp_2); } p->jmp_2 = p->jmp_2->follow; BB_ADD_PRED(p->jmp_2, p); ok = 0; goto jmp_z; } goto jmp_2; case ZEND_JMPNZ_EX: jmp_nz_ex: /* L1: JMPNZ_EX $x,L1+1,$x => NOP */ if (p->follow == p->jmp_2 && op->op1.op_type == IS_TMP_VAR && op->result.op_type == IS_TMP_VAR && op->op1.u.var == op->result.u.var) { p->jmp_2 = NULL; SET_TO_NOP(op); --(p->len); ok = 0; break; /* L1: JMPNZ_EX $x,L1+1,$y => BOOL $x,$y */ } else if (p->follow == p->jmp_2) { p->jmp_2 = NULL; op->opcode = ZEND_BOOL; op->op2.op_type = IS_UNUSED; ok = 0; break; } else if (p->jmp_2->len == 1 && op->result.op_type == IS_TMP_VAR) { /* JMPNZ_EX ?,L1,$x => JMPNZ_EX ?,L2,$x ... ... L1: JMPNZ $x,L2 JMPNZ $x,L2 ------------------------------------------ JMPNZ_EX ?,L1,$x => JMPNZ_EX ?,L2,$x ... ... L1: JMPNZ_EX $x,L2,$x JMPNZ_EX $x,L2,$x */ if ((p->jmp_2->start->opcode == ZEND_JMPNZ && p->jmp_2->start->op1.op_type == IS_TMP_VAR && op->result.u.var == p->jmp_2->start->op1.u.var) || (p->jmp_2->start->opcode == ZEND_JMPNZ_EX && p->jmp_2->start->op1.op_type == IS_TMP_VAR && p->jmp_2->start->result.op_type == IS_TMP_VAR && op->result.u.var == p->jmp_2->start->op1.u.var && op->result.u.var == p->jmp_2->start->result.u.var)) { if (p->jmp_2 != p->follow) { BB_DEL_PRED(p->jmp_2, p); RM_BB(p->jmp_2); } p->jmp_2 = p->jmp_2->jmp_2; BB_ADD_PRED(p->jmp_2, p); ok = 0; goto jmp_nz_ex; /* JMPNZ_EX ?,L1,$x => JMPNZ_EX ?,L3,$x ... ... L1: JMPZNZ $x,L2,L3 JMPZNZ $x,L2,L3 */ } else if (p->jmp_2->start->opcode == ZEND_JMPZNZ && p->jmp_2->start->op1.op_type == IS_TMP_VAR && op->result.u.var == p->jmp_2->start->op1.u.var) { if (p->jmp_2 != p->follow) { BB_DEL_PRED(p->jmp_2, p); RM_BB(p->jmp_2); } p->jmp_2 = p->jmp_2->jmp_ext; BB_ADD_PRED(p->jmp_2, p); ok = 0; goto jmp_nz_ex; /* JMPNZ_EX ?,L1,$x => JMPNZ_EX ?,L1+1,$x ... ... L1: JMPZ $x,L2 JMPZ $x,L2 ------------------------------------------ JMPNZ_EX ?,L1,$x => JMPNZ_EX ?,L1+1,$x ... ... L1: JMPZ_EX $x,L2,$x JMPZ_EX $x,L2,$x */ } else if ((p->jmp_2->start->opcode == ZEND_JMPZ && p->jmp_2->start->op1.op_type == IS_TMP_VAR && op->result.u.var == p->jmp_2->start->op1.u.var) || (p->jmp_2->start->opcode == ZEND_JMPZ_EX && p->jmp_2->start->op1.op_type == IS_TMP_VAR && p->jmp_2->start->result.op_type == IS_TMP_VAR && op->result.u.var == p->jmp_2->start->op1.u.var && op->result.u.var == p->jmp_2->start->result.u.var)) { if (p->jmp_2 != p->follow) { BB_DEL_PRED(p->jmp_2, p); RM_BB(p->jmp_2); } p->jmp_2 = p->jmp_2->follow; BB_ADD_PRED(p->jmp_2, p); ok = 0; goto jmp_nz_ex; /* JMPNZ_EX ?,L1,$x => JMPNZ_EX ?,L1+1,$y ... ... L1: BOOL $x,$y BOOL $x,$y */ } else if (p->jmp_2->start->opcode == ZEND_BOOL && p->jmp_2->start->op1.op_type == IS_TMP_VAR && op->result.u.var == p->jmp_2->start->op1.u.var) { memcpy(&op->result, &p->jmp_2->start->result, sizeof(zval)); if (p->jmp_2 != p->follow) { BB_DEL_PRED(p->jmp_2, p); RM_BB(p->jmp_2); } p->jmp_2 = p->jmp_2->follow; BB_ADD_PRED(p->jmp_2, p); ok = 0; goto jmp_nz_ex; /* JMPNZ_EX ?,L1,$x => JMPNZ ?,L1+1 ... ... L1: FREE $x FREE $x */ } else if (p->jmp_2->start->opcode == ZEND_FREE && p->jmp_2->start->op1.op_type == IS_TMP_VAR && op->result.u.var == p->jmp_2->start->op1.u.var) { op->opcode = ZEND_JMPNZ; op->result.op_type = IS_UNUSED; if (p->jmp_2 != p->follow) { BB_DEL_PRED(p->jmp_2, p); RM_BB(p->jmp_2); } p->jmp_2 = p->jmp_2->follow; BB_ADD_PRED(p->jmp_2, p); ok = 0; goto jmp_nz; } /* JMPNZ_EX ?,L1,$x => JMPNZ_EX ?,L1+1,$x ... ... L1: FREE $x FREE $x */ } else if (op->result.op_type == IS_TMP_VAR && p->jmp_2->start->opcode == ZEND_FREE && p->jmp_2->start->op1.op_type == IS_TMP_VAR && op->result.u.var == p->jmp_2->start->op1.u.var) { if (p->jmp_2->len > 1) { /* splitting */ BB* new_bb = (p->jmp_2+1); new_bb->used = 1; new_bb->start = p->jmp_2->start+1; new_bb->len = p->jmp_2->len-1; p->jmp_2->len = 1; new_bb->next = p->jmp_2->next; p->jmp_2->next = new_bb; new_bb->pred = NULL; if (p->jmp_2->jmp_1) { new_bb->jmp_1 = p->jmp_2->jmp_1; BB_ADD_PRED(new_bb->jmp_1, new_bb); BB_DEL_PRED(new_bb->jmp_1, p->jmp_2); p->jmp_2->jmp_1 = NULL; } if (p->jmp_2->jmp_2) { new_bb->jmp_2 = p->jmp_2->jmp_2; BB_ADD_PRED(new_bb->jmp_2, new_bb); BB_DEL_PRED(new_bb->jmp_2, p->jmp_2); p->jmp_2->jmp_2 = NULL; } if (p->jmp_2->jmp_ext) { new_bb->jmp_ext = p->jmp_2->jmp_ext; BB_ADD_PRED(new_bb->jmp_ext, new_bb); BB_DEL_PRED(new_bb->jmp_ext, p->jmp_2); p->jmp_2->jmp_ext = NULL; } if (p->jmp_2->follow) { new_bb->follow = p->jmp_2->follow; BB_ADD_PRED(new_bb->follow, new_bb); BB_DEL_PRED(new_bb->follow, p->jmp_2); p->jmp_2->follow = NULL; } p->jmp_2->follow = new_bb; BB_ADD_PRED(p->jmp_2->follow, p->jmp_2); } op->opcode = ZEND_JMPNZ; op->result.op_type = IS_UNUSED; if (p->jmp_2 != p->follow) { BB_DEL_PRED(p->jmp_2, p); RM_BB(p->jmp_2); } p->jmp_2 = p->jmp_2->follow; BB_ADD_PRED(p->jmp_2, p); ok = 0; goto jmp_nz; } goto jmp_2; #ifndef ZEND_ENGINE_2_1 /* Pre-PHP 5.1 only */ case ZEND_JMP_NO_CTOR: #else case ZEND_NEW: #endif case ZEND_FE_FETCH: jmp_2: while (p->jmp_2->len == 1 && p->jmp_2->start->opcode == ZEND_JMP) { BB* x_p = p->jmp_2; if (p->jmp_2 != p->follow) { BB_DEL_PRED(p->jmp_2, p); RM_BB(x_p); } p->jmp_2 = x_p->jmp_1; BB_ADD_PRED(p->jmp_2, p); ok = 0; } } } /* Merging Basic Blocks */ if (p->used && p->pred != NULL && p->pred->bb->used && p->pred->next == NULL && p->pred->bb->follow == p && p->pred->bb->next == p && p->pred->bb->jmp_1 == NULL && p->pred->bb->jmp_2 == NULL && p->pred->bb->jmp_ext == NULL && /* HOESH: See structure declaration */ p->protect_merge == 0) { BB* x = p->pred->bb; BB_DEL_PRED(p, x); x->len = &p->start[p->len] - x->start; if (p->jmp_1 != NULL) { x->jmp_1 = p->jmp_1; BB_DEL_PRED(p->jmp_1, p); BB_ADD_PRED(p->jmp_1, x); } if (p->jmp_2 != NULL) { x->jmp_2 = p->jmp_2; BB_DEL_PRED(p->jmp_2, p); BB_ADD_PRED(p->jmp_2, x); } if (p->jmp_ext != NULL) { x->jmp_ext = p->jmp_ext; BB_DEL_PRED(p->jmp_ext, p); BB_ADD_PRED(p->jmp_ext, x); } x->follow = p->follow; if (p->follow != NULL) { BB_DEL_PRED(p->follow, p); BB_ADD_PRED(p->follow, x); } p->used = 0; p->len = 0; ok = 0; /*??? } else if (p != bb && p->used && p->pred != NULL && p->pred->bb->used && p->pred->next == NULL && p->follow == NULL && p->pred->bb->follow == NULL && p->pred->bb->jmp_1 == p && p->pred->bb->jmp_2 == NULL && p->pred->bb->jmp_ext == NULL && p->pred->bb != p && p->pred->bb->start[p->pred->bb->len-1].opcode == ZEND_JMP) { BB* x = p->pred->bb; int offset = p->len; zend_op* save = emalloc(sizeof(zend_op)*offset); memcpy(save, p->start, sizeof(zend_op)*offset); if (x->start < p->start) { if (offset > 1) { BB* q = x->next; while (q != p) { if (q->len > 0) { memcpy(q->start + (offset-1), q->start, sizeof(zend_op)*q->len); } q->start += offset - 1; q = q->next; } } } else { BB* q = p->next; while (q != x->next) { if (q->len > 0) { memcpy(q->start - offset, q->start, sizeof(zend_op)*q->len); q->start -= offset; } q = q->next; } } memcpy(x->start+(x->len-1),save, sizeof(zend_op)*offset); efree(save); x->len += offset-1; if (x->start < p->start) { SET_TO_NOP(&p->start[p->len-1]); } else { SET_TO_NOP(&x->start[x->len]); } BB_DEL_PRED(p, x); x->jmp_1 = p->jmp_1; if (p->jmp_1 != NULL) { BB_DEL_PRED(p->jmp_1, p); BB_ADD_PRED(p->jmp_1, x); } if (p->jmp_2 != NULL) { x->jmp_2 = p->jmp_2; BB_DEL_PRED(p->jmp_2, p); BB_ADD_PRED(p->jmp_2, x); } if (p->jmp_ext != NULL) { x->jmp_ext = p->jmp_ext; BB_DEL_PRED(p->jmp_ext, p); BB_ADD_PRED(p->jmp_ext, x); } if (p->follow != NULL) { x->follow = p->follow; BB_DEL_PRED(p->follow, p); BB_ADD_PRED(p->follow, x); } p->start = NULL; p->used = 0; p->len = 0; ok = 0; */ } p = p->next; } if (ok) { /* Eliminate JMP to RETURN or EXIT */ p = bb; while (p != NULL) { if (p->used && p->len > 0) { zend_op* op = &p->start[p->len-1]; if (op->opcode == ZEND_JMP && p->jmp_1->len == 1 && (p->jmp_1->start->opcode == ZEND_RETURN || p->jmp_1->start->opcode == ZEND_EXIT)) { BB_DEL_PRED(p->jmp_1, p); RM_BB(p->jmp_1); memcpy(op, p->jmp_1->start, sizeof(zend_op)); if (op->op1.op_type == IS_CONST) { zval_copy_ctor(&op->op1.u.constant); } p->jmp_1 = NULL; ok = 0; } } p = p->next; } } if (ok) { break; } } } static int opt_get_constant(const char* name, int name_len, zend_constant** result TSRMLS_DC) { if (!EAG(encoder) || (name_len == sizeof("false")-1 && strcmp(name,"false") == 0) || (name_len == sizeof("true")-1 && strcmp(name,"true") == 0)) { union { zend_constant *v; void *ptr; } c; int retval; char *lookup_name = do_alloca(name_len+1); memcpy(lookup_name, name, name_len); lookup_name[name_len] = '\0'; if (zend_hash_find(EG(zend_constants), lookup_name, name_len+1, &c.ptr)==SUCCESS) { *result = c.v; retval=1; } else { zend_str_tolower(lookup_name, name_len); if (zend_hash_find(EG(zend_constants), lookup_name, name_len+1, &c.ptr)==SUCCESS) { if ((c.v->flags & CONST_CS) && (memcmp(c.v->name, name, name_len)!=0)) { retval=0; } else { *result = c.v; retval=1; } } else { retval=0; } } free_alloca(lookup_name); return retval; } else { return 0; } } static int opt_function_exists(const char* name, int name_len TSRMLS_DC) { if (!EAG(encoder)) { char *lcname; char *lcfname; Bucket *p; lcname = estrndup(name,name_len+1); zend_str_tolower(lcname, name_len); p = module_registry.pListHead; while (p != NULL) { zend_module_entry *m = (zend_module_entry*)p->pData; if (m->type == MODULE_PERSISTENT) { zend_function_entry* f = m->functions; if (f != NULL) { while (f->fname) { lcfname = estrdup(f->fname); zend_str_tolower(lcfname, strlen(lcfname)); if (strcmp(lcname,lcfname) == 0) { efree(lcfname); efree(lcname); return 1; } efree(lcfname); f++; } } } p = p->pListNext; } efree(lcname); } return 0; } static int opt_extension_loaded(const char* name, int name_len TSRMLS_DC) { if (!EAG(encoder)) { Bucket *p = module_registry.pListHead; while (p != NULL) { zend_module_entry *m = (zend_module_entry*)p->pData; if (m->type == MODULE_PERSISTENT && strcmp(m->name,name) == 0) { return 1; } p = p->pListNext; } } return 0; } static int opt_result_is_numeric(zend_op* x) { switch (x->opcode) { case ZEND_ADD: case ZEND_SUB: case ZEND_MUL: case ZEND_DIV: case ZEND_MOD: case ZEND_SL: case ZEND_SR: case ZEND_BOOL_NOT: case ZEND_BOOL_XOR: case ZEND_IS_IDENTICAL: case ZEND_IS_NOT_IDENTICAL: case ZEND_IS_EQUAL: case ZEND_IS_NOT_EQUAL: case ZEND_IS_SMALLER: case ZEND_IS_SMALLER_OR_EQUAL: case ZEND_PRE_DEC: case ZEND_PRE_INC: case ZEND_ASSIGN_ADD: case ZEND_ASSIGN_SUB: case ZEND_ASSIGN_MUL: case ZEND_ASSIGN_DIV: case ZEND_ASSIGN_MOD: case ZEND_ASSIGN_SL: case ZEND_ASSIGN_SR: case ZEND_BOOL: return 1; case ZEND_CAST: if (x->extended_value == IS_BOOL || x->extended_value == IS_LONG || x->extended_value == IS_DOUBLE) { return 1; } return 0; case ZEND_DO_FCALL: if (x->op1.op_type == IS_CONST && x->op1.u.constant.type == IS_STRING && (strcmp(x->op1.u.constant.value.str.val,"count") == 0 || strcmp(x->op1.u.constant.value.str.val,"sizeof") == 0 || strcmp(x->op1.u.constant.value.str.val,"strcmp") == 0 || strcmp(x->op1.u.constant.value.str.val,"strlen") == 0 || strcmp(x->op1.u.constant.value.str.val,"strpos") == 0 || strcmp(x->op1.u.constant.value.str.val,"strncmp") == 0 || strcmp(x->op1.u.constant.value.str.val,"strcoll") == 0 || strcmp(x->op1.u.constant.value.str.val,"strcasecmp") == 0)) { return 1; } return 0; default: return 0; } return 0; } #ifndef ZEND_ENGINE_2 #define FETCH_TYPE(op) ((op)->op2.u.fetch_type) #else #define FETCH_TYPE(op) ((op)->op2.u.EA.type) #endif #define SET_UNDEFINED(op) Ts[VAR_NUM((op).u.var)] = NULL; #define SET_DEFINED(op) Ts[VAR_NUM((op)->result.u.var)] = (op); #define IS_DEFINED(op) (Ts[VAR_NUM((op).u.var)] != NULL) #define DEFINED_OP(op) (Ts[VAR_NUM((op).u.var)]) static void optimize_bb(BB* bb, zend_op_array* op_array, char* global, int pass TSRMLS_DC) { zend_op* prev = NULL; zend_op* op = bb->start; zend_op* end = op + bb->len; HashTable assigns; HashTable fetch_dim; zend_op** Ts = do_alloca(sizeof(zend_op*)*op_array->T); memset(Ts, 0, sizeof(zend_op*)*op_array->T); zend_hash_init(&assigns, 0, NULL, NULL, 0); zend_hash_init(&fetch_dim, 0, NULL, NULL, 0); while (op < end) { /* Constant Folding */ if (op->op1.op_type == IS_TMP_VAR && IS_DEFINED(op->op1) && DEFINED_OP(op->op1)->opcode == ZEND_QM_ASSIGN && DEFINED_OP(op->op1)->op1.op_type == IS_CONST) { zend_op *x = DEFINED_OP(op->op1); if (op->opcode != ZEND_CASE) { SET_UNDEFINED(op->op1); memcpy(&op->op1, &x->op1, sizeof(znode)); SET_TO_NOP(x); } } if (op->op2.op_type == IS_TMP_VAR && IS_DEFINED(op->op2) && DEFINED_OP(op->op2)->opcode == ZEND_QM_ASSIGN && DEFINED_OP(op->op2)->op1.op_type == IS_CONST) { zend_op *x = DEFINED_OP(op->op2); SET_UNDEFINED(op->op2); memcpy(&op->op2, &x->op1, sizeof(znode)); SET_TO_NOP(x); } /* if (op->result.op_type == IS_TMP_VAR && Ts[VAR_NUM(op->result.u.var)].op_type == IS_CONST && op->opcode != ZEND_ADD_CHAR && op->opcode != ZEND_ADD_STRING && op->opcode != ZEND_ADD_VAR && op->opcode != ZEND_ADD_ARRAY_ELEMENT) { Ts[VAR_NUM(op->result.u.var)].op_type = IS_UNUSED; } */ if (op->opcode == ZEND_IS_EQUAL) { if (op->op1.op_type == IS_CONST && (op->op1.u.constant.type == IS_BOOL && op->op1.u.constant.value.lval == 0)) { op->opcode = ZEND_BOOL_NOT; memcpy(&op->op1, &op->op2, sizeof(znode)); op->op2.op_type = IS_UNUSED; } else if (op->op1.op_type == IS_CONST && op->op1.u.constant.type == IS_BOOL && op->op1.u.constant.value.lval == 1) { op->opcode = ZEND_BOOL; memcpy(&op->op1, &op->op2, sizeof(znode)); op->op2.op_type = IS_UNUSED; } else if (op->op2.op_type == IS_CONST && op->op2.u.constant.type == IS_BOOL && op->op2.u.constant.value.lval == 0) { op->opcode = ZEND_BOOL_NOT; op->op2.op_type = IS_UNUSED; } else if (op->op2.op_type == IS_CONST && op->op2.u.constant.type == IS_BOOL && op->op2.u.constant.value.lval == 1) { op->opcode = ZEND_BOOL; op->op2.op_type = IS_UNUSED; } else if (op->op2.op_type == IS_CONST && op->op2.u.constant.type == IS_LONG && op->op2.u.constant.value.lval == 0 && (op->op1.op_type == IS_TMP_VAR || op->op1.op_type == IS_VAR) && IS_DEFINED(op->op1) && opt_result_is_numeric(DEFINED_OP(op->op1))) { op->opcode = ZEND_BOOL_NOT; op->op2.op_type = IS_UNUSED; } } else if (op->opcode == ZEND_IS_NOT_EQUAL) { if (op->op1.op_type == IS_CONST && op->op1.u.constant.type == IS_BOOL && op->op1.u.constant.value.lval == 0) { op->opcode = ZEND_BOOL; memcpy(&op->op1, &op->op2, sizeof(znode)); op->op2.op_type = IS_UNUSED; } else if (op->op1.op_type == IS_CONST && op->op1.u.constant.type == IS_BOOL && op->op1.u.constant.value.lval == 1) { op->opcode = ZEND_BOOL_NOT; memcpy(&op->op1, &op->op2, sizeof(znode)); op->op2.op_type = IS_UNUSED; } else if (op->op2.op_type == IS_CONST && op->op2.u.constant.type == IS_BOOL && op->op2.u.constant.value.lval == 0) { op->opcode = ZEND_BOOL; op->op2.op_type = IS_UNUSED; } else if (op->op2.op_type == IS_CONST && op->op2.u.constant.type == IS_BOOL && op->op2.u.constant.value.lval == 1) { op->opcode = ZEND_BOOL_NOT; op->op2.op_type = IS_UNUSED; } else if (op->op2.op_type == IS_CONST && op->op2.u.constant.type == IS_LONG && op->op2.u.constant.value.lval == 0 && (op->op1.op_type == IS_TMP_VAR || op->op1.op_type == IS_VAR) && IS_DEFINED(op->op1) && opt_result_is_numeric(DEFINED_OP(op->op1))) { op->opcode = ZEND_BOOL; op->op2.op_type = IS_UNUSED; } } #if 0 /* doesn't work with newer php versions */ if (op->opcode == ZEND_FETCH_CONSTANT && op->result.op_type == IS_TMP_VAR) { zend_constant *c = NULL; if (op->op1.u.constant.value.str.val != NULL && opt_get_constant(op->op1.u.constant.value.str.val, op->op1.u.constant.value.str.len, &c TSRMLS_CC) && c != NULL && ((c->flags & CONST_PERSISTENT) != 0)) { STR_FREE(op->op1.u.constant.value.str.val); memcpy(&op->op1.u.constant, &c->value, sizeof(zval)); zval_copy_ctor(&op->op1.u.constant); op->opcode = ZEND_QM_ASSIGN; op->extended_value = 0; op->op1.op_type = IS_CONST; op->op2.op_type = IS_UNUSED; } } else #endif if ((op->opcode == ZEND_ADD || op->opcode == ZEND_SUB || op->opcode == ZEND_MUL || op->opcode == ZEND_DIV || op->opcode == ZEND_MOD || op->opcode == ZEND_SL || op->opcode == ZEND_SR || op->opcode == ZEND_CONCAT || op->opcode == ZEND_BW_OR || op->opcode == ZEND_BW_AND || op->opcode == ZEND_BW_XOR || op->opcode == ZEND_BOOL_XOR || op->opcode == ZEND_IS_IDENTICAL || op->opcode == ZEND_IS_NOT_IDENTICAL || op->opcode == ZEND_IS_EQUAL || op->opcode == ZEND_IS_NOT_EQUAL || op->opcode == ZEND_IS_SMALLER || op->opcode == ZEND_IS_SMALLER_OR_EQUAL) && op->op1.op_type == IS_CONST && op->op2.op_type == IS_CONST && op->result.op_type == IS_TMP_VAR) { typedef int (*binary_op_type)(zval *, zval *, zval* TSRMLS_DC); binary_op_type binary_op = (binary_op_type)get_binary_op(op->opcode); if (binary_op != NULL) { int old = EG(error_reporting); zval res; EG(error_reporting) = 0; if (binary_op(&res, &op->op1.u.constant, &op->op2.u.constant TSRMLS_CC) != FAILURE) { zval_dtor(&op->op1.u.constant); zval_dtor(&op->op2.u.constant); op->opcode = ZEND_QM_ASSIGN; op->extended_value = 0; op->op1.op_type = IS_CONST; memcpy(&op->op1.u.constant, &res, sizeof(zval)); op->op2.op_type = IS_UNUSED; } EG(error_reporting) = old; } } else if ((op->opcode == ZEND_BW_NOT || op->opcode == ZEND_BOOL_NOT) && op->op1.op_type == IS_CONST && op->result.op_type == IS_TMP_VAR) { int (*unary_op)(zval *result, zval *op1) = unary_op = get_unary_op(op->opcode); if (unary_op != NULL) { int old = EG(error_reporting); zval res; EG(error_reporting) = 0; if (unary_op(&res, &op->op1.u.constant) != FAILURE) { zval_dtor(&op->op1.u.constant); op->opcode = ZEND_QM_ASSIGN; op->extended_value = 0; op->op1.op_type = IS_CONST; memcpy(&op->op1.u.constant, &res, sizeof(zval)); op->op2.op_type = IS_UNUSED; } EG(error_reporting) = old; } } else if ((op->opcode == ZEND_BOOL) && op->op1.op_type == IS_CONST && op->result.op_type == IS_TMP_VAR) { zval res; res.type = IS_BOOL; res.value.lval = zend_is_true(&op->op1.u.constant); zval_dtor(&op->op1.u.constant); op->opcode = ZEND_QM_ASSIGN; op->extended_value = 0; op->op1.op_type = IS_CONST; memcpy(&op->op1.u.constant, &res, sizeof(zval)); op->op2.op_type = IS_UNUSED; } else if ((op->opcode == ZEND_CAST) && op->op1.op_type == IS_CONST && op->result.op_type == IS_TMP_VAR && op->extended_value != IS_ARRAY && op->extended_value != IS_OBJECT && op->extended_value != IS_RESOURCE) { zval res; memcpy(&res,&op->op1.u.constant,sizeof(zval)); zval_copy_ctor(&res); switch (op->extended_value) { case IS_NULL: convert_to_null(&res); break; case IS_BOOL: convert_to_boolean(&res); break; case IS_LONG: convert_to_long(&res); break; case IS_DOUBLE: convert_to_double(&res); break; case IS_STRING: convert_to_string(&res); break; case IS_ARRAY: convert_to_array(&res); break; case IS_OBJECT: convert_to_object(&res); break; } zval_dtor(&op->op1.u.constant); op->opcode = ZEND_QM_ASSIGN; op->extended_value = 0; op->op1.op_type = IS_CONST; memcpy(&op->op1.u.constant, &res, sizeof(zval)); op->op2.op_type = IS_UNUSED; /* FREE(CONST) => NOP */ } else if (op->opcode == ZEND_FREE && op->op1.op_type == IS_CONST) { zval_dtor(&op->op1.u.constant); SET_TO_NOP(op); /* INIT_STRING ADD_CAHR ADD_STRIN ADD_VAR folding */ /* INIT_STRING($y) => QM_ASSIGN('',$y) */ } else if (op->opcode == ZEND_INIT_STRING) { op->opcode = ZEND_QM_ASSIGN; op->op1.op_type = IS_CONST; op->op2.op_type = IS_UNUSED; op->op1.u.constant.type = IS_STRING; op->op1.u.constant.value.str.len = 0; op->op1.u.constant.value.str.val = empty_string; /* ADD_CHAR(CONST,CONST,$y) => QM_ASSIGN(CONST,$y) */ } else if (op->opcode == ZEND_ADD_CHAR && op->op1.op_type == IS_CONST) { size_t len; op->opcode = ZEND_QM_ASSIGN; op->op1.op_type = IS_CONST; op->op2.op_type = IS_UNUSED; convert_to_string(&op->op1.u.constant); len = op->op1.u.constant.value.str.len + 1; STR_REALLOC(op->op1.u.constant.value.str.val,len+1); op->op1.u.constant.value.str.val[len-1] = (char) op->op2.u.constant.value.lval; op->op1.u.constant.value.str.val[len] = 0; op->op1.u.constant.value.str.len = len; /* ADD_STRING(CONST,CONST,$y) => QM_ASSIGN(CONST,$y) */ } else if (op->opcode == ZEND_ADD_STRING && op->op1.op_type == IS_CONST) { size_t len; op->opcode = ZEND_QM_ASSIGN; op->op1.op_type = IS_CONST; op->op2.op_type = IS_UNUSED; convert_to_string(&op->op1.u.constant); convert_to_string(&op->op2.u.constant); len = op->op1.u.constant.value.str.len + op->op2.u.constant.value.str.len; STR_REALLOC(op->op1.u.constant.value.str.val,len+1); memcpy(op->op1.u.constant.value.str.val+op->op1.u.constant.value.str.len, op->op2.u.constant.value.str.val, op->op2.u.constant.value.str.len); op->op1.u.constant.value.str.val[len] = 0; op->op1.u.constant.value.str.len = len; STR_FREE(op->op2.u.constant.value.str.val); /* ADD_VAR(CONST,VAR,$y) => CONCAT(CONST,$y) */ } else if (op->opcode == ZEND_ADD_VAR && op->op1.op_type == IS_CONST) { op->opcode = ZEND_CONCAT; /* CONCAT('',$x,$y) + ADD_CHAR($y,CHAR,$z) => CONCAT($x, CONST, $z) */ } else if (op->opcode == ZEND_ADD_CHAR && op->op1.op_type == IS_TMP_VAR && IS_DEFINED(op->op1) && DEFINED_OP(op->op1)->opcode == ZEND_CONCAT && DEFINED_OP(op->op1)->op1.op_type == IS_CONST && DEFINED_OP(op->op1)->op1.u.constant.type == IS_STRING && DEFINED_OP(op->op1)->op1.u.constant.value.str.len == 0) { char ch = (char) op->op2.u.constant.value.lval; zend_op *x = DEFINED_OP(op->op1); SET_UNDEFINED(op->op1); memcpy(&op->op1, &x->op2, sizeof(op->op2)); op->opcode = ZEND_CONCAT; op->op2.u.constant.type = IS_STRING; op->op2.u.constant.value.str.val = emalloc(2); op->op2.u.constant.value.str.val[0] = ch; op->op2.u.constant.value.str.val[1] = '\000'; op->op2.u.constant.value.str.len = 1; STR_FREE(x->op1.u.constant.value.str.val); SET_TO_NOP(x); /* CONCAT('',$x,$y) + ADD_STRING($y,$v,$z) => CONCAT($x, $v, $z) CONCAT('',$x,$y) + CONCAT($y,$v,$z) => CONCAT($x, $v, $z) CONCAT('',$x,$y) + ADD_VAR($y,$v,$z) => CONCAT($x, $v, $z) */ } else if ((op->opcode == ZEND_ADD_STRING || op->opcode == ZEND_CONCAT || op->opcode == ZEND_ADD_VAR) && op->op1.op_type == IS_TMP_VAR && IS_DEFINED(op->op1) && DEFINED_OP(op->op1)->opcode == ZEND_CONCAT && DEFINED_OP(op->op1)->op1.op_type == IS_CONST && DEFINED_OP(op->op1)->op1.u.constant.type == IS_STRING && DEFINED_OP(op->op1)->op1.u.constant.value.str.len == 0) { zend_op *x = DEFINED_OP(op->op1); SET_UNDEFINED(op->op1); op->opcode = ZEND_CONCAT; memcpy(&op->op1, &x->op2, sizeof(op->op2)); STR_FREE(x->op1.u.constant.value.str.val); SET_TO_NOP(x); /* ADD_CHAR($x,CONST,$y) + ADD_CHAR($y,CHAR,$z) => ADD_STRING($x, CONST, $z) */ } else if (op->opcode == ZEND_ADD_CHAR && op->op1.op_type == IS_TMP_VAR && IS_DEFINED(op->op1) && DEFINED_OP(op->op1)->opcode == ZEND_ADD_CHAR) { char ch1 = (char) DEFINED_OP(op->op1)->op2.u.constant.value.lval; char ch2 = (char) op->op2.u.constant.value.lval; DEFINED_OP(op->op1)->op2.u.constant.type = IS_STRING; DEFINED_OP(op->op1)->op2.u.constant.value.str.val = emalloc(3); DEFINED_OP(op->op1)->op2.u.constant.value.str.val[0] = ch1; DEFINED_OP(op->op1)->op2.u.constant.value.str.val[1] = ch2; DEFINED_OP(op->op1)->op2.u.constant.value.str.val[2] = '\000'; DEFINED_OP(op->op1)->op2.u.constant.value.str.len = 2; memcpy(&DEFINED_OP(op->op1)->result, &op->result, sizeof(op->result)); DEFINED_OP(op->op1)->opcode = ZEND_ADD_STRING; SET_DEFINED(DEFINED_OP(op->op1)); SET_TO_NOP(op); /* CONCAT($x,CONST,$y) + ADD_CHAR($y,CONST,$z) => CONCAT($x, CONST, $z) ADD_STRING($x,CONST,$y) + ADD_CHAR($y,CONST,$z) => ADD_STRING($x, CONST, $z) */ } else if (op->opcode == ZEND_ADD_CHAR && op->op1.op_type == IS_TMP_VAR && IS_DEFINED(op->op1) && (DEFINED_OP(op->op1)->opcode == ZEND_CONCAT || DEFINED_OP(op->op1)->opcode == ZEND_ADD_STRING) && DEFINED_OP(op->op1)->op2.op_type == IS_CONST) { size_t len; convert_to_string(&DEFINED_OP(op->op1)->op2.u.constant); len = DEFINED_OP(op->op1)->op2.u.constant.value.str.len + 1; STR_REALLOC(DEFINED_OP(op->op1)->op2.u.constant.value.str.val,len+1); DEFINED_OP(op->op1)->op2.u.constant.value.str.val[len-1] = (char) op->op2.u.constant.value.lval; DEFINED_OP(op->op1)->op2.u.constant.value.str.val[len] = 0; DEFINED_OP(op->op1)->op2.u.constant.value.str.len = len; memcpy(&DEFINED_OP(op->op1)->result, &op->result, sizeof(op->result)); if (DEFINED_OP(op->op1)->op1.op_type == DEFINED_OP(op->op1)->result.op_type && DEFINED_OP(op->op1)->op1.u.var == DEFINED_OP(op->op1)->result.u.var) { DEFINED_OP(op->op1)->opcode = ZEND_ADD_STRING; } SET_DEFINED(DEFINED_OP(op->op1)); SET_TO_NOP(op); /* ADD_CHAR($x,CONST,$y) + ADD_STRING($y,CONST,$z) => ADD_STRING($x, CONST, $z) ADD_CHAR($x,CONST,$y) + CONCAT($y,CONST,$z) => CONCAT($x, CONST, $z) */ } else if ((op->opcode == ZEND_ADD_STRING || op->opcode == ZEND_CONCAT) && op->op2.op_type == IS_CONST && op->op1.op_type == IS_TMP_VAR && IS_DEFINED(op->op1) && DEFINED_OP(op->op1)->opcode == ZEND_ADD_CHAR) { char ch = (char) DEFINED_OP(op->op1)->op2.u.constant.value.lval; size_t len; convert_to_string(&op->op2.u.constant); len = op->op2.u.constant.value.str.len + 1; DEFINED_OP(op->op1)->op2.u.constant.type = IS_STRING; DEFINED_OP(op->op1)->op2.u.constant.value.str.val = emalloc(len+1); DEFINED_OP(op->op1)->op2.u.constant.value.str.val[0] = ch; memcpy(DEFINED_OP(op->op1)->op2.u.constant.value.str.val+1, op->op2.u.constant.value.str.val, op->op2.u.constant.value.str.len); DEFINED_OP(op->op1)->op2.u.constant.value.str.val[len] = 0; DEFINED_OP(op->op1)->op2.u.constant.value.str.len = len; STR_FREE(op->op2.u.constant.value.str.val); memcpy(&DEFINED_OP(op->op1)->result, &op->result, sizeof(op->result)); DEFINED_OP(op->op1)->opcode = op->opcode; if (DEFINED_OP(op->op1)->op1.op_type == DEFINED_OP(op->op1)->result.op_type && DEFINED_OP(op->op1)->op1.u.var == DEFINED_OP(op->op1)->result.u.var) { DEFINED_OP(op->op1)->opcode = ZEND_ADD_STRING; } SET_DEFINED(DEFINED_OP(op->op1)); SET_TO_NOP(op); /* ADD_STRING($x,CONST,$y) + ADD_STRING($y,CONST,$z) => ADD_STRING($x, CONST, $z) ADD_STRING($x,CONST,$y) + CONCAT($y,CONST,$z) => CONCAT($x, CONST, $z) CONCAT($x,CONST,$y) + ADD_STRING($y,CONST,$z) => CONCAT($x, CONST, $z) CONCAT($x,CONST,$y) + CONCAT($y,CONST,$z) => CONCAT($x, CONST, $z) */ } else if ((op->opcode == ZEND_ADD_STRING || op->opcode == ZEND_CONCAT) && op->op2.op_type == IS_CONST && op->op1.op_type == IS_TMP_VAR && IS_DEFINED(op->op1) && (DEFINED_OP(op->op1)->opcode == ZEND_CONCAT || DEFINED_OP(op->op1)->opcode == ZEND_ADD_STRING) && DEFINED_OP(op->op1)->op2.op_type == IS_CONST) { size_t len; convert_to_string(&DEFINED_OP(op->op1)->op2.u.constant); convert_to_string(&op->op2.u.constant); len = DEFINED_OP(op->op1)->op2.u.constant.value.str.len + op->op2.u.constant.value.str.len; STR_REALLOC(DEFINED_OP(op->op1)->op2.u.constant.value.str.val,len+1); memcpy(DEFINED_OP(op->op1)->op2.u.constant.value.str.val+DEFINED_OP(op->op1)->op2.u.constant.value.str.len, op->op2.u.constant.value.str.val, op->op2.u.constant.value.str.len); DEFINED_OP(op->op1)->op2.u.constant.value.str.val[len] = 0; DEFINED_OP(op->op1)->op2.u.constant.value.str.len = len; STR_FREE(op->op2.u.constant.value.str.val); memcpy(&DEFINED_OP(op->op1)->result, &op->result, sizeof(op->result)); if (op->opcode == ZEND_CONCAT) { DEFINED_OP(op->op1)->opcode = ZEND_CONCAT; } if (DEFINED_OP(op->op1)->op1.op_type == DEFINED_OP(op->op1)->result.op_type && DEFINED_OP(op->op1)->op1.u.var == DEFINED_OP(op->op1)->result.u.var) { DEFINED_OP(op->op1)->opcode = ZEND_ADD_STRING; } SET_DEFINED(DEFINED_OP(op->op1)); SET_TO_NOP(op); /* FETCH_X local("GLOBALS"),$x => FETCH_X global($y),$z FETCH_DIM_X $x,$y,$z NOP */ } else if ( #ifndef ZEND_ENGINE_2 op_array->uses_globals && #endif ((op->opcode == ZEND_FETCH_DIM_R && op->op1.op_type == IS_VAR && /*??? op->extended_value == ZEND_FETCH_STANDARD &&*/ IS_DEFINED(op->op1) && DEFINED_OP(op->op1)->opcode == ZEND_FETCH_R) || (op->opcode == ZEND_FETCH_DIM_W && op->op1.op_type == IS_VAR && IS_DEFINED(op->op1) && DEFINED_OP(op->op1)->opcode == ZEND_FETCH_W) || (op->opcode == ZEND_FETCH_DIM_RW && op->op1.op_type == IS_VAR && IS_DEFINED(op->op1) && DEFINED_OP(op->op1)->opcode == ZEND_FETCH_RW) || (op->opcode == ZEND_FETCH_DIM_IS && op->op1.op_type == IS_VAR && IS_DEFINED(op->op1) && DEFINED_OP(op->op1)->opcode == ZEND_FETCH_IS) || (op->opcode == ZEND_FETCH_DIM_FUNC_ARG && op->op1.op_type == IS_VAR && IS_DEFINED(op->op1) && DEFINED_OP(op->op1)->opcode == ZEND_FETCH_FUNC_ARG) || (op->opcode == ZEND_FETCH_DIM_UNSET && op->op1.op_type == IS_VAR && IS_DEFINED(op->op1) && DEFINED_OP(op->op1)->opcode == ZEND_FETCH_UNSET)) && #ifdef ZEND_ENGINE_2 FETCH_TYPE(DEFINED_OP(op->op1)) == ZEND_FETCH_GLOBAL && #else FETCH_TYPE(DEFINED_OP(op->op1)) == ZEND_FETCH_LOCAL && #endif DEFINED_OP(op->op1)->op1.op_type == IS_CONST && DEFINED_OP(op->op1)->op1.u.constant.type == IS_STRING && DEFINED_OP(op->op1)->op1.u.constant.value.str.len == (sizeof("GLOBALS")-1) && memcmp(DEFINED_OP(op->op1)->op1.u.constant.value.str.val, "GLOBALS", sizeof("GLOBALS")-1) == 0) { zend_op *x = DEFINED_OP(op->op1); SET_UNDEFINED(op->op1); STR_FREE(x->op1.u.constant.value.str.val); FETCH_TYPE(x) = ZEND_FETCH_GLOBAL; memcpy(&x->op1,&op->op2,sizeof(znode)); memcpy(&x->result,&op->result,sizeof(znode)); SET_DEFINED(x); SET_TO_NOP(op); #ifndef ZEND_ENGINE_2 if (x->op1.op_type == IS_VAR) { memcpy(&op->op1,&x->op1,sizeof(znode)); op->opcode = ZEND_SWITCH_FREE; op->extended_value = 0; } #endif #ifdef ZEND_ENGINE_2 /* FETCH_IS local("GLOBALS"),$x ISSET_ISEMPTY_VAR $y(global),res ISSET_ISEMPTY_DIM_OBJ $x,$y,$res => NOP */ } else if (op->opcode == ZEND_ISSET_ISEMPTY_DIM_OBJ && op->op1.op_type == IS_VAR && IS_DEFINED(op->op1) && DEFINED_OP(op->op1)->opcode == ZEND_FETCH_IS && FETCH_TYPE(DEFINED_OP(op->op1)) == ZEND_FETCH_GLOBAL && DEFINED_OP(op->op1)->op1.op_type == IS_CONST && DEFINED_OP(op->op1)->op1.u.constant.type == IS_STRING && DEFINED_OP(op->op1)->op1.u.constant.value.str.len == (sizeof("GLOBALS")-1) && memcmp(DEFINED_OP(op->op1)->op1.u.constant.value.str.val, "GLOBALS", sizeof("GLOBALS")-1) == 0) { zend_op* x = DEFINED_OP(op->op1); STR_FREE(x->op1.u.constant.value.str.val); x->opcode = ZEND_ISSET_ISEMPTY_VAR; x->extended_value = op->extended_value; FETCH_TYPE(x) = ZEND_FETCH_GLOBAL; memcpy(&x->op1,&op->op2,sizeof(znode)); memcpy(&x->result,&op->result,sizeof(znode)); SET_DEFINED(x); SET_TO_NOP(op); #endif } else if (op->opcode == ZEND_FREE && op->op1.op_type == IS_TMP_VAR && IS_DEFINED(op->op1)) { /* POST_INC + FREE => PRE_INC */ if (DEFINED_OP(op->op1)->opcode == ZEND_POST_INC) { DEFINED_OP(op->op1)->opcode = ZEND_PRE_INC; DEFINED_OP(op->op1)->result.op_type = IS_VAR; DEFINED_OP(op->op1)->result.u.EA.type |= EXT_TYPE_UNUSED; SET_UNDEFINED(op->op1); SET_TO_NOP(op); /* POST_DEC + FREE => PRE_DEC */ } else if (DEFINED_OP(op->op1)->opcode == ZEND_POST_DEC) { DEFINED_OP(op->op1)->opcode = ZEND_PRE_DEC; DEFINED_OP(op->op1)->result.op_type = IS_VAR; DEFINED_OP(op->op1)->result.u.EA.type |= EXT_TYPE_UNUSED; SET_UNDEFINED(op->op1); SET_TO_NOP(op); /* PRINT + FREE => ECHO */ } else if (DEFINED_OP(op->op1)->opcode == ZEND_PRINT) { DEFINED_OP(op->op1)->opcode = ZEND_ECHO; DEFINED_OP(op->op1)->result.op_type = IS_UNUSED; SET_UNDEFINED(op->op1); SET_TO_NOP(op); /* BOOL + FREE => NOP + NOP */ } else if (DEFINED_OP(op->op1)->opcode == ZEND_BOOL) { SET_TO_NOP(DEFINED_OP(op->op1)); SET_UNDEFINED(op->op1); SET_TO_NOP(op); } /* CMP + BOOL => CMP + NOP */ } else if (op->opcode == ZEND_BOOL && op->op1.op_type == IS_TMP_VAR && (!global[VAR_NUM(op->op1.u.var)] || (op->result.op_type == IS_TMP_VAR && op->op1.u.var == op->result.u.var)) && IS_DEFINED(op->op1) && (DEFINED_OP(op->op1)->opcode == ZEND_IS_IDENTICAL || DEFINED_OP(op->op1)->opcode == ZEND_IS_NOT_IDENTICAL || DEFINED_OP(op->op1)->opcode == ZEND_IS_EQUAL || DEFINED_OP(op->op1)->opcode == ZEND_IS_NOT_EQUAL || DEFINED_OP(op->op1)->opcode == ZEND_IS_SMALLER || DEFINED_OP(op->op1)->opcode == ZEND_IS_SMALLER_OR_EQUAL)) { memcpy(&DEFINED_OP(op->op1)->result, &op->result, sizeof(op->result)); SET_DEFINED(DEFINED_OP(op->op1)); SET_TO_NOP(op); /* BOOL + BOOL => NOP + BOOL BOOL + BOOL_NOT => NOP + BOOL_NOT BOOL + JMP... => NOP + JMP... */ } else if ((op->opcode == ZEND_BOOL || op->opcode == ZEND_BOOL_NOT || op->opcode == ZEND_JMPZ|| op->opcode == ZEND_JMPNZ || op->opcode == ZEND_JMPZNZ || op->opcode == ZEND_JMPZ_EX || op->opcode == ZEND_JMPNZ_EX) && op->op1.op_type == IS_TMP_VAR && (!global[VAR_NUM(op->op1.u.var)] || (op->result.op_type == IS_TMP_VAR && op->op1.u.var == op->result.u.var)) && IS_DEFINED(op->op1) && DEFINED_OP(op->op1)->opcode == ZEND_BOOL) { zend_op *x = DEFINED_OP(op->op1); SET_UNDEFINED(op->op1); memcpy(&op->op1, &x->op1, sizeof(op->op1)); SET_TO_NOP(x); /* BOOL_NOT + BOOL => NOP + BOOL_NOT BOOL_NOT + BOOL_NOT => NOP + BOOL BOOL_NOT + JMP... => NOP + JMP[n]... */ } else if ((op->opcode == ZEND_BOOL || op->opcode == ZEND_BOOL_NOT || op->opcode == ZEND_JMPZ|| op->opcode == ZEND_JMPNZ) && op->op1.op_type == IS_TMP_VAR && (!global[VAR_NUM(op->op1.u.var)] || (op->result.op_type == IS_TMP_VAR && op->op1.u.var == op->result.u.var)) && IS_DEFINED(op->op1) && DEFINED_OP(op->op1)->opcode == ZEND_BOOL_NOT) { zend_op *x = DEFINED_OP(op->op1); switch (op->opcode) { case ZEND_BOOL: op->opcode = ZEND_BOOL_NOT; break; case ZEND_BOOL_NOT: op->opcode = ZEND_BOOL; break; case ZEND_JMPZ: op->opcode = ZEND_JMPNZ; break; case ZEND_JMPNZ: op->opcode = ZEND_JMPZ; break; } SET_UNDEFINED(op->op1); memcpy(&op->op1, &x->op1, sizeof(op->op1)); SET_TO_NOP(x); /* function_exists(STR) or is_callable(STR) */ } else if ((op->opcode == ZEND_BOOL || op->opcode == ZEND_BOOL_NOT || op->opcode == ZEND_JMPZ|| op->opcode == ZEND_JMPNZ || op->opcode == ZEND_JMPZNZ || op->opcode == ZEND_JMPZ_EX || op->opcode == ZEND_JMPNZ_EX) && op->op1.op_type == IS_VAR && !global[VAR_NUM(op->op1.u.var)] && IS_DEFINED(op->op1) && DEFINED_OP(op->op1)->opcode == ZEND_DO_FCALL && DEFINED_OP(op->op1)->extended_value == 1 && DEFINED_OP(op->op1)->op1.op_type == IS_CONST && DEFINED_OP(op->op1)->op1.u.constant.type == IS_STRING) { zend_op* call = DEFINED_OP(op->op1); zend_op* send = call-1; if (send->opcode == ZEND_SEND_VAL && send->extended_value == ZEND_DO_FCALL && send->op1.op_type == IS_CONST && send->op1.u.constant.type == IS_STRING && (strcmp(call->op1.u.constant.value.str.val,"function_exists") == 0 || strcmp(call->op1.u.constant.value.str.val,"is_callable") == 0)) { if (opt_function_exists(send->op1.u.constant.value.str.val, send->op1.u.constant.value.str.len TSRMLS_CC)) { SET_UNDEFINED(op->op1); zval_dtor(&send->op1.u.constant); SET_TO_NOP(send); zval_dtor(&call->op1.u.constant); SET_TO_NOP(call); op->op1.op_type = IS_CONST; op->op1.u.constant.type = IS_BOOL; op->op1.u.constant.value.lval = 1; } } else if (send->opcode == ZEND_SEND_VAL && send->extended_value == ZEND_DO_FCALL && send->op1.op_type == IS_CONST && send->op1.u.constant.type == IS_STRING && strcmp(call->op1.u.constant.value.str.val,"extension_loaded") == 0) { if (opt_extension_loaded(send->op1.u.constant.value.str.val, send->op1.u.constant.value.str.len TSRMLS_CC)) { SET_UNDEFINED(op->op1); zval_dtor(&send->op1.u.constant); SET_TO_NOP(send); zval_dtor(&call->op1.u.constant); SET_TO_NOP(call); op->op1.op_type = IS_CONST; op->op1.u.constant.type = IS_BOOL; op->op1.u.constant.value.lval = 1; } } else if (send->opcode == ZEND_SEND_VAL && send->extended_value == ZEND_DO_FCALL && send->op1.op_type == IS_CONST && send->op1.u.constant.type == IS_STRING && strcmp(call->op1.u.constant.value.str.val,"defined") == 0) { zend_constant *c = NULL; if (opt_get_constant(send->op1.u.constant.value.str.val, send->op1.u.constant.value.str.len, &c TSRMLS_CC) && c != NULL && ((c->flags & CONST_PERSISTENT) != 0)) { SET_UNDEFINED(op->op1); zval_dtor(&send->op1.u.constant); SET_TO_NOP(send); zval_dtor(&call->op1.u.constant); SET_TO_NOP(call); op->op1.op_type = IS_CONST; op->op1.u.constant.type = IS_BOOL; op->op1.u.constant.value.lval = 1; } } /* QM_ASSIGN($x,$x) => NOP */ } else if (op->opcode == ZEND_QM_ASSIGN && op->op1.op_type == IS_TMP_VAR && op->result.op_type == IS_TMP_VAR && op->op1.u.var == op->result.u.var) { SET_TO_NOP(op); /* ?(,,$tmp_x) +QM_ASSIGN($tmp_x,$tmp_y) => ?(,,$tmp_y) + NOP */ } else if (op->opcode == ZEND_QM_ASSIGN && op->op1.op_type == IS_TMP_VAR && !global[VAR_NUM(op->op1.u.var)] && op->op1.u.var != op->result.u.var && IS_DEFINED(op->op1)) { zend_op *x = DEFINED_OP(op->op1); if (x->opcode != ZEND_ADD_ARRAY_ELEMENT && x->opcode != ZEND_ADD_STRING && x->opcode != ZEND_ADD_CHAR && x->opcode != ZEND_ADD_VAR) { SET_UNDEFINED(op->op1); memcpy(&x->result, &op->result, sizeof(op->result)); SET_DEFINED(x); SET_TO_NOP(op); } /* ECHO(const) + ECHO(const) => ECHO(const) */ } else if (prev != NULL && op->opcode == ZEND_ECHO && op->op1.op_type == IS_CONST && prev->opcode == ZEND_ECHO && prev->op1.op_type == IS_CONST) { int len; convert_to_string(&prev->op1.u.constant); convert_to_string(&op->op1.u.constant); len = prev->op1.u.constant.value.str.len + op->op1.u.constant.value.str.len; STR_REALLOC(prev->op1.u.constant.value.str.val,len+1); memcpy(prev->op1.u.constant.value.str.val+prev->op1.u.constant.value.str.len, op->op1.u.constant.value.str.val, op->op1.u.constant.value.str.len); prev->op1.u.constant.value.str.val[len] = 0; prev->op1.u.constant.value.str.len = len; STR_FREE(op->op1.u.constant.value.str.val); SET_TO_NOP(op); /* END_SILENCE + BEGIN_SILENCE => NOP + NOP */ } else if (prev != NULL && prev->opcode == ZEND_END_SILENCE && op->opcode == ZEND_BEGIN_SILENCE) { zend_op *x = op+1; while (x < end) { if (x->opcode == ZEND_END_SILENCE && x->op1.u.var == op->result.u.var) { x->op1.u.var = prev->op1.u.var; SET_TO_NOP(prev); SET_TO_NOP(op); break; } x++; } /* BEGIN_SILENCE + END_SILENCE => NOP + NOP */ } else if (prev != NULL && prev->opcode == ZEND_BEGIN_SILENCE && op->opcode == ZEND_END_SILENCE && prev->result.u.var == op->op1.u.var) { SET_TO_NOP(prev); SET_TO_NOP(op); /* SEND_VAR_NO_REF => SEND_VAR (cond) */ } else if (op->opcode == ZEND_SEND_VAR_NO_REF && (op->extended_value & ZEND_ARG_COMPILE_TIME_BOUND) && !(op->extended_value & ZEND_ARG_SEND_BY_REF)) { op->opcode = ZEND_SEND_VAR; op->extended_value = ZEND_DO_FCALL; /* INIT_FCALL_BY_NAME + DO_FCALL_BY_NAME => DO_FCALL $x */ } else if (prev != NULL && op->opcode == ZEND_DO_FCALL_BY_NAME && op->extended_value == 0 && op->op1.op_type == IS_CONST && op->op1.u.constant.type == IS_STRING && prev->opcode == ZEND_INIT_FCALL_BY_NAME && prev->op1.op_type == IS_UNUSED && prev->op2.op_type == IS_CONST && prev->op2.u.constant.type == IS_STRING && op->op1.u.constant.value.str.len == prev->op2.u.constant.value.str.len && (memcmp(op->op1.u.constant.value.str.val,prev->op2.u.constant.value.str.val,op->op1.u.constant.value.str.len) == 0)) { op->opcode = ZEND_DO_FCALL; STR_FREE(prev->op2.u.constant.value.str.val); SET_TO_NOP(prev); /* SEND_REF $x + FCALL "reset" => FE_RESET $x */ } #if 0 // this makes php 5 and php >= 4.3.11 go in an endless loop with reset(null) else if (prev != NULL && prev->opcode == ZEND_SEND_REF && prev->extended_value == ZEND_DO_FCALL && prev->op1.op_type == IS_VAR && prev->op2.u.opline_num == 1 && op->opcode == ZEND_DO_FCALL && op->extended_value == 1 && op->op1.op_type == IS_CONST && op->op1.u.constant.type == IS_STRING && op->op1.u.constant.value.str.len == (sizeof("reset")-1) && (memcmp(op->op1.u.constant.value.str.val, "reset", sizeof("reset")-1) == 0) && op->result.op_type == IS_VAR && ((op->result.u.EA.type & EXT_TYPE_UNUSED) != 0)) { STR_FREE(op->op1.u.constant.value.str.val); prev->opcode = ZEND_FE_RESET; prev->extended_value = 1; prev->op2.op_type = IS_UNUSED; memcpy(&prev->result, &op->result, sizeof(op->result)); if (op->result.op_type == IS_VAR && (op->result.u.EA.type & EXT_TYPE_UNUSED) == 0) { SET_DEFINED(prev); SET_TO_NOP(op); } else { prev->result.u.EA.type &= ~EXT_TYPE_UNUSED; op->opcode = ZEND_SWITCH_FREE; op->extended_value = 1; memcpy(&op->op1,&prev->result,sizeof(prev->result)); op->op2.op_type = IS_UNUSED; op->result.op_type = IS_UNUSED; } } #endif /* $a = $a + ? => $a+= ? */ if (op->opcode == ZEND_ASSIGN && op->op1.op_type == IS_VAR && op->op2.op_type == IS_TMP_VAR && IS_DEFINED(op->op1) && IS_DEFINED(op->op2)) { zend_op* l = DEFINED_OP(op->op1); zend_op* r = DEFINED_OP(op->op2); if (l->opcode == ZEND_FETCH_W && l->op1.op_type == IS_CONST && l->op1.u.constant.type == IS_STRING && (r->opcode == ZEND_ADD || r->opcode == ZEND_SUB || r->opcode == ZEND_MUL || r->opcode == ZEND_DIV || r->opcode == ZEND_MOD || r->opcode == ZEND_SL || r->opcode == ZEND_SR || r->opcode == ZEND_CONCAT || r->opcode == ZEND_BW_OR || r->opcode == ZEND_BW_AND || r->opcode == ZEND_BW_XOR) && r->op1.op_type == IS_VAR && IS_DEFINED(r->op1)) { zend_op* rl = DEFINED_OP(r->op1); if (rl->opcode == ZEND_FETCH_R && rl->op1.op_type == IS_CONST && rl->op1.u.constant.type == IS_STRING && FETCH_TYPE(rl) == FETCH_TYPE(l) && l->op1.u.constant.value.str.len == rl->op1.u.constant.value.str.len && memcmp(l->op1.u.constant.value.str.val, rl->op1.u.constant.value.str.val, l->op1.u.constant.value.str.len) == 0) { switch (r->opcode) { case ZEND_ADD: op->opcode = ZEND_ASSIGN_ADD; break; case ZEND_SUB: op->opcode = ZEND_ASSIGN_SUB; break; case ZEND_MUL: op->opcode = ZEND_ASSIGN_MUL; break; case ZEND_DIV: op->opcode = ZEND_ASSIGN_DIV; break; case ZEND_MOD: op->opcode = ZEND_ASSIGN_MOD; break; case ZEND_SL: op->opcode = ZEND_ASSIGN_SL; break; case ZEND_SR: op->opcode = ZEND_ASSIGN_SR; break; case ZEND_CONCAT: op->opcode = ZEND_ASSIGN_CONCAT; break; case ZEND_BW_OR: op->opcode = ZEND_ASSIGN_BW_OR; break; case ZEND_BW_AND: op->opcode = ZEND_ASSIGN_BW_AND; break; case ZEND_BW_XOR: op->opcode = ZEND_ASSIGN_BW_XOR; break; default: break; } memcpy(&op->op2, &r->op2, sizeof(op->op2)); l->opcode = ZEND_FETCH_RW; SET_TO_NOP(r); STR_FREE(rl->op1.u.constant.value.str.val); SET_TO_NOP(rl); } } } if (pass == 1) { /* FETCH_W var,$x + ASSIGN $x,?,_ + FETCH_R var,$y => FETCH_W var,$x + ASSIGN $x,?,$y */ if (op->opcode == ZEND_UNSET_VAR || op->opcode == ZEND_DO_FCALL || op->opcode == ZEND_DO_FCALL_BY_NAME || op->opcode == ZEND_POST_INC || op->opcode == ZEND_POST_DEC || #ifndef ZEND_ENGINE_2_1 /* Pre-PHP 5.1 only */ op->opcode == ZEND_UNSET_DIM_OBJ || #else op->opcode == ZEND_UNSET_DIM || op->opcode == ZEND_UNSET_OBJ || #endif op->opcode == ZEND_INCLUDE_OR_EVAL #ifdef ZEND_ENGINE_2 || op->opcode == ZEND_ASSIGN_DIM || op->opcode == ZEND_ASSIGN_OBJ #endif ) { zend_hash_clean(&assigns); zend_hash_clean(&fetch_dim); } else if (op->opcode == ZEND_ASSIGN_REF || op->opcode == ZEND_ASSIGN || op->opcode == ZEND_PRE_INC || op->opcode == ZEND_PRE_DEC || op->opcode == ZEND_ASSIGN_ADD || op->opcode == ZEND_ASSIGN_SUB || op->opcode == ZEND_ASSIGN_MUL || op->opcode == ZEND_ASSIGN_DIV || op->opcode == ZEND_ASSIGN_MOD || op->opcode == ZEND_ASSIGN_SL || op->opcode == ZEND_ASSIGN_SR || op->opcode == ZEND_ASSIGN_CONCAT || op->opcode == ZEND_ASSIGN_BW_OR || op->opcode == ZEND_ASSIGN_BW_AND || op->opcode == ZEND_ASSIGN_BW_XOR) { zend_hash_clean(&assigns); zend_hash_clean(&fetch_dim); if ((op->result.u.EA.type & EXT_TYPE_UNUSED) != 0 && op->op1.op_type == IS_VAR && #ifdef ZEND_ENGINE_2 op->extended_value != ZEND_ASSIGN_DIM && op->extended_value != ZEND_ASSIGN_OBJ && #endif IS_DEFINED(op->op1)) { zend_op *x = DEFINED_OP(op->op1); if ((x->opcode == ZEND_FETCH_W || x->opcode == ZEND_FETCH_RW) && x->op1.op_type == IS_CONST && x->op1.u.constant.type == IS_STRING) { union { zend_op *v; void *ptr; } op_copy; char *s = emalloc(x->op1.u.constant.value.str.len+2); op_copy.v = op; memcpy(s,x->op1.u.constant.value.str.val,x->op1.u.constant.value.str.len); s[x->op1.u.constant.value.str.len] = (char)FETCH_TYPE(x); s[x->op1.u.constant.value.str.len+1] = '\0'; zend_hash_update(&assigns, s, x->op1.u.constant.value.str.len+2, &op_copy.ptr, sizeof(void*), NULL); efree(s); } } } else if ((op->opcode == ZEND_FETCH_R || op->opcode == ZEND_FETCH_IS) && !global[VAR_NUM(op->result.u.var)] && op->op1.op_type == IS_CONST && op->op1.u.constant.type == IS_STRING) { union { zend_op *v; void *ptr; } x; char *s = emalloc(op->op1.u.constant.value.str.len+2); memcpy(s,op->op1.u.constant.value.str.val,op->op1.u.constant.value.str.len); s[op->op1.u.constant.value.str.len] = (char)FETCH_TYPE(op); s[op->op1.u.constant.value.str.len+1] = '\0'; if (zend_hash_find(&assigns, s, op->op1.u.constant.value.str.len+2, &x.ptr) == SUCCESS) { x.v = *(zend_op**)x.v; memcpy(&x.v->result, &op->result, sizeof(op->result)); x.v->result.u.EA.type = 0; SET_DEFINED(x.v); zend_hash_del(&assigns, s, op->op1.u.constant.value.str.len+2); STR_FREE(op->op1.u.constant.value.str.val); SET_TO_NOP(op); } efree(s); } else if (op->opcode == ZEND_FETCH_DIM_R && op->extended_value != ZEND_FETCH_ADD_LOCK && op->op1.op_type == IS_VAR && IS_DEFINED(op->op1)) { zend_op *x = DEFINED_OP(op->op1); while ((x->opcode == ZEND_ASSIGN_REF || x->opcode == ZEND_ASSIGN || x->opcode == ZEND_PRE_INC || x->opcode == ZEND_PRE_DEC || x->opcode == ZEND_ASSIGN_ADD || x->opcode == ZEND_ASSIGN_SUB || x->opcode == ZEND_ASSIGN_MUL || x->opcode == ZEND_ASSIGN_DIV || x->opcode == ZEND_ASSIGN_MOD || x->opcode == ZEND_ASSIGN_SL || x->opcode == ZEND_ASSIGN_SR || x->opcode == ZEND_ASSIGN_CONCAT || x->opcode == ZEND_ASSIGN_BW_OR || x->opcode == ZEND_ASSIGN_BW_AND || x->opcode == ZEND_ASSIGN_BW_XOR) && x->op1.op_type == IS_VAR && IS_DEFINED(x->op1)) { x = DEFINED_OP(x->op1); } if ((x->opcode == ZEND_FETCH_R || x->opcode == ZEND_FETCH_W || x->opcode == ZEND_FETCH_RW) && x->op1.op_type == IS_CONST && x->op1.u.constant.type == IS_STRING) { union { zend_op *v; void *ptr; } y; union { zend_op *v; void *ptr; } op_copy; char *s = emalloc(x->op1.u.constant.value.str.len+2); op_copy.v = op; memcpy(s,x->op1.u.constant.value.str.val,x->op1.u.constant.value.str.len); s[x->op1.u.constant.value.str.len] = (char)FETCH_TYPE(x); s[x->op1.u.constant.value.str.len+1] = '\0'; if (zend_hash_find(&fetch_dim, s, x->op1.u.constant.value.str.len+2, &y.ptr) == SUCCESS) { y.v = *(zend_op**)y.v; y.v->extended_value = ZEND_FETCH_ADD_LOCK; zend_hash_update(&fetch_dim, s, x->op1.u.constant.value.str.len+2, &op_copy.ptr, sizeof(void*), NULL); SET_UNDEFINED(x->result); STR_FREE(x->op1.u.constant.value.str.val); SET_TO_NOP(x); memcpy(&op->op1, &y.v->op1, sizeof(op->op1)); } else { zend_hash_update(&fetch_dim, s, x->op1.u.constant.value.str.len+2, &op_copy.ptr, sizeof(void*), NULL); } efree(s); } } } if (op->opcode != ZEND_NOP) { prev = op; } if ((op->result.op_type == IS_VAR && #ifdef ZEND_ENGINE_2 (op->opcode == ZEND_RECV || op->opcode == ZEND_RECV_INIT || (op->result.u.EA.type & EXT_TYPE_UNUSED) == 0)) || #else (op->result.u.EA.type & EXT_TYPE_UNUSED) == 0) || #endif (op->result.op_type == IS_TMP_VAR)) { if (op->opcode == ZEND_RECV || op->opcode == ZEND_RECV_INIT) { SET_UNDEFINED(op->result); } else { SET_DEFINED(op); } } ++op; } /* NOP Removing */ op = bb->start; end = op + bb->len; while (op < end) { if (op->opcode == ZEND_NOP) { zend_op *next = op+1; while (next < end && next->opcode == ZEND_NOP) next++; if (next < end) { memcpy(op,next,(end-next) * sizeof(zend_op)); while (next > op) { --end; SET_TO_NOP(end); --next; } } else { end -= (next-op); } } else { ++op; } } bb->len = end - bb->start; zend_hash_destroy(&fetch_dim); zend_hash_destroy(&assigns); free_alloca(Ts); } /* * Find All Basic Blocks in op_array and build Control Flow Graph (CFG) */ static int build_cfg(zend_op_array *op_array, BB* bb) { zend_op* op = op_array->opcodes; int len = op_array->last; int line_num; BB* p; int remove_brk_cont_array = 1; #ifndef ZEND_ENGINE_2 int* overload_var = do_alloca(op_array->T * sizeof(int)); memset(overload_var,-1,op_array->T * sizeof(int)); #else /* HOESH: Just to use later... */ zend_uint innermost_ketchup; /* HOESH: Mark try & catch blocks */ if (op_array->last_try_catch > 0) { int i; zend_try_catch_element* tc_element = op_array->try_catch_array; for (i=0; ilast_try_catch; i++, tc_element++) { bb[tc_element->try_op].start = &op_array->opcodes[tc_element->try_op]; bb[tc_element->try_op].protect_merge = 1; bb[tc_element->catch_op].start = &op_array->opcodes[tc_element->catch_op]; bb[tc_element->catch_op].protect_merge = 1; } } #endif /* Find Starts of Basic Blocks */ bb[0].start = op; for (line_num=0; line_num < len; op++,line_num++) { #ifdef ZEND_ENGINE_2 const opcode_dsc* dsc = get_opcode_dsc(op->opcode); if (dsc != NULL) { if ((dsc->ops & OP1_MASK) == OP1_UCLASS) { if (op->op1.op_type != IS_UNUSED) { op->op1.op_type = IS_VAR; } } else if ((dsc->ops & OP1_MASK) == OP1_CLASS) { op->op1.op_type = IS_VAR; } else if ((dsc->ops & OP1_MASK) == OP1_UNUSED) { op->op1.op_type = IS_UNUSED; } if ((dsc->ops & OP2_MASK) == OP2_CLASS) { op->op2.op_type = IS_VAR; } else if ((dsc->ops & OP2_MASK) == OP2_UNUSED) { op->op2.op_type = IS_UNUSED; } else if ((dsc->ops & OP2_MASK) == OP2_FETCH && op->op2.u.EA.type == ZEND_FETCH_STATIC_MEMBER) { op->op2.op_type = IS_VAR; } if ((dsc->ops & RES_MASK) == RES_CLASS) { op->result.op_type = IS_VAR; op->result.u.EA.type &= ~EXT_TYPE_UNUSED; } else if ((dsc->ops & RES_MASK) == RES_UNUSED) { op->result.op_type = IS_UNUSED; } } #endif switch(op->opcode) { case ZEND_RETURN: case ZEND_EXIT: bb[line_num+1].start = op+1; break; case ZEND_JMP: bb[op->op1.u.opline_num].start = &op_array->opcodes[op->op1.u.opline_num]; bb[line_num+1].start = op+1; break; case ZEND_JMPZNZ: bb[op->extended_value].start = &op_array->opcodes[op->extended_value]; bb[op->op2.u.opline_num].start = &op_array->opcodes[op->op2.u.opline_num]; bb[line_num+1].start = op+1; break; case ZEND_JMPZ: case ZEND_JMPNZ: case ZEND_JMPZ_EX: case ZEND_JMPNZ_EX: #ifndef ZEND_ENGINE_2_1 /* Pre-PHP 5.1 only */ case ZEND_JMP_NO_CTOR: #else case ZEND_NEW: case ZEND_FE_RESET: #endif case ZEND_FE_FETCH: bb[line_num+1].start = op+1; bb[op->op2.u.opline_num].start = &op_array->opcodes[op->op2.u.opline_num]; break; case ZEND_BRK: /* Replace BRK by JMP */ if (op->op1.u.opline_num == -1) { } else if (op->op2.op_type == IS_CONST && op->op2.u.constant.type == IS_LONG) { int level = op->op2.u.constant.value.lval; zend_uint offset = op->op1.u.opline_num; zend_brk_cont_element *jmp_to; do { if (offset < 0 || offset >= op_array->last_brk_cont) { goto brk_failed; } jmp_to = &op_array->brk_cont_array[offset]; if (level>1 && (op_array->opcodes[jmp_to->brk].opcode == ZEND_SWITCH_FREE || op_array->opcodes[jmp_to->brk].opcode == ZEND_FREE)) { goto brk_failed; } offset = jmp_to->parent; } while (--level > 0); op->opcode = ZEND_JMP; op->op1.u.opline_num = jmp_to->brk; op->op2.op_type = IS_UNUSED; op->extended_value = ZEND_BRK; /* Mark the opcode as former ZEND_BRK */ bb[op->op1.u.opline_num].start = &op_array->opcodes[jmp_to->brk]; } else { brk_failed: remove_brk_cont_array = 0; } bb[line_num+1].start = op+1; break; case ZEND_CONT: /* Replace CONT by JMP */ if (op->op1.u.opline_num == -1) { } else if (op->op2.op_type == IS_CONST && op->op2.u.constant.type == IS_LONG) { int level = op->op2.u.constant.value.lval; zend_uint offset = op->op1.u.opline_num; zend_brk_cont_element *jmp_to; do { if (offset < 0 || offset >= op_array->last_brk_cont) { goto cont_failed; } jmp_to = &op_array->brk_cont_array[offset]; if (level>1 && (op_array->opcodes[jmp_to->brk].opcode == ZEND_SWITCH_FREE || op_array->opcodes[jmp_to->brk].opcode == ZEND_FREE)) { goto cont_failed; } offset = jmp_to->parent; } while (--level > 0); op->opcode = ZEND_JMP; op->op1.u.opline_num = jmp_to->cont; op->op2.op_type = IS_UNUSED; op->extended_value = ZEND_CONT; /* Mark the opcode as former ZEND_CONT */ bb[op->op1.u.opline_num].start = &op_array->opcodes[jmp_to->cont]; } else { cont_failed: remove_brk_cont_array = 0; } bb[line_num+1].start = op+1; break; #ifdef ZEND_ENGINE_2 case ZEND_CATCH: bb[op->extended_value].start = &op_array->opcodes[op->extended_value]; bb[line_num+1].start = op+1; break; case ZEND_THROW: if (op->op2.u.opline_num != -1) { bb[op->op2.u.opline_num].start = &op_array->opcodes[op->op2.u.opline_num]; } bb[line_num+1].start = op+1; break; case ZEND_DO_FCALL: case ZEND_DO_FCALL_BY_NAME: if (op->op2.u.opline_num != -1) { bb[op->op2.u.opline_num].start = &op_array->opcodes[op->op2.u.opline_num]; bb[line_num+1].start = op+1; } break; #else case ZEND_INIT_FCALL_BY_NAME: if (op->op1.op_type == IS_VAR && op->result.op_type == IS_VAR) { overload_var[op->result.u.var] = op->op1.u.var; op->result.u.var = op->op1.u.var; } break; case ZEND_DO_FCALL_BY_NAME: if (op->op1.op_type == IS_VAR && overload_var[op->op1.u.var] >= 0) { op->op1.u.var = overload_var[op->op1.u.var]; } break; #endif case ZEND_UNSET_VAR: #ifndef ZEND_ENGINE_2_1 /* Pre-PHP 5.1 only */ case ZEND_UNSET_DIM_OBJ: op->result.op_type = IS_UNUSED; break; #else case ZEND_UNSET_DIM: op->result.op_type = IS_UNUSED; break; case ZEND_UNSET_OBJ: op->result.op_type = IS_UNUSED; break; #endif default: break; } } #ifndef ZEND_ENGINE_2 free_alloca(overload_var); #endif /* Find Lengths of Basic Blocks and build CFG */ p = bb; for (line_num=1; line_num < len; line_num++) { #ifdef ZEND_ENGINE_2 /* Calculate innermost CATCH op */ innermost_ketchup = 0; if (op_array->last_try_catch > 0) { int i; zend_try_catch_element* tc_element = op_array->try_catch_array; for (i=0; ilast_try_catch; i++, tc_element++) { // silence compile warnings. Line_num can't be negative here so casting is safe. if (tc_element->try_op <= (zend_uint)line_num-1 && (zend_uint)line_num-1 < tc_element->catch_op && (innermost_ketchup == 0 || innermost_ketchup > tc_element->catch_op) ) { innermost_ketchup = tc_element->catch_op; } } } #endif if (bb[line_num].start != NULL) { p->len = bb[line_num].start - p->start; p->next = &bb[line_num]; op = &p->start[p->len-1]; switch (op->opcode) { case ZEND_JMP: p->jmp_1 = &bb[op->op1.u.opline_num]; #ifdef ZEND_ENGINE_2 if (op->extended_value == ZEND_BRK || op->extended_value == ZEND_CONT) { /* This was a ZEND_BRK or ZEND_CONT opcode changed into a ZEND_JMP in an earlier stage. see comment above ZEND_BRK/ZEND_CONT below */ p->follow = (innermost_ketchup > 0) ? &bb[innermost_ketchup] : &bb[len-1]; /* clear extended_value again just for tidyness :) */ op->extended_value = 0; } # if (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION >= 2 && PHP_RELEASE_VERSION >= 1) || PHP_MAJOR_VERSION >= 6 /* php >= 5.2.1 introduces a ZEND_JMP before a ZEND_FETCH_CLASS and ZEND_CATCH this leaves those blocks intact */ else if ((op+1)->opcode == ZEND_FETCH_CLASS && (op+2)->opcode == ZEND_CATCH) { /* fix for #242 */ p->follow = &bb[line_num]; } # endif #endif break; case ZEND_JMPZNZ: p->jmp_2 = &bb[op->op2.u.opline_num]; p->jmp_ext = &bb[op->extended_value]; break; case ZEND_JMPZ: case ZEND_JMPNZ: case ZEND_JMPZ_EX: case ZEND_JMPNZ_EX: #ifndef ZEND_ENGINE_2_1 /* Pre-PHP 5.1 only */ case ZEND_JMP_NO_CTOR: #else case ZEND_NEW: case ZEND_FE_RESET: #endif case ZEND_FE_FETCH: p->jmp_2 = &bb[op->op2.u.opline_num]; p->follow = &bb[line_num]; break; case ZEND_RETURN: case ZEND_EXIT: case ZEND_BRK: case ZEND_CONT: #ifdef ZEND_ENGINE_2 /* HOESH: The control might flow to the innermost CATCH * op if an exception thrown earlier. We can follow to CATCH * to protect it against unnecessary K.O. In that case, * the last RETURN will hold HANDLE_EXCEPTION. * If no CATCH op toward, then glue it to the last opcode, * that is HANDLE_EXCEPTION. */ p->follow = (innermost_ketchup > 0) ? &bb[innermost_ketchup] : &bb[len-1]; break; case ZEND_DO_FCALL: case ZEND_DO_FCALL_BY_NAME: if (op->op2.u.opline_num != -1) { p->jmp_2 = &bb[op->op2.u.opline_num]; } p->follow = &bb[line_num]; break; case ZEND_CATCH: p->jmp_ext = &bb[op->extended_value]; p->follow = &bb[line_num]; break; case ZEND_THROW: if (op->op2.u.opline_num != -1) { p->jmp_2 = &bb[op->op2.u.opline_num]; } p->follow = &bb[line_num]; #endif break; default: p->follow = &bb[line_num]; } p = &bb[line_num]; } } p->len = (op_array->opcodes + op_array->last) - p->start; /* Remove Unused brk_cont_array (BRK and COND instructions replaced by JMP)*/ if (remove_brk_cont_array) { if (op_array->brk_cont_array != NULL) { efree(op_array->brk_cont_array); op_array->brk_cont_array = NULL; } op_array->last_brk_cont = 0; } return remove_brk_cont_array; } /* * Emits Optimized Code */ static void emit_cfg(zend_op_array *op_array, BB* bb) { /* Compacting Optimized Code */ BB* p = bb; zend_op* start = op_array->opcodes; zend_op* op = start; zend_op* end = op + op_array->last; while (p != NULL) { if (p->used) { if (p->len > 0 && op != p->start) { memcpy(op, p->start, p->len * sizeof(zend_op)); } p->start = op; op += p->len; } p = p->next; } op_array->last = op - start; op_array->start_op = NULL; while (op < end) { SET_TO_NOP(op); op++; } /* Set Branch Targets */ p = bb; while (p != NULL) { if (p->used) { if (p->jmp_1 != NULL) { p->start[p->len-1].op1.u.opline_num = p->jmp_1->start - start; } if (p->jmp_2 != NULL) { p->start[p->len-1].op2.u.opline_num = p->jmp_2->start - start; } if (p->jmp_ext != NULL) { p->start[p->len-1].extended_value = p->jmp_ext->start - start; } } p = p->next; } #ifdef ZEND_ENGINE_2 /* * HOESH: Reassign try & catch blocks */ if (op_array->last_try_catch>0) { int i; int last_try_catch = op_array->last_try_catch; zend_try_catch_element* old_tc_element = op_array->try_catch_array; for (i=0; ilast_try_catch; i++, old_tc_element++) { if (bb[old_tc_element->try_op].used && bb[old_tc_element->catch_op].used) { old_tc_element->try_op = bb[old_tc_element->try_op].start - start; old_tc_element->catch_op = bb[old_tc_element->catch_op].start - start; } else { old_tc_element->try_op = 0; old_tc_element->catch_op = 0; last_try_catch--; } } if (op_array->last_try_catch > last_try_catch) { zend_try_catch_element* new_tc_array = NULL; if (last_try_catch > 0) { /* Lost some try & catch blocks */ zend_try_catch_element* new_tc_element = emalloc(sizeof(zend_try_catch_element)*last_try_catch); new_tc_array = new_tc_element; old_tc_element = op_array->try_catch_array; for (i=0; ilast_try_catch; i++, old_tc_element++) { if (old_tc_element->try_op != old_tc_element->catch_op) { new_tc_element->try_op = old_tc_element->try_op; new_tc_element->catch_op = old_tc_element->catch_op; new_tc_element++; } } } /* Otherwise lost all try & catch blocks */ efree(op_array->try_catch_array); op_array->try_catch_array = new_tc_array; op_array->last_try_catch = last_try_catch; } } #endif } #define GET_REG(R) {\ if (assigned[(R)] < 0) {\ zend_uint j = 0;\ while (j < op_array->T) {\ if (reg_pool[j] == 0 &&\ (global[(R)] == 0 || used[j] == 0)) {\ reg_pool[j] = 1;\ assigned[(R)] = j;\ if (j+1 > n) {n = j+1;}\ break;\ }\ j++;\ }\ }\ used[assigned[(R)]] = 1;\ } #define FREE_REG(R) reg_pool[(R)] = 0; void reassign_registers(zend_op_array *op_array, BB* p, char *global) { zend_uint i; zend_uint n = 0; #ifndef ZEND_ENGINE_2 int uses_globals = 0; #endif int* assigned = do_alloca(op_array->T * sizeof(int)); char* reg_pool = do_alloca(op_array->T * sizeof(char)); char* used = do_alloca(op_array->T * sizeof(char)); for (i = 0; i < op_array->T; i++) { assigned[i] = -1; reg_pool[i] = 0; used[i] = 0; } while (p != NULL) { if (p->used && p->len > 0) { zend_op* start = p->start; zend_op* op = start + p->len; #ifdef ZEND_ENGINE_2 zend_op* op_data; #endif for (i = 0; i < op_array->T; i++) { if (!global[i]) { if (assigned[i] >= 0) {reg_pool[assigned[i]] = 0;} assigned[i] = -1; } } while (start < op) { --op; /* zend_printf("op=%d\n", op-op_array->opcodes); */ #ifdef ZEND_ENGINE_2 op_data = NULL; #else if (op_array->uses_globals && (op->opcode == ZEND_FETCH_R || op->opcode == ZEND_FETCH_W || op->opcode == ZEND_FETCH_RW || op->opcode == ZEND_FETCH_IS || op->opcode == ZEND_FETCH_FUNC_ARG || op->opcode == ZEND_FETCH_UNSET) && op->op1.op_type == IS_CONST && op->op1.u.constant.type == IS_STRING && op->op1.u.constant.value.str.len == (sizeof("GLOBALS")-1) && memcmp(op->op1.u.constant.value.str.val, "GLOBALS", sizeof("GLOBALS")-1) == 0) { uses_globals = 1; } #endif if (op->opcode == ZEND_DO_FCALL_BY_NAME && op->op1.op_type == IS_CONST) { zval_dtor(&op->op1.u.constant); op->op1.op_type = IS_UNUSED; } if (op->op1.op_type == IS_VAR || op->op1.op_type == IS_TMP_VAR) { int r = VAR_NUM(op->op1.u.var); GET_REG(r); if (op->opcode == ZEND_DO_FCALL_BY_NAME) { op->op1.op_type = IS_UNUSED; } else if (op->opcode == ZEND_FETCH_CONSTANT && op->op1.op_type == IS_VAR) { op->op1.u.var = VAR_VAL(assigned[r]); /* restore op1 type from VAR to CONST (the opcode handler expects this or bombs out with invalid opcode) */ op->op1.op_type = IS_CONST; } else { op->op1.u.var = VAR_VAL(assigned[r]); } } if (op->op2.op_type == IS_VAR || op->op2.op_type == IS_TMP_VAR) { int r = VAR_NUM(op->op2.u.var); GET_REG(r); op->op2.u.var = VAR_VAL(assigned[r]); } #ifdef ZEND_ENGINE_2 if (op->opcode == ZEND_DECLARE_INHERITED_CLASS) { int r = VAR_NUM(op->extended_value); GET_REG(r); op->extended_value = VAR_VAL(assigned[r]); } #endif if (op->result.op_type == IS_VAR || op->result.op_type == IS_TMP_VAR) { int r = VAR_NUM(op->result.u.var); GET_REG(r); op->result.u.var = VAR_VAL(assigned[r]); if (op->result.op_type == IS_VAR && #ifdef ZEND_ENGINE_2 op->opcode != ZEND_RECV && op->opcode != ZEND_RECV_INIT && #endif ((op->result.u.EA.type & EXT_TYPE_UNUSED) != 0)) { FREE_REG(VAR_NUM(op->result.u.var)) } else if (!(op->op1.op_type == op->result.op_type && op->op1.u.var == op->result.u.var) && !(op->op2.op_type == op->result.op_type && op->op2.u.var == op->result.u.var) && !global[r]) { switch (op->opcode) { case ZEND_RECV: case ZEND_RECV_INIT: case ZEND_ADD_ARRAY_ELEMENT: break; default: FREE_REG(VAR_NUM(op->result.u.var)); } } } } } p = p->next; } op_array->T = n; #ifndef ZEND_ENGINE_2 if (op_array->uses_globals && !uses_globals) { op_array->uses_globals = 0; } #endif free_alloca(used); free_alloca(reg_pool); free_alloca(assigned); } void restore_operand_types(zend_op_array *op_array) { zend_op* op = op_array->opcodes; int len = op_array->last; int line_num; for (line_num=0; line_num < len; op++,line_num++) { if (op->opcode == ZEND_FETCH_CONSTANT && op->op1.op_type == IS_VAR) { /* restore op1 type from VAR to CONST (the opcode handler expects this or bombs out with invalid opcode) */ op->op1.op_type = IS_CONST; } } } /* * Main Optimization Routine */ void eaccelerator_optimize(zend_op_array *op_array) { BB* p; int i; BB* bb; TSRMLS_FETCH(); /*??? #ifdef ZEND_ENGINE_2 return; #endif */ if (!EAG(compiler) || op_array->type != ZEND_USER_FUNCTION) { return; } /* Allocate memory for CFG */ if ((bb = do_alloca(sizeof(BB)*(op_array->last+1))) == NULL) return; memset(bb, 0, sizeof(BB)*(op_array->last+1)); /* Find All Basic Blocks and build CFG */ if (build_cfg(op_array, bb)) { char *global = do_alloca(op_array->T * sizeof(char)); if (global == NULL) return; for (i=0; i<2; i++) { /* Determine Used Blocks and its Predcessors */ mark_used_bb(bb); /* JMP Optimization */ optimize_jmp(bb, op_array); compute_live_var(bb, op_array, global); /* Optimize Each Basic Block */ p = bb; while (p != NULL) { optimize_bb(p, op_array, global, i TSRMLS_CC); p = p->next; } /* Mark All Basik Blocks as Unused. Free Predcessors Links. */ p = bb; while (p != NULL) { rm_bb(p); p = p->next; } } /* Mark Used Blocks */ mark_used_bb2(bb); /* Remove Unused Basic Blocks */ p = bb; while (p->next != NULL) { if (p->next->used) { p = p->next; } else { del_bb(p->next); p->next = p->next->next; } } /* Store Optimized Code */ emit_cfg(op_array, bb); reassign_registers(op_array, bb, global); /* dump_bb(bb, op_array); */ free_alloca(global); } # ifdef ZEND_ENGINE_2_1 else { /* build_cfg encountered some nested ZEND_BRK or ZEND_CONT's which it could not replace with JMP's now restore the operand type changes that build_cfg had already applied, to prevent 'invalid opcode' errors on opcode handlers that expect a strict set of operand types since php-5.1 (like ZEND_FETCH_CONSTANT) */ restore_operand_types(op_array); } # endif free_alloca(bb); } #endif #endif /* #ifdef HAVE_EACCELERATOR */