| 2 | | #include "utils.h" |
| 3 | | |
| | 7 | /* the "vector" stack */ |
| | 8 | #include "stack.h" |
| | 9 | |
| | 10 | #ifdef DEBUG |
| | 11 | # include "processor.h" |
| | 12 | #endif |
| | 13 | |
| | 14 | typedef int bbid_t; |
| | 15 | enum { |
| | 16 | BBID_INVALID = -1, |
| | 17 | }; |
| | 18 | /* {{{ basic block */ |
| | 19 | typedef struct _bb_t { |
| | 20 | bbid_t id; |
| | 21 | zend_bool used; |
| | 22 | |
| | 23 | zend_bool alloc; |
| | 24 | zend_op *opcodes; |
| | 25 | int count; |
| | 26 | int size; |
| | 27 | |
| | 28 | bbid_t jmpout_op1; |
| | 29 | bbid_t jmpout_op2; |
| | 30 | bbid_t jmpout_ext; |
| | 31 | bbid_t follow; |
| | 32 | } bb_t; |
| | 33 | /* }}} */ |
| | 34 | |
| | 35 | /* basic blocks */ |
| | 36 | typedef xc_stack_t bbs_t; |
| | 37 | |
| | 38 | /* oparray help functions */ |
| | 39 | static int op_array_convert_switch(zend_op_array *op_array) /* {{{ */ |
| | 40 | { |
| | 41 | int i; |
| | 42 | |
| | 43 | if (op_array->brk_cont_array == NULL) { |
| | 44 | return SUCCESS; |
| | 45 | } |
| | 46 | |
| | 47 | for (i = 0; i < op_array->last; i ++) { |
| | 48 | zend_op *opline = &op_array->opcodes[i]; |
| | 49 | zend_brk_cont_element *jmp_to; |
| | 50 | int array_offset, nest_levels, original_nest_levels; |
| | 51 | |
| | 52 | if (opline->opcode != ZEND_BRK && opline->opcode != ZEND_CONT) { |
| | 53 | continue; |
| | 54 | } |
| | 55 | if (opline->op2.op_type != IS_CONST |
| | 56 | || opline->op2.u.constant.type != IS_LONG) { |
| | 57 | return FAILURE; |
| | 58 | } |
| | 59 | |
| | 60 | nest_levels = opline->op2.u.constant.value.lval; |
| | 61 | original_nest_levels = nest_levels; |
| | 62 | |
| | 63 | array_offset = opline->op1.u.opline_num; |
| | 64 | do { |
| | 65 | if (array_offset == -1) { |
| | 66 | /* this is a runtime error in ZE |
| | 67 | zend_error(E_ERROR, "Cannot break/continue %d level%s", original_nest_levels, (original_nest_levels == 1) ? "" : "s"); |
| | 68 | */ |
| | 69 | return FAILURE; |
| | 70 | } |
| | 71 | jmp_to = &op_array->brk_cont_array[array_offset]; |
| | 72 | if (nest_levels > 1) { |
| | 73 | zend_op *brk_opline = &op_array->opcodes[jmp_to->brk]; |
| | 74 | |
| | 75 | switch (brk_opline->opcode) { |
| | 76 | case ZEND_SWITCH_FREE: |
| | 77 | break; |
| | 78 | case ZEND_FREE: |
| | 79 | break; |
| | 80 | } |
| | 81 | } |
| | 82 | array_offset = jmp_to->parent; |
| | 83 | } while (--nest_levels > 0); |
| | 84 | |
| | 85 | /* rewrite to jmp */ |
| | 86 | if (opline->opcode == ZEND_BRK) { |
| | 87 | opline->op1.u.opline_num = jmp_to->brk; |
| | 88 | } |
| | 89 | else { |
| | 90 | opline->op1.u.opline_num = jmp_to->cont; |
| | 91 | } |
| | 92 | opline->op2.op_type = IS_UNUSED; |
| | 93 | opline->opcode = ZEND_JMP; |
| | 94 | } |
| | 95 | |
| | 96 | if (op_array->brk_cont_array != NULL) { |
| | 97 | efree(op_array->brk_cont_array); |
| | 98 | op_array->brk_cont_array = NULL; |
| | 99 | } |
| | 100 | op_array->last_brk_cont = 0; |
| | 101 | return SUCCESS; |
| | 102 | } |
| | 103 | /* }}} */ |
| | 104 | static int op_get_jmpout(bb_t *bb, zend_op *opcodes, zend_op *opline) /* {{{ */ |
| | 105 | { |
| | 106 | /* break=have follow */ |
| | 107 | switch (opline->opcode) { |
| | 108 | case ZEND_RETURN: |
| | 109 | case ZEND_EXIT: |
| | 110 | break; |
| | 111 | |
| | 112 | case ZEND_JMP: |
| | 113 | bb->jmpout_op1 = opline->op1.u.opline_num; |
| | 114 | return SUCCESS; /* no follow */ |
| | 115 | |
| | 116 | case ZEND_JMPZNZ: |
| | 117 | bb->jmpout_ext = opline->extended_value; |
| | 118 | bb->jmpout_op2 = opline->op2.u.opline_num; |
| | 119 | break; |
| | 120 | |
| | 121 | case ZEND_JMPZ: |
| | 122 | case ZEND_JMPNZ: |
| | 123 | case ZEND_JMPZ_EX: |
| | 124 | case ZEND_JMPNZ_EX: |
| | 125 | #ifndef ZEND_ENGINE_2_1 |
| | 126 | /* Pre-PHP 5.1 only */ |
| | 127 | case ZEND_JMP_NO_CTOR: |
| | 128 | #else |
| | 129 | case ZEND_NEW: |
| | 130 | case ZEND_FE_RESET: |
| | 131 | #endif |
| | 132 | case ZEND_FE_FETCH: |
| | 133 | bb->jmpout_op2 = opline->op2.u.opline_num; |
| | 134 | break; |
| | 135 | |
| | 136 | default: |
| | 137 | return FAILURE; |
| | 138 | } |
| | 139 | |
| | 140 | return SUCCESS; |
| | 141 | } |
| | 142 | /* }}} */ |
| | 143 | |
| | 144 | /* |
| | 145 | * basic block functions |
| | 146 | */ |
| | 147 | |
| | 148 | static bb_t *bb_new_ex(zend_op *opcodes, int count) /* {{{ */ |
| | 149 | { |
| | 150 | bb_t *bb = (bb_t *) ecalloc(sizeof(bb_t), 1); |
| | 151 | |
| | 152 | bb->jmpout_op1 = BBID_INVALID; |
| | 153 | bb->jmpout_op2 = BBID_INVALID; |
| | 154 | bb->jmpout_ext = BBID_INVALID; |
| | 155 | bb->follow = BBID_INVALID; |
| | 156 | |
| | 157 | if (opcodes) { |
| | 158 | bb->alloc = 0; |
| | 159 | bb->size = bb->count = count; |
| | 160 | bb->opcodes = opcodes; |
| | 161 | } |
| | 162 | else { |
| | 163 | bb->alloc = 1; |
| | 164 | bb->size = bb->count = 8; |
| | 165 | bb->opcodes = ecalloc(sizeof(zend_op), bb->size); |
| | 166 | } |
| | 167 | |
| | 168 | return bb; |
| | 169 | } |
| | 170 | /* }}} */ |
| | 171 | #define bb_new() bb_new_ex(NULL, 0) |
| | 172 | static void bb_destroy(bb_t *bb) /* {{{ */ |
| | 173 | { |
| | 174 | if (bb->alloc) { |
| | 175 | efree(bb->opcodes); |
| | 176 | } |
| | 177 | efree(bb); |
| | 178 | } |
| | 179 | /* }}} */ |
| | 180 | #define bbs_get(bbs, n) xc_stack_get(bbs, n) |
| | 181 | static void bbs_destroy(bbs_t *bbs) /* {{{ */ |
| | 182 | { |
| | 183 | bb_t *bb; |
| | 184 | while (xc_stack_count(bbs)) { |
| | 185 | bb = (bb_t *) xc_stack_pop(bbs); |
| | 186 | bb_destroy(bb); |
| | 187 | } |
| | 188 | } |
| | 189 | /* }}} */ |
| | 190 | #define bbs_init(bbs) xc_stack_init_ex(bbs, 16) |
| | 191 | static bb_t *bbs_add_bb(bbs_t *bbs, bb_t *bb) /* {{{ */ |
| | 192 | { |
| | 193 | bb->id = (bbid_t) xc_stack_count(bbs); |
| | 194 | xc_stack_push(bbs, (void *) bb); |
| | 195 | return bb; |
| | 196 | } |
| | 197 | /* }}} */ |
| | 198 | static bb_t *bbs_new_bb_ex(bbs_t *bbs, zend_op *opcodes, int count) /* {{{ */ |
| | 199 | { |
| | 200 | return bbs_add_bb(bbs, bb_new_ex(opcodes, count)); |
| | 201 | } |
| | 202 | /* }}} */ |
| | 203 | static int bbs_build_from(bbs_t *bbs, zend_op *opcodes, int count) /* {{{ */ |
| | 204 | { |
| | 205 | int i, prev; |
| | 206 | bb_t bb, *pbb; |
| | 207 | zend_bool *markjmpins = do_alloca(count); |
| | 208 | zend_bool *markjmpouts = do_alloca(count); |
| | 209 | |
| | 210 | memset(markjmpins, 0, sizeof(zend_bool)); |
| | 211 | memset(markjmpouts, 0, sizeof(zend_bool)); |
| | 212 | |
| | 213 | for (i = 0; i < count; i ++) { |
| | 214 | /* BBID_INVALID=invalidate line |
| | 215 | * bb.jmpout_op1 bb.jmpout_op2 bb.jmpout_ext bb.follow means opline number here, not basicblock id |
| | 216 | */ |
| | 217 | bb.jmpout_op1 = bb.jmpout_op2 = BBID_INVALID; |
| | 218 | bb.jmpout_ext = bb.follow = BBID_INVALID; |
| | 219 | if (op_get_jmpout(&bb, opcodes, &opcodes[i]) == SUCCESS) { |
| | 220 | markjmpouts[i] = 1; |
| | 221 | |
| | 222 | if (bb.jmpout_op1 != BBID_INVALID) { |
| | 223 | markjmpins[bb.jmpout_op1] = 1; |
| | 224 | } |
| | 225 | if (bb.jmpout_op2 != BBID_INVALID) { |
| | 226 | markjmpins[bb.jmpout_op2] = 1; |
| | 227 | } |
| | 228 | if (bb.jmpout_ext != BBID_INVALID) { |
| | 229 | markjmpins[bb.jmpout_ext] = 1; |
| | 230 | } |
| | 231 | |
| | 232 | if (i < count && bb.follow != BBID_INVALID) { |
| | 233 | markjmpins[i + 1] = 1; |
| | 234 | } |
| | 235 | } |
| | 236 | } |
| | 237 | |
| | 238 | prev = 0; |
| | 239 | for (i = 1; i < count; i ++) { |
| | 240 | if (markjmpins[i]) { |
| | 241 | pbb = bbs_new_bb_ex(bbs, opcodes + prev, i - prev); |
| | 242 | op_get_jmpout(pbb, opcodes, &opcodes[prev]); |
| | 243 | prev = i; |
| | 244 | } |
| | 245 | } |
| | 246 | |
| | 247 | if (prev != count - 1) { |
| | 248 | pbb = bbs_new_bb_ex(bbs, opcodes + prev, count - prev); |
| | 249 | } |
| | 250 | |
| | 251 | return SUCCESS; |
| | 252 | } |
| | 253 | /* }}} */ |
| | 254 | |
| | 255 | /* |
| | 256 | * optimize |
| | 257 | */ |