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