1 /*
2 +----------------------------------------------------------------------+
3 | Zend OPcache |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 1998-2018 The PHP Group |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
15 | Authors: Andi Gutmans <andi@zend.com> |
16 | Zeev Suraski <zeev@zend.com> |
17 | Stanislav Malyshev <stas@zend.com> |
18 | Dmitry Stogov <dmitry@zend.com> |
19 +----------------------------------------------------------------------+
20 */
21
22 #include "php.h"
23 #include "Optimizer/zend_optimizer.h"
24 #include "Optimizer/zend_optimizer_internal.h"
25 #include "zend_API.h"
26 #include "zend_constants.h"
27 #include "zend_execute.h"
28 #include "zend_vm.h"
29 #include "zend_cfg.h"
30 #include "zend_func_info.h"
31 #include "zend_call_graph.h"
32 #include "zend_inference.h"
33 #include "zend_dump.h"
34
35 #ifndef HAVE_DFA_PASS
36 # define HAVE_DFA_PASS 1
37 #endif
38
zend_optimizer_zval_dtor_wrapper(zval * zvalue)39 static void zend_optimizer_zval_dtor_wrapper(zval *zvalue)
40 {
41 zval_dtor(zvalue);
42 }
43
zend_optimizer_collect_constant(zend_optimizer_ctx * ctx,zval * name,zval * value)44 void zend_optimizer_collect_constant(zend_optimizer_ctx *ctx, zval *name, zval* value)
45 {
46 zval val;
47
48 if (!ctx->constants) {
49 ctx->constants = zend_arena_alloc(&ctx->arena, sizeof(HashTable));
50 zend_hash_init(ctx->constants, 16, NULL, zend_optimizer_zval_dtor_wrapper, 0);
51 }
52 ZVAL_DUP(&val, value);
53 zend_hash_add(ctx->constants, Z_STR_P(name), &val);
54 }
55
zend_optimizer_get_collected_constant(HashTable * constants,zval * name,zval * value)56 int zend_optimizer_get_collected_constant(HashTable *constants, zval *name, zval* value)
57 {
58 zval *val;
59
60 if ((val = zend_hash_find(constants, Z_STR_P(name))) != NULL) {
61 ZVAL_DUP(value, val);
62 return 1;
63 }
64 return 0;
65 }
66
zend_optimizer_lookup_cv(zend_op_array * op_array,zend_string * name)67 int zend_optimizer_lookup_cv(zend_op_array *op_array, zend_string* name)
68 {
69 int i = 0;
70 zend_ulong hash_value = zend_string_hash_val(name);
71
72 while (i < op_array->last_var) {
73 if (op_array->vars[i] == name ||
74 (ZSTR_H(op_array->vars[i]) == hash_value &&
75 ZSTR_LEN(op_array->vars[i]) == ZSTR_LEN(name) &&
76 memcmp(ZSTR_VAL(op_array->vars[i]), ZSTR_VAL(name), ZSTR_LEN(name)) == 0)) {
77 return (int)(zend_intptr_t)ZEND_CALL_VAR_NUM(NULL, i);
78 }
79 i++;
80 }
81 i = op_array->last_var;
82 op_array->last_var++;
83 op_array->vars = erealloc(op_array->vars, op_array->last_var * sizeof(zend_string*));
84 op_array->vars[i] = zend_string_dup(name, 0);
85
86 /* all IS_TMP_VAR and IS_VAR variable numbers have to be adjusted */
87 {
88 zend_op *opline = op_array->opcodes;
89 zend_op *end = opline + op_array->last;
90 while (opline < end) {
91 if (opline->op1_type & (IS_TMP_VAR|IS_VAR)) {
92 opline->op1.var += sizeof(zval);
93 }
94 if (opline->op2_type & (IS_TMP_VAR|IS_VAR)) {
95 opline->op2.var += sizeof(zval);
96 }
97 if (opline->result_type & (IS_TMP_VAR|IS_VAR)) {
98 opline->result.var += sizeof(zval);
99 }
100 opline++;
101 }
102 }
103
104 return (int)(zend_intptr_t)ZEND_CALL_VAR_NUM(NULL, i);
105 }
106
zend_optimizer_add_literal(zend_op_array * op_array,zval * zv)107 int zend_optimizer_add_literal(zend_op_array *op_array, zval *zv)
108 {
109 int i = op_array->last_literal;
110 op_array->last_literal++;
111 op_array->literals = (zval*)erealloc(op_array->literals, op_array->last_literal * sizeof(zval));
112 ZVAL_COPY_VALUE(&op_array->literals[i], zv);
113 Z_CACHE_SLOT(op_array->literals[i]) = -1;
114 return i;
115 }
116
zend_optimizer_add_literal_string(zend_op_array * op_array,zend_string * str)117 static inline int zend_optimizer_add_literal_string(zend_op_array *op_array, zend_string *str) {
118 zval zv;
119 ZVAL_STR(&zv, str);
120 zend_string_hash_val(str);
121 return zend_optimizer_add_literal(op_array, &zv);
122 }
123
zend_optimizer_is_disabled_func(const char * name,size_t len)124 int zend_optimizer_is_disabled_func(const char *name, size_t len) {
125 zend_function *fbc = (zend_function *)zend_hash_str_find_ptr(EG(function_table), name, len);
126
127 return (fbc && fbc->type == ZEND_INTERNAL_FUNCTION &&
128 fbc->internal_function.handler == ZEND_FN(display_disabled_function));
129 }
130
drop_leading_backslash(zval * val)131 static inline void drop_leading_backslash(zval *val) {
132 if (Z_STRVAL_P(val)[0] == '\\') {
133 zend_string *str = zend_string_init(Z_STRVAL_P(val) + 1, Z_STRLEN_P(val) - 1, 0);
134 zval_dtor(val);
135 ZVAL_STR(val, str);
136 }
137 }
138
alloc_cache_slots_op1(zend_op_array * op_array,zend_op * opline,uint32_t num)139 static inline void alloc_cache_slots_op1(zend_op_array *op_array, zend_op *opline, uint32_t num) {
140 Z_CACHE_SLOT(op_array->literals[opline->op1.constant]) = op_array->cache_size;
141 op_array->cache_size += num * sizeof(void *);
142 }
alloc_cache_slots_op2(zend_op_array * op_array,zend_op * opline,uint32_t num)143 static inline void alloc_cache_slots_op2(zend_op_array *op_array, zend_op *opline, uint32_t num) {
144 Z_CACHE_SLOT(op_array->literals[opline->op2.constant]) = op_array->cache_size;
145 op_array->cache_size += num * sizeof(void *);
146 }
147
148 #define REQUIRES_STRING(val) do { \
149 if (Z_TYPE_P(val) != IS_STRING) { \
150 zval_dtor(val); \
151 return 0; \
152 } \
153 } while (0)
154
155 #define TO_STRING_NOWARN(val) do { \
156 if (Z_TYPE_P(val) >= IS_ARRAY) { \
157 zval_dtor(val); \
158 return 0; \
159 } \
160 convert_to_string(val); \
161 } while (0)
162
zend_optimizer_update_op1_const(zend_op_array * op_array,zend_op * opline,zval * val)163 int zend_optimizer_update_op1_const(zend_op_array *op_array,
164 zend_op *opline,
165 zval *val)
166 {
167 switch (opline->opcode) {
168 case ZEND_FREE:
169 MAKE_NOP(opline);
170 zval_dtor(val);
171 return 1;
172 case ZEND_INIT_STATIC_METHOD_CALL:
173 case ZEND_CATCH:
174 case ZEND_FETCH_CONSTANT:
175 case ZEND_FETCH_CLASS_CONSTANT:
176 case ZEND_DEFINED:
177 case ZEND_NEW:
178 REQUIRES_STRING(val);
179 drop_leading_backslash(val);
180 opline->op1.constant = zend_optimizer_add_literal(op_array, val);
181 alloc_cache_slots_op1(op_array, opline, 1);
182 zend_optimizer_add_literal_string(op_array, zend_string_tolower(Z_STR_P(val)));
183 break;
184 case ZEND_FETCH_STATIC_PROP_R:
185 case ZEND_FETCH_STATIC_PROP_W:
186 case ZEND_FETCH_STATIC_PROP_RW:
187 case ZEND_FETCH_STATIC_PROP_IS:
188 case ZEND_FETCH_STATIC_PROP_UNSET:
189 case ZEND_FETCH_STATIC_PROP_FUNC_ARG:
190 TO_STRING_NOWARN(val);
191 opline->op1.constant = zend_optimizer_add_literal(op_array, val);
192 alloc_cache_slots_op1(op_array, opline, 2);
193 break;
194 case ZEND_SEND_VAR:
195 opline->opcode = ZEND_SEND_VAL;
196 opline->op1.constant = zend_optimizer_add_literal(op_array, val);
197 break;
198 case ZEND_SEPARATE:
199 case ZEND_SEND_VAR_NO_REF:
200 case ZEND_SEND_VAR_NO_REF_EX:
201 zval_ptr_dtor(val);
202 return 0;
203 case ZEND_VERIFY_RETURN_TYPE:
204 /* This would require a non-local change.
205 * zend_optimizer_replace_by_const() supports this. */
206 zval_ptr_dtor(val);
207 return 0;
208 case ZEND_CONCAT:
209 case ZEND_FAST_CONCAT:
210 case ZEND_FETCH_R:
211 case ZEND_FETCH_W:
212 case ZEND_FETCH_RW:
213 case ZEND_FETCH_IS:
214 case ZEND_FETCH_UNSET:
215 case ZEND_FETCH_FUNC_ARG:
216 TO_STRING_NOWARN(val);
217 /* break missing intentionally */
218 default:
219 opline->op1.constant = zend_optimizer_add_literal(op_array, val);
220 break;
221 }
222
223 ZEND_OP1_TYPE(opline) = IS_CONST;
224 if (Z_TYPE(ZEND_OP1_LITERAL(opline)) == IS_STRING) {
225 zend_string_hash_val(Z_STR(ZEND_OP1_LITERAL(opline)));
226 }
227 return 1;
228 }
229
zend_optimizer_update_op2_const(zend_op_array * op_array,zend_op * opline,zval * val)230 int zend_optimizer_update_op2_const(zend_op_array *op_array,
231 zend_op *opline,
232 zval *val)
233 {
234 switch (opline->opcode) {
235 case ZEND_ASSIGN_REF:
236 case ZEND_FAST_CALL:
237 zval_dtor(val);
238 return 0;
239 case ZEND_FETCH_CLASS:
240 case ZEND_INIT_FCALL_BY_NAME:
241 /*case ZEND_INIT_NS_FCALL_BY_NAME:*/
242 case ZEND_ADD_INTERFACE:
243 case ZEND_ADD_TRAIT:
244 case ZEND_INSTANCEOF:
245 case ZEND_FETCH_STATIC_PROP_R:
246 case ZEND_FETCH_STATIC_PROP_W:
247 case ZEND_FETCH_STATIC_PROP_RW:
248 case ZEND_FETCH_STATIC_PROP_IS:
249 case ZEND_FETCH_STATIC_PROP_UNSET:
250 case ZEND_FETCH_STATIC_PROP_FUNC_ARG:
251 case ZEND_UNSET_STATIC_PROP:
252 case ZEND_ISSET_ISEMPTY_STATIC_PROP:
253 REQUIRES_STRING(val);
254 drop_leading_backslash(val);
255 opline->op2.constant = zend_optimizer_add_literal(op_array, val);
256 zend_optimizer_add_literal_string(op_array, zend_string_tolower(Z_STR_P(val)));
257 alloc_cache_slots_op2(op_array, opline, 1);
258 break;
259 case ZEND_INIT_FCALL:
260 REQUIRES_STRING(val);
261 zend_str_tolower(Z_STRVAL_P(val), Z_STRLEN_P(val));
262 opline->op2.constant = zend_optimizer_add_literal(op_array, val);
263 alloc_cache_slots_op2(op_array, opline, 1);
264 break;
265 case ZEND_INIT_DYNAMIC_CALL:
266 if (Z_TYPE_P(val) == IS_STRING) {
267 if (zend_memrchr(Z_STRVAL_P(val), ':', Z_STRLEN_P(val))) {
268 zval_dtor(val);
269 return 0;
270 }
271
272 if (zend_optimizer_classify_function(Z_STR_P(val), opline->extended_value)) {
273 /* Dynamic call to various special functions must stay dynamic,
274 * otherwise would drop a warning */
275 zval_dtor(val);
276 return 0;
277 }
278
279 opline->opcode = ZEND_INIT_FCALL_BY_NAME;
280 drop_leading_backslash(val);
281 opline->op2.constant = zend_optimizer_add_literal(op_array, val);
282 zend_optimizer_add_literal_string(op_array, zend_string_tolower(Z_STR_P(val)));
283 alloc_cache_slots_op2(op_array, opline, 1);
284 } else {
285 opline->op2.constant = zend_optimizer_add_literal(op_array, val);
286 }
287 break;
288 case ZEND_INIT_METHOD_CALL:
289 case ZEND_INIT_STATIC_METHOD_CALL:
290 REQUIRES_STRING(val);
291 opline->op2.constant = zend_optimizer_add_literal(op_array, val);
292 zend_optimizer_add_literal_string(op_array, zend_string_tolower(Z_STR_P(val)));
293 alloc_cache_slots_op2(op_array, opline, 2);
294 break;
295 /*case ZEND_FETCH_CLASS_CONSTANT:*/
296 case ZEND_ASSIGN_OBJ:
297 case ZEND_FETCH_OBJ_R:
298 case ZEND_FETCH_OBJ_W:
299 case ZEND_FETCH_OBJ_RW:
300 case ZEND_FETCH_OBJ_IS:
301 case ZEND_FETCH_OBJ_UNSET:
302 case ZEND_FETCH_OBJ_FUNC_ARG:
303 case ZEND_UNSET_OBJ:
304 case ZEND_PRE_INC_OBJ:
305 case ZEND_PRE_DEC_OBJ:
306 case ZEND_POST_INC_OBJ:
307 case ZEND_POST_DEC_OBJ:
308 case ZEND_ISSET_ISEMPTY_PROP_OBJ:
309 TO_STRING_NOWARN(val);
310 opline->op2.constant = zend_optimizer_add_literal(op_array, val);
311 alloc_cache_slots_op2(op_array, opline, 2);
312 break;
313 case ZEND_ASSIGN_ADD:
314 case ZEND_ASSIGN_SUB:
315 case ZEND_ASSIGN_MUL:
316 case ZEND_ASSIGN_DIV:
317 case ZEND_ASSIGN_POW:
318 case ZEND_ASSIGN_MOD:
319 case ZEND_ASSIGN_SL:
320 case ZEND_ASSIGN_SR:
321 case ZEND_ASSIGN_CONCAT:
322 case ZEND_ASSIGN_BW_OR:
323 case ZEND_ASSIGN_BW_AND:
324 case ZEND_ASSIGN_BW_XOR:
325 if (opline->extended_value == ZEND_ASSIGN_OBJ) {
326 TO_STRING_NOWARN(val);
327 opline->op2.constant = zend_optimizer_add_literal(op_array, val);
328 alloc_cache_slots_op2(op_array, opline, 2);
329 } else {
330 opline->op2.constant = zend_optimizer_add_literal(op_array, val);
331 }
332 break;
333 case ZEND_ISSET_ISEMPTY_DIM_OBJ:
334 case ZEND_ADD_ARRAY_ELEMENT:
335 case ZEND_INIT_ARRAY:
336 case ZEND_ASSIGN_DIM:
337 case ZEND_UNSET_DIM:
338 case ZEND_FETCH_DIM_R:
339 case ZEND_FETCH_DIM_W:
340 case ZEND_FETCH_DIM_RW:
341 case ZEND_FETCH_DIM_IS:
342 case ZEND_FETCH_DIM_FUNC_ARG:
343 case ZEND_FETCH_DIM_UNSET:
344 case ZEND_FETCH_LIST:
345 if (Z_TYPE_P(val) == IS_STRING) {
346 zend_ulong index;
347 if (ZEND_HANDLE_NUMERIC(Z_STR_P(val), index)) {
348 zval_dtor(val);
349 ZVAL_LONG(val, index);
350 }
351 }
352 opline->op2.constant = zend_optimizer_add_literal(op_array, val);
353 break;
354 case ZEND_ROPE_INIT:
355 case ZEND_ROPE_ADD:
356 case ZEND_ROPE_END:
357 case ZEND_CONCAT:
358 case ZEND_FAST_CONCAT:
359 TO_STRING_NOWARN(val);
360 /* break missing intentionally */
361 default:
362 opline->op2.constant = zend_optimizer_add_literal(op_array, val);
363 break;
364 }
365
366 ZEND_OP2_TYPE(opline) = IS_CONST;
367 if (Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_STRING) {
368 zend_string_hash_val(Z_STR(ZEND_OP2_LITERAL(opline)));
369 }
370 return 1;
371 }
372
zend_optimizer_remove_live_range(zend_op_array * op_array,uint32_t var)373 void zend_optimizer_remove_live_range(zend_op_array *op_array, uint32_t var)
374 {
375 if (op_array->last_live_range) {
376 int i = 0;
377 int j = 0;
378 uint32_t *map;
379 ALLOCA_FLAG(use_heap);
380
381 map = (uint32_t *)do_alloca(sizeof(uint32_t) * op_array->last_live_range, use_heap);
382
383 do {
384 if ((op_array->live_range[i].var & ~ZEND_LIVE_MASK) != var) {
385 map[i] = j;
386 if (i != j) {
387 op_array->live_range[j] = op_array->live_range[i];
388 }
389 j++;
390 }
391 i++;
392 } while (i < op_array->last_live_range);
393 if (i != j) {
394 if ((op_array->last_live_range = j)) {
395 zend_op *opline = op_array->opcodes;
396 zend_op *end = opline + op_array->last;
397
398 while (opline != end) {
399 if ((opline->opcode == ZEND_FREE || opline->opcode == ZEND_FE_FREE) &&
400 opline->extended_value == ZEND_FREE_ON_RETURN) {
401 opline->op2.num = map[opline->op2.num];
402 }
403 opline++;
404 }
405 } else {
406 efree(op_array->live_range);
407 op_array->live_range = NULL;
408 }
409 }
410 free_alloca(map, use_heap);
411 }
412 }
413
zend_optimizer_replace_by_const(zend_op_array * op_array,zend_op * opline,zend_uchar type,uint32_t var,zval * val)414 int zend_optimizer_replace_by_const(zend_op_array *op_array,
415 zend_op *opline,
416 zend_uchar type,
417 uint32_t var,
418 zval *val)
419 {
420 zend_op *end = op_array->opcodes + op_array->last;
421
422 while (opline < end) {
423 if (ZEND_OP1_TYPE(opline) == type &&
424 ZEND_OP1(opline).var == var) {
425 switch (opline->opcode) {
426 case ZEND_FETCH_DIM_W:
427 case ZEND_FETCH_DIM_RW:
428 case ZEND_FETCH_DIM_FUNC_ARG:
429 case ZEND_FETCH_DIM_UNSET:
430 case ZEND_ASSIGN_DIM:
431 case ZEND_SEPARATE:
432 case ZEND_RETURN_BY_REF:
433 zval_dtor(val);
434 return 0;
435 case ZEND_SEND_VAR:
436 opline->extended_value = 0;
437 opline->opcode = ZEND_SEND_VAL;
438 break;
439 case ZEND_SEND_VAR_EX:
440 opline->extended_value = 0;
441 opline->opcode = ZEND_SEND_VAL_EX;
442 break;
443 case ZEND_SEND_VAR_NO_REF:
444 zval_dtor(val);
445 return 0;
446 case ZEND_SEND_VAR_NO_REF_EX:
447 opline->opcode = ZEND_SEND_VAL;
448 break;
449 case ZEND_SEND_USER:
450 opline->opcode = ZEND_SEND_VAL_EX;
451 break;
452 /* In most cases IS_TMP_VAR operand may be used only once.
453 * The operands are usually destroyed by the opcode handler.
454 * ZEND_CASE and ZEND_FETCH_LIST are exceptions, they keeps operand
455 * unchanged, and allows its reuse. these instructions
456 * usually terminated by ZEND_FREE that finally kills the value.
457 */
458 case ZEND_FETCH_LIST: {
459 zend_op *m = opline;
460 do {
461 if (m->opcode == ZEND_FETCH_LIST &&
462 ZEND_OP1_TYPE(m) == type &&
463 ZEND_OP1(m).var == var) {
464 zend_optimizer_update_op1_const(op_array, m, val);
465 }
466 m++;
467 } while (m->opcode != ZEND_FREE || ZEND_OP1_TYPE(m) != type || ZEND_OP1(m).var != var);
468 ZEND_ASSERT(m->opcode == ZEND_FREE && ZEND_OP1_TYPE(m) == type && ZEND_OP1(m).var == var);
469 MAKE_NOP(m);
470 zend_optimizer_remove_live_range(op_array, var);
471 return 1;
472 }
473 case ZEND_CASE:
474 case ZEND_FREE: {
475 zend_op *m, *n;
476 int brk = op_array->last_live_range;
477 zend_bool in_switch = 0;
478 while (brk--) {
479 if (op_array->live_range[brk].start <= (uint32_t)(opline - op_array->opcodes) &&
480 op_array->live_range[brk].end > (uint32_t)(opline - op_array->opcodes)) {
481 in_switch = 1;
482 break;
483 }
484 }
485
486 if (!in_switch) {
487 ZEND_ASSERT(opline->opcode == ZEND_FREE);
488 MAKE_NOP(opline);
489 zval_dtor(val);
490 return 1;
491 }
492
493 m = opline;
494 n = op_array->opcodes + op_array->live_range[brk].end;
495 if (n->opcode == ZEND_FREE &&
496 !(n->extended_value & ZEND_FREE_ON_RETURN)) {
497 n++;
498 } else {
499 n = op_array->opcodes + op_array->last;
500 }
501 while (m < n) {
502 if (ZEND_OP1_TYPE(m) == type &&
503 ZEND_OP1(m).var == var) {
504 if (m->opcode == ZEND_CASE) {
505 zval old_val;
506 ZVAL_COPY_VALUE(&old_val, val);
507 zval_copy_ctor(val);
508 zend_optimizer_update_op1_const(op_array, m, val);
509 ZVAL_COPY_VALUE(val, &old_val);
510 } else if (m->opcode == ZEND_FREE) {
511 MAKE_NOP(m);
512 } else {
513 ZEND_ASSERT(0);
514 }
515 }
516 m++;
517 }
518 zval_dtor(val);
519 zend_optimizer_remove_live_range(op_array, var);
520 return 1;
521 }
522 case ZEND_VERIFY_RETURN_TYPE: {
523 zend_arg_info *ret_info = op_array->arg_info - 1;
524 if (ret_info->class_name
525 || ret_info->type_hint == IS_CALLABLE
526 || !ZEND_SAME_FAKE_TYPE(ret_info->type_hint, Z_TYPE_P(val))
527 || (op_array->fn_flags & ZEND_ACC_RETURN_REFERENCE)) {
528 zval_dtor(val);
529 return 0;
530 }
531 MAKE_NOP(opline);
532
533 /* zend_handle_loops_and_finally may inserts other oplines */
534 do {
535 ++opline;
536 } while (opline->opcode != ZEND_RETURN && opline->opcode != ZEND_RETURN_BY_REF);
537 ZEND_ASSERT(ZEND_OP1(opline).var == var);
538
539 break;
540 }
541 default:
542 break;
543 }
544 if (zend_optimizer_update_op1_const(op_array, opline, val)) {
545 zend_optimizer_remove_live_range(op_array, var);
546 return 1;
547 }
548 return 0;
549 }
550
551 if (ZEND_OP2_TYPE(opline) == type &&
552 ZEND_OP2(opline).var == var) {
553 if (zend_optimizer_update_op2_const(op_array, opline, val)) {
554 zend_optimizer_remove_live_range(op_array, var);
555 return 1;
556 }
557 return 0;
558 }
559 opline++;
560 }
561
562 return 1;
563 }
564
get_class_entry_from_op1(zend_script * script,zend_op_array * op_array,zend_op * opline,zend_bool rt_constants)565 static zend_class_entry *get_class_entry_from_op1(
566 zend_script *script, zend_op_array *op_array, zend_op *opline, zend_bool rt_constants) {
567 if (opline->op1_type == IS_CONST) {
568 zval *op1 = CRT_CONSTANT_EX(op_array, opline->op1, rt_constants);
569 if (Z_TYPE_P(op1) == IS_STRING) {
570 zend_string *class_name = Z_STR_P(op1 + 1);
571 zend_class_entry *ce;
572 if (script && (ce = zend_hash_find_ptr(&script->class_table, class_name))) {
573 return ce;
574 } else if ((ce = zend_hash_find_ptr(EG(class_table), class_name))) {
575 if (ce->type == ZEND_INTERNAL_CLASS) {
576 return ce;
577 } else if (ce->type == ZEND_USER_CLASS &&
578 ce->info.user.filename &&
579 ce->info.user.filename == op_array->filename) {
580 return ce;
581 }
582 }
583 }
584 } else if (opline->op1_type == IS_UNUSED && op_array->scope
585 && !(op_array->scope->ce_flags & ZEND_ACC_TRAIT)
586 && (opline->op1.num & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_SELF) {
587 return op_array->scope;
588 }
589 return NULL;
590 }
591
zend_optimizer_get_called_func(zend_script * script,zend_op_array * op_array,zend_op * opline,zend_bool rt_constants)592 zend_function *zend_optimizer_get_called_func(
593 zend_script *script, zend_op_array *op_array, zend_op *opline, zend_bool rt_constants)
594 {
595 #define GET_OP(op) CRT_CONSTANT_EX(op_array, opline->op, rt_constants)
596 switch (opline->opcode) {
597 case ZEND_INIT_FCALL:
598 {
599 zend_string *function_name = Z_STR_P(GET_OP(op2));
600 zend_function *func;
601 if (script && (func = zend_hash_find_ptr(&script->function_table, function_name)) != NULL) {
602 return func;
603 } else if ((func = zend_hash_find_ptr(EG(function_table), function_name)) != NULL) {
604 if (func->type == ZEND_INTERNAL_FUNCTION) {
605 return func;
606 } else if (func->type == ZEND_USER_FUNCTION &&
607 func->op_array.filename &&
608 func->op_array.filename == op_array->filename) {
609 return func;
610 }
611 }
612 break;
613 }
614 case ZEND_INIT_FCALL_BY_NAME:
615 case ZEND_INIT_NS_FCALL_BY_NAME:
616 if (opline->op2_type == IS_CONST && Z_TYPE_P(GET_OP(op2)) == IS_STRING) {
617 zval *function_name = GET_OP(op2) + 1;
618 zend_function *func;
619 if (script && (func = zend_hash_find_ptr(&script->function_table, Z_STR_P(function_name)))) {
620 return func;
621 } else if ((func = zend_hash_find_ptr(EG(function_table), Z_STR_P(function_name))) != NULL) {
622 if (func->type == ZEND_INTERNAL_FUNCTION) {
623 return func;
624 } else if (func->type == ZEND_USER_FUNCTION &&
625 func->op_array.filename &&
626 func->op_array.filename == op_array->filename) {
627 return func;
628 }
629 }
630 }
631 break;
632 case ZEND_INIT_STATIC_METHOD_CALL:
633 if (opline->op2_type == IS_CONST && Z_TYPE_P(GET_OP(op2)) == IS_STRING) {
634 zend_class_entry *ce = get_class_entry_from_op1(
635 script, op_array, opline, rt_constants);
636 if (ce) {
637 zend_string *func_name = Z_STR_P(GET_OP(op2) + 1);
638 return zend_hash_find_ptr(&ce->function_table, func_name);
639 }
640 }
641 break;
642 case ZEND_INIT_METHOD_CALL:
643 if (opline->op1_type == IS_UNUSED
644 && opline->op2_type == IS_CONST && Z_TYPE_P(GET_OP(op2)) == IS_STRING
645 && op_array->scope && !(op_array->scope->ce_flags & ZEND_ACC_TRAIT)) {
646 zend_string *method_name = Z_STR_P(GET_OP(op2) + 1);
647 zend_function *fbc = zend_hash_find_ptr(
648 &op_array->scope->function_table, method_name);
649 if (fbc) {
650 zend_bool is_private = (fbc->common.fn_flags & ZEND_ACC_PRIVATE) != 0;
651 zend_bool is_final = (fbc->common.fn_flags & ZEND_ACC_FINAL) != 0;
652 zend_bool same_scope = fbc->common.scope == op_array->scope;
653 if ((is_private && same_scope)
654 || (is_final && (!is_private || same_scope))) {
655 return fbc;
656 }
657 }
658 }
659 break;
660 case ZEND_NEW:
661 {
662 zend_class_entry *ce = get_class_entry_from_op1(
663 script, op_array, opline, rt_constants);
664 if (ce && ce->type == ZEND_USER_CLASS) {
665 return ce->constructor;
666 }
667 break;
668 }
669 }
670 return NULL;
671 #undef GET_OP
672 }
673
zend_optimizer_classify_function(zend_string * name,uint32_t num_args)674 uint32_t zend_optimizer_classify_function(zend_string *name, uint32_t num_args) {
675 if (zend_string_equals_literal(name, "extract")) {
676 return ZEND_FUNC_INDIRECT_VAR_ACCESS;
677 } else if (zend_string_equals_literal(name, "compact")) {
678 return ZEND_FUNC_INDIRECT_VAR_ACCESS;
679 } else if (zend_string_equals_literal(name, "parse_str") && num_args <= 1) {
680 return ZEND_FUNC_INDIRECT_VAR_ACCESS;
681 } else if (zend_string_equals_literal(name, "mb_parse_str") && num_args <= 1) {
682 return ZEND_FUNC_INDIRECT_VAR_ACCESS;
683 } else if (zend_string_equals_literal(name, "get_defined_vars")) {
684 return ZEND_FUNC_INDIRECT_VAR_ACCESS;
685 } else if (zend_string_equals_literal(name, "assert")) {
686 return ZEND_FUNC_INDIRECT_VAR_ACCESS;
687 } else if (zend_string_equals_literal(name, "func_num_args")) {
688 return ZEND_FUNC_VARARG;
689 } else if (zend_string_equals_literal(name, "func_get_arg")) {
690 return ZEND_FUNC_VARARG;
691 } else if (zend_string_equals_literal(name, "func_get_args")) {
692 return ZEND_FUNC_VARARG;
693 } else {
694 return 0;
695 }
696 }
697
zend_optimize(zend_op_array * op_array,zend_optimizer_ctx * ctx)698 static void zend_optimize(zend_op_array *op_array,
699 zend_optimizer_ctx *ctx)
700 {
701 if (op_array->type == ZEND_EVAL_CODE) {
702 return;
703 }
704
705 if (ctx->debug_level & ZEND_DUMP_BEFORE_OPTIMIZER) {
706 zend_dump_op_array(op_array, 0, "before optimizer", NULL);
707 }
708
709 /* pass 1
710 * - substitute persistent constants (true, false, null, etc)
711 * - perform compile-time evaluation of constant binary and unary operations
712 * - optimize series of ADD_STRING and/or ADD_CHAR
713 * - convert CAST(IS_BOOL,x) into BOOL(x)
714 * - pre-evaluate constant function calls
715 */
716 if (ZEND_OPTIMIZER_PASS_1 & ctx->optimization_level) {
717 zend_optimizer_pass1(op_array, ctx);
718 if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_1) {
719 zend_dump_op_array(op_array, 0, "after pass 1", NULL);
720 }
721 }
722
723 /* pass 2:
724 * - convert non-numeric constants to numeric constants in numeric operators
725 * - optimize constant conditional JMPs
726 */
727 if (ZEND_OPTIMIZER_PASS_2 & ctx->optimization_level) {
728 zend_optimizer_pass2(op_array);
729 if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_2) {
730 zend_dump_op_array(op_array, 0, "after pass 2", NULL);
731 }
732 }
733
734 /* pass 3:
735 * - optimize $i = $i+expr to $i+=expr
736 * - optimize series of JMPs
737 * - change $i++ to ++$i where possible
738 */
739 if (ZEND_OPTIMIZER_PASS_3 & ctx->optimization_level) {
740 zend_optimizer_pass3(op_array);
741 if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_3) {
742 zend_dump_op_array(op_array, 0, "after pass 3", NULL);
743 }
744 }
745
746 /* pass 4:
747 * - INIT_FCALL_BY_NAME -> DO_FCALL
748 */
749 if (ZEND_OPTIMIZER_PASS_4 & ctx->optimization_level) {
750 zend_optimize_func_calls(op_array, ctx);
751 if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_4) {
752 zend_dump_op_array(op_array, 0, "after pass 4", NULL);
753 }
754 }
755
756 /* pass 5:
757 * - CFG optimization
758 */
759 if (ZEND_OPTIMIZER_PASS_5 & ctx->optimization_level) {
760 zend_optimize_cfg(op_array, ctx);
761 if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_5) {
762 zend_dump_op_array(op_array, 0, "after pass 5", NULL);
763 }
764 }
765
766 #if HAVE_DFA_PASS
767 /* pass 6:
768 * - DFA optimization
769 */
770 if ((ZEND_OPTIMIZER_PASS_6 & ctx->optimization_level) &&
771 !(ZEND_OPTIMIZER_PASS_7 & ctx->optimization_level)) {
772 zend_optimize_dfa(op_array, ctx);
773 if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_6) {
774 zend_dump_op_array(op_array, 0, "after pass 6", NULL);
775 }
776 }
777 #endif
778
779 /* pass 9:
780 * - Optimize temp variables usage
781 */
782 if (ZEND_OPTIMIZER_PASS_9 & ctx->optimization_level) {
783 zend_optimize_temporary_variables(op_array, ctx);
784 if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_9) {
785 zend_dump_op_array(op_array, 0, "after pass 9", NULL);
786 }
787 }
788
789 /* pass 10:
790 * - remove NOPs
791 */
792 if (((ZEND_OPTIMIZER_PASS_10|ZEND_OPTIMIZER_PASS_5) & ctx->optimization_level) == ZEND_OPTIMIZER_PASS_10) {
793 zend_optimizer_nop_removal(op_array);
794 if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_10) {
795 zend_dump_op_array(op_array, 0, "after pass 10", NULL);
796 }
797 }
798
799 /* pass 11:
800 * - Compact literals table
801 */
802 if (ZEND_OPTIMIZER_PASS_11 & ctx->optimization_level) {
803 zend_optimizer_compact_literals(op_array, ctx);
804 if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_11) {
805 zend_dump_op_array(op_array, 0, "after pass 11", NULL);
806 }
807 }
808
809 if (ctx->debug_level & ZEND_DUMP_AFTER_OPTIMIZER) {
810 zend_dump_op_array(op_array, 0, "after optimizer", NULL);
811 }
812 }
813
zend_revert_pass_two(zend_op_array * op_array)814 static void zend_revert_pass_two(zend_op_array *op_array)
815 {
816 zend_op *opline, *end;
817
818 opline = op_array->opcodes;
819 end = opline + op_array->last;
820 while (opline < end) {
821 if (opline->op1_type == IS_CONST) {
822 ZEND_PASS_TWO_UNDO_CONSTANT(op_array, opline->op1);
823 }
824 if (opline->op2_type == IS_CONST) {
825 ZEND_PASS_TWO_UNDO_CONSTANT(op_array, opline->op2);
826 }
827 opline++;
828 }
829 }
830
zend_redo_pass_two(zend_op_array * op_array)831 static void zend_redo_pass_two(zend_op_array *op_array)
832 {
833 zend_op *opline, *end;
834
835 opline = op_array->opcodes;
836 end = opline + op_array->last;
837 while (opline < end) {
838 if (opline->op1_type == IS_CONST) {
839 ZEND_PASS_TWO_UPDATE_CONSTANT(op_array, opline->op1);
840 }
841 if (opline->op2_type == IS_CONST) {
842 ZEND_PASS_TWO_UPDATE_CONSTANT(op_array, opline->op2);
843 }
844 ZEND_VM_SET_OPCODE_HANDLER(opline);
845 opline++;
846 }
847 }
848
849 #if HAVE_DFA_PASS
zend_redo_pass_two_ex(zend_op_array * op_array,zend_ssa * ssa)850 static void zend_redo_pass_two_ex(zend_op_array *op_array, zend_ssa *ssa)
851 {
852 zend_op *opline, *end;
853
854 opline = op_array->opcodes;
855 end = opline + op_array->last;
856 while (opline < end) {
857 zend_vm_set_opcode_handler_ex(opline,
858 opline->op1_type == IS_UNUSED ? 0 : (OP1_INFO() & (MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_KEY_ANY)),
859 opline->op2_type == IS_UNUSED ? 0 : (OP2_INFO() & (MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_KEY_ANY)),
860 (opline->opcode == ZEND_PRE_INC ||
861 opline->opcode == ZEND_PRE_DEC ||
862 opline->opcode == ZEND_POST_INC ||
863 opline->opcode == ZEND_POST_DEC) ?
864 ((ssa->ops[opline - op_array->opcodes].op1_def >= 0) ? (OP1_DEF_INFO() & (MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_KEY_ANY)) : MAY_BE_ANY) :
865 (opline->result_type == IS_UNUSED ? 0 : (RES_INFO() & (MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_KEY_ANY))));
866 if (opline->op1_type == IS_CONST) {
867 ZEND_PASS_TWO_UPDATE_CONSTANT(op_array, opline->op1);
868 }
869 if (opline->op2_type == IS_CONST) {
870 ZEND_PASS_TWO_UPDATE_CONSTANT(op_array, opline->op2);
871 }
872 opline++;
873 }
874 }
875 #endif
876
zend_optimize_op_array(zend_op_array * op_array,zend_optimizer_ctx * ctx)877 static void zend_optimize_op_array(zend_op_array *op_array,
878 zend_optimizer_ctx *ctx)
879 {
880 /* Revert pass_two() */
881 zend_revert_pass_two(op_array);
882
883 /* Do actual optimizations */
884 zend_optimize(op_array, ctx);
885
886 /* Redo pass_two() */
887 zend_redo_pass_two(op_array);
888 }
889
zend_adjust_fcall_stack_size(zend_op_array * op_array,zend_optimizer_ctx * ctx)890 static void zend_adjust_fcall_stack_size(zend_op_array *op_array, zend_optimizer_ctx *ctx)
891 {
892 zend_function *func;
893 zend_op *opline, *end;
894
895 opline = op_array->opcodes;
896 end = opline + op_array->last;
897 while (opline < end) {
898 if (opline->opcode == ZEND_INIT_FCALL) {
899 func = zend_hash_find_ptr(
900 &ctx->script->function_table,
901 Z_STR_P(RT_CONSTANT(op_array, opline->op2)));
902 if (func) {
903 opline->op1.num = zend_vm_calc_used_stack(opline->extended_value, func);
904 }
905 }
906 opline++;
907 }
908 }
909
910 #if HAVE_DFA_PASS
zend_adjust_fcall_stack_size_graph(zend_op_array * op_array)911 static void zend_adjust_fcall_stack_size_graph(zend_op_array *op_array)
912 {
913 zend_func_info *func_info = ZEND_FUNC_INFO(op_array);
914
915 if (func_info) {
916 zend_call_info *call_info =func_info->callee_info;
917
918 while (call_info) {
919 zend_op *opline = call_info->caller_init_opline;
920
921 if (opline && call_info->callee_func && opline->opcode == ZEND_INIT_FCALL) {
922 opline->op1.num = zend_vm_calc_used_stack(opline->extended_value, call_info->callee_func);
923 }
924 call_info = call_info->next_callee;
925 }
926 }
927 }
928 #endif
929
zend_optimize_script(zend_script * script,zend_long optimization_level,zend_long debug_level)930 int zend_optimize_script(zend_script *script, zend_long optimization_level, zend_long debug_level)
931 {
932 zend_class_entry *ce;
933 zend_op_array *op_array;
934 zend_string *name;
935 zend_optimizer_ctx ctx;
936 #if HAVE_DFA_PASS
937 zend_call_graph call_graph;
938 #endif
939
940 ctx.arena = zend_arena_create(64 * 1024);
941 ctx.script = script;
942 ctx.constants = NULL;
943 ctx.optimization_level = optimization_level;
944 ctx.debug_level = debug_level;
945
946 zend_optimize_op_array(&script->main_op_array, &ctx);
947
948 ZEND_HASH_FOREACH_PTR(&script->function_table, op_array) {
949 zend_optimize_op_array(op_array, &ctx);
950 } ZEND_HASH_FOREACH_END();
951
952 ZEND_HASH_FOREACH_PTR(&script->class_table, ce) {
953 ZEND_HASH_FOREACH_STR_KEY_PTR(&ce->function_table, name, op_array) {
954 if (op_array->scope == ce) {
955 zend_optimize_op_array(op_array, &ctx);
956 } else if (op_array->type == ZEND_USER_FUNCTION) {
957 zend_op_array *orig_op_array;
958 if ((orig_op_array = zend_hash_find_ptr(&op_array->scope->function_table, name)) != NULL) {
959 HashTable *ht = op_array->static_variables;
960 *op_array = *orig_op_array;
961 op_array->static_variables = ht;
962 }
963 }
964 } ZEND_HASH_FOREACH_END();
965 } ZEND_HASH_FOREACH_END();
966
967 #if HAVE_DFA_PASS
968 if ((ZEND_OPTIMIZER_PASS_6 & optimization_level) &&
969 (ZEND_OPTIMIZER_PASS_7 & optimization_level) &&
970 zend_build_call_graph(&ctx.arena, script, ZEND_RT_CONSTANTS, &call_graph) == SUCCESS) {
971 /* Optimize using call-graph */
972 void *checkpoint = zend_arena_checkpoint(ctx.arena);
973 int i;
974 zend_func_info *func_info;
975
976 for (i = 0; i < call_graph.op_arrays_count; i++) {
977 zend_revert_pass_two(call_graph.op_arrays[i]);
978 }
979
980 for (i = 0; i < call_graph.op_arrays_count; i++) {
981 func_info = ZEND_FUNC_INFO(call_graph.op_arrays[i]);
982 if (func_info) {
983 func_info->call_map = zend_build_call_map(&ctx.arena, func_info, call_graph.op_arrays[i]);
984 if (call_graph.op_arrays[i]->fn_flags & ZEND_ACC_HAS_RETURN_TYPE) {
985 zend_init_func_return_info(call_graph.op_arrays[i], script, &func_info->return_info);
986 }
987 }
988 }
989
990 for (i = 0; i < call_graph.op_arrays_count; i++) {
991 func_info = ZEND_FUNC_INFO(call_graph.op_arrays[i]);
992 if (func_info) {
993 zend_dfa_analyze_op_array(call_graph.op_arrays[i], &ctx, &func_info->ssa, &func_info->flags);
994 }
995 }
996
997 //TODO: perform inner-script inference???
998 for (i = 0; i < call_graph.op_arrays_count; i++) {
999 func_info = ZEND_FUNC_INFO(call_graph.op_arrays[i]);
1000 if (func_info) {
1001 zend_dfa_optimize_op_array(call_graph.op_arrays[i], &ctx, &func_info->ssa);
1002 }
1003 }
1004
1005 if (debug_level & ZEND_DUMP_AFTER_PASS_7) {
1006 for (i = 0; i < call_graph.op_arrays_count; i++) {
1007 zend_dump_op_array(call_graph.op_arrays[i], 0, "after pass 7", NULL);
1008 }
1009 }
1010
1011 if (ZEND_OPTIMIZER_PASS_12 & optimization_level) {
1012 for (i = 0; i < call_graph.op_arrays_count; i++) {
1013 zend_adjust_fcall_stack_size_graph(call_graph.op_arrays[i]);
1014 }
1015 }
1016
1017 for (i = 0; i < call_graph.op_arrays_count; i++) {
1018 func_info = ZEND_FUNC_INFO(call_graph.op_arrays[i]);
1019 if (func_info && func_info->ssa.var_info) {
1020 zend_redo_pass_two_ex(call_graph.op_arrays[i], &func_info->ssa);
1021 } else {
1022 zend_redo_pass_two(call_graph.op_arrays[i]);
1023 }
1024 }
1025
1026 for (i = 0; i < call_graph.op_arrays_count; i++) {
1027 ZEND_SET_FUNC_INFO(call_graph.op_arrays[i], NULL);
1028 }
1029
1030 ZEND_HASH_FOREACH_PTR(&script->class_table, ce) {
1031 ZEND_HASH_FOREACH_STR_KEY_PTR(&ce->function_table, name, op_array) {
1032 if (op_array->scope != ce) {
1033 zend_op_array *orig_op_array;
1034 if ((orig_op_array = zend_hash_find_ptr(&op_array->scope->function_table, name)) != NULL) {
1035 HashTable *ht = op_array->static_variables;
1036 *op_array = *orig_op_array;
1037 op_array->static_variables = ht;
1038 }
1039 }
1040 } ZEND_HASH_FOREACH_END();
1041 } ZEND_HASH_FOREACH_END();
1042
1043 zend_arena_release(&ctx.arena, checkpoint);
1044 } else
1045 #endif
1046
1047 if (ZEND_OPTIMIZER_PASS_12 & optimization_level) {
1048 zend_adjust_fcall_stack_size(&script->main_op_array, &ctx);
1049
1050 ZEND_HASH_FOREACH_PTR(&script->function_table, op_array) {
1051 zend_adjust_fcall_stack_size(op_array, &ctx);
1052 } ZEND_HASH_FOREACH_END();
1053
1054 ZEND_HASH_FOREACH_PTR(&script->class_table, ce) {
1055 ZEND_HASH_FOREACH_STR_KEY_PTR(&ce->function_table, name, op_array) {
1056 if (op_array->scope == ce) {
1057 zend_adjust_fcall_stack_size(op_array, &ctx);
1058 } else if (op_array->type == ZEND_USER_FUNCTION) {
1059 zend_op_array *orig_op_array;
1060 if ((orig_op_array = zend_hash_find_ptr(&op_array->scope->function_table, name)) != NULL) {
1061 HashTable *ht = op_array->static_variables;
1062 *op_array = *orig_op_array;
1063 op_array->static_variables = ht;
1064 }
1065 }
1066 } ZEND_HASH_FOREACH_END();
1067 } ZEND_HASH_FOREACH_END();
1068 }
1069
1070 if (ctx.constants) {
1071 zend_hash_destroy(ctx.constants);
1072 }
1073 zend_arena_destroy(ctx.arena);
1074
1075 return 1;
1076 }
1077
zend_optimizer_startup(void)1078 int zend_optimizer_startup(void)
1079 {
1080 return zend_func_info_startup();
1081 }
1082
zend_optimizer_shutdown(void)1083 int zend_optimizer_shutdown(void)
1084 {
1085 return zend_func_info_shutdown();
1086 }
1087
1088 /*
1089 * Local variables:
1090 * tab-width: 4
1091 * c-basic-offset: 4
1092 * indent-tabs-mode: t
1093 * End:
1094 */
1095