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