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