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