1 /*
2 +----------------------------------------------------------------------+
3 | Zend OPcache |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 1998-2017 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
zend_optimizer_zval_dtor_wrapper(zval * zvalue)30 static void zend_optimizer_zval_dtor_wrapper(zval *zvalue)
31 {
32 zval_dtor(zvalue);
33 }
34
zend_optimizer_collect_constant(zend_optimizer_ctx * ctx,zval * name,zval * value)35 void zend_optimizer_collect_constant(zend_optimizer_ctx *ctx, zval *name, zval* value)
36 {
37 zval val;
38
39 if (!ctx->constants) {
40 ctx->constants = zend_arena_alloc(&ctx->arena, sizeof(HashTable));
41 zend_hash_init(ctx->constants, 16, NULL, zend_optimizer_zval_dtor_wrapper, 0);
42 }
43 ZVAL_DUP(&val, value);
44 zend_hash_add(ctx->constants, Z_STR_P(name), &val);
45 }
46
zend_optimizer_get_collected_constant(HashTable * constants,zval * name,zval * value)47 int zend_optimizer_get_collected_constant(HashTable *constants, zval *name, zval* value)
48 {
49 zval *val;
50
51 if ((val = zend_hash_find(constants, Z_STR_P(name))) != NULL) {
52 ZVAL_DUP(value, val);
53 return 1;
54 }
55 return 0;
56 }
57
zend_optimizer_lookup_cv(zend_op_array * op_array,zend_string * name)58 int zend_optimizer_lookup_cv(zend_op_array *op_array, zend_string* name)
59 {
60 int i = 0;
61 zend_ulong hash_value = zend_string_hash_val(name);
62
63 while (i < op_array->last_var) {
64 if (op_array->vars[i] == name ||
65 (ZSTR_H(op_array->vars[i]) == hash_value &&
66 ZSTR_LEN(op_array->vars[i]) == ZSTR_LEN(name) &&
67 memcmp(ZSTR_VAL(op_array->vars[i]), ZSTR_VAL(name), ZSTR_LEN(name)) == 0)) {
68 return (int)(zend_intptr_t)ZEND_CALL_VAR_NUM(NULL, i);
69 }
70 i++;
71 }
72 i = op_array->last_var;
73 op_array->last_var++;
74 op_array->vars = erealloc(op_array->vars, op_array->last_var * sizeof(zend_string*));
75 op_array->vars[i] = zend_string_dup(name, 0);
76
77 /* all IS_TMP_VAR and IS_VAR variable numbers have to be adjusted */
78 {
79 zend_op *opline = op_array->opcodes;
80 zend_op *end = opline + op_array->last;
81 while (opline < end) {
82 if (opline->op1_type & (IS_TMP_VAR|IS_VAR)) {
83 opline->op1.var += sizeof(zval);
84 }
85 if (opline->op2_type & (IS_TMP_VAR|IS_VAR)) {
86 opline->op2.var += sizeof(zval);
87 }
88 if (opline->result_type & (IS_TMP_VAR|IS_VAR)) {
89 opline->result.var += sizeof(zval);
90 }
91 if (opline->opcode == ZEND_DECLARE_INHERITED_CLASS ||
92 opline->opcode == ZEND_DECLARE_ANON_INHERITED_CLASS ||
93 opline->opcode == ZEND_DECLARE_INHERITED_CLASS_DELAYED) {
94 opline->extended_value += sizeof(zval);
95 }
96 opline++;
97 }
98 }
99
100 return (int)(zend_intptr_t)ZEND_CALL_VAR_NUM(NULL, i);
101 }
102
zend_optimizer_add_literal(zend_op_array * op_array,zval * zv)103 int zend_optimizer_add_literal(zend_op_array *op_array, zval *zv)
104 {
105 int i = op_array->last_literal;
106 op_array->last_literal++;
107 op_array->literals = (zval*)erealloc(op_array->literals, op_array->last_literal * sizeof(zval));
108 ZVAL_COPY_VALUE(&op_array->literals[i], zv);
109 Z_CACHE_SLOT(op_array->literals[i]) = -1;
110 return i;
111 }
112
zend_optimizer_add_literal_string(zend_op_array * op_array,zend_string * str)113 static inline int zend_optimizer_add_literal_string(zend_op_array *op_array, zend_string *str) {
114 zval zv;
115 ZVAL_STR(&zv, str);
116 zend_string_hash_val(str);
117 return zend_optimizer_add_literal(op_array, &zv);
118 }
119
zend_optimizer_is_disabled_func(const char * name,size_t len)120 int zend_optimizer_is_disabled_func(const char *name, size_t len) {
121 zend_function *fbc = (zend_function *)zend_hash_str_find_ptr(EG(function_table), name, len);
122
123 return (fbc && fbc->type == ZEND_INTERNAL_FUNCTION &&
124 fbc->internal_function.handler == ZEND_FN(display_disabled_function));
125 }
126
drop_leading_backslash(zval * val)127 static inline void drop_leading_backslash(zval *val) {
128 if (Z_STRVAL_P(val)[0] == '\\') {
129 zend_string *str = zend_string_init(Z_STRVAL_P(val) + 1, Z_STRLEN_P(val) - 1, 0);
130 zval_dtor(val);
131 ZVAL_STR(val, str);
132 }
133 }
134
alloc_cache_slots_op1(zend_op_array * op_array,zend_op * opline,uint32_t num)135 static inline void alloc_cache_slots_op1(zend_op_array *op_array, zend_op *opline, uint32_t num) {
136 Z_CACHE_SLOT(op_array->literals[opline->op1.constant]) = op_array->cache_size;
137 op_array->cache_size += num * sizeof(void *);
138 }
alloc_cache_slots_op2(zend_op_array * op_array,zend_op * opline,uint32_t num)139 static inline void alloc_cache_slots_op2(zend_op_array *op_array, zend_op *opline, uint32_t num) {
140 Z_CACHE_SLOT(op_array->literals[opline->op2.constant]) = op_array->cache_size;
141 op_array->cache_size += num * sizeof(void *);
142 }
143
144 #define REQUIRES_STRING(val) do { \
145 if (Z_TYPE_P(val) != IS_STRING) { \
146 zval_dtor(val); \
147 return 0; \
148 } \
149 } while (0)
150
151 #define TO_STRING_NOWARN(val) do { \
152 if (Z_TYPE_P(val) >= IS_ARRAY) { \
153 zval_dtor(val); \
154 return 0; \
155 } \
156 convert_to_string(val); \
157 } while (0)
158
zend_optimizer_update_op1_const(zend_op_array * op_array,zend_op * opline,zval * val)159 int zend_optimizer_update_op1_const(zend_op_array *op_array,
160 zend_op *opline,
161 zval *val)
162 {
163 switch (opline->opcode) {
164 case ZEND_FREE:
165 MAKE_NOP(opline);
166 zval_dtor(val);
167 return 1;
168 case ZEND_INIT_STATIC_METHOD_CALL:
169 case ZEND_CATCH:
170 case ZEND_FETCH_CONSTANT:
171 case ZEND_DEFINED:
172 case ZEND_NEW:
173 REQUIRES_STRING(val);
174 drop_leading_backslash(val);
175 opline->op1.constant = zend_optimizer_add_literal(op_array, val);
176 alloc_cache_slots_op1(op_array, opline, 1);
177 zend_optimizer_add_literal_string(op_array, zend_string_tolower(Z_STR_P(val)));
178 break;
179 case ZEND_FETCH_R:
180 case ZEND_FETCH_W:
181 case ZEND_FETCH_RW:
182 case ZEND_FETCH_IS:
183 case ZEND_FETCH_UNSET:
184 case ZEND_FETCH_FUNC_ARG:
185 TO_STRING_NOWARN(val);
186 opline->op1.constant = zend_optimizer_add_literal(op_array, val);
187 if (opline->extended_value == ZEND_FETCH_STATIC_MEMBER) {
188 alloc_cache_slots_op1(op_array, opline, 2);
189 }
190 break;
191 case ZEND_CONCAT:
192 case ZEND_FAST_CONCAT:
193 TO_STRING_NOWARN(val);
194 /* break missing intentionally */
195 default:
196 opline->op1.constant = zend_optimizer_add_literal(op_array, val);
197 break;
198 }
199
200 ZEND_OP1_TYPE(opline) = IS_CONST;
201 if (Z_TYPE(ZEND_OP1_LITERAL(opline)) == IS_STRING) {
202 zend_string_hash_val(Z_STR(ZEND_OP1_LITERAL(opline)));
203 }
204 return 1;
205 }
206
zend_optimizer_update_op2_const(zend_op_array * op_array,zend_op * opline,zval * val)207 int zend_optimizer_update_op2_const(zend_op_array *op_array,
208 zend_op *opline,
209 zval *val)
210 {
211 switch (opline->opcode) {
212 case ZEND_ASSIGN_REF:
213 zval_dtor(val);
214 return 0;
215 case ZEND_FETCH_R:
216 case ZEND_FETCH_W:
217 case ZEND_FETCH_RW:
218 case ZEND_FETCH_IS:
219 case ZEND_FETCH_UNSET:
220 case ZEND_FETCH_FUNC_ARG:
221 case ZEND_FETCH_CLASS:
222 case ZEND_INIT_FCALL_BY_NAME:
223 /*case ZEND_INIT_NS_FCALL_BY_NAME:*/
224 case ZEND_UNSET_VAR:
225 case ZEND_ISSET_ISEMPTY_VAR:
226 case ZEND_ADD_INTERFACE:
227 case ZEND_ADD_TRAIT:
228 case ZEND_INSTANCEOF:
229 REQUIRES_STRING(val);
230 drop_leading_backslash(val);
231 opline->op2.constant = zend_optimizer_add_literal(op_array, val);
232 zend_optimizer_add_literal_string(op_array, zend_string_tolower(Z_STR_P(val)));
233 alloc_cache_slots_op2(op_array, opline, 1);
234 break;
235 case ZEND_INIT_FCALL:
236 REQUIRES_STRING(val);
237 zend_str_tolower(Z_STRVAL_P(val), Z_STRLEN_P(val));
238 opline->op2.constant = zend_optimizer_add_literal(op_array, val);
239 alloc_cache_slots_op2(op_array, opline, 1);
240 break;
241 case ZEND_INIT_DYNAMIC_CALL:
242 if (Z_TYPE_P(val) == IS_STRING) {
243 if (zend_memrchr(Z_STRVAL_P(val), ':', Z_STRLEN_P(val))) {
244 zval_dtor(val);
245 return 0;
246 }
247
248 opline->opcode = ZEND_INIT_FCALL_BY_NAME;
249 drop_leading_backslash(val);
250 opline->op2.constant = zend_optimizer_add_literal(op_array, val);
251 zend_optimizer_add_literal_string(op_array, zend_string_tolower(Z_STR_P(val)));
252 alloc_cache_slots_op2(op_array, opline, 1);
253 } else {
254 opline->op2.constant = zend_optimizer_add_literal(op_array, val);
255 }
256 break;
257 case ZEND_INIT_METHOD_CALL:
258 case ZEND_INIT_STATIC_METHOD_CALL:
259 REQUIRES_STRING(val);
260 opline->op2.constant = zend_optimizer_add_literal(op_array, val);
261 zend_optimizer_add_literal_string(op_array, zend_string_tolower(Z_STR_P(val)));
262 alloc_cache_slots_op2(op_array, opline, 2);
263 break;
264 /*case ZEND_FETCH_CONSTANT:*/
265 case ZEND_ASSIGN_OBJ:
266 case ZEND_FETCH_OBJ_R:
267 case ZEND_FETCH_OBJ_W:
268 case ZEND_FETCH_OBJ_RW:
269 case ZEND_FETCH_OBJ_IS:
270 case ZEND_FETCH_OBJ_UNSET:
271 case ZEND_FETCH_OBJ_FUNC_ARG:
272 case ZEND_UNSET_OBJ:
273 case ZEND_PRE_INC_OBJ:
274 case ZEND_PRE_DEC_OBJ:
275 case ZEND_POST_INC_OBJ:
276 case ZEND_POST_DEC_OBJ:
277 case ZEND_ISSET_ISEMPTY_PROP_OBJ:
278 TO_STRING_NOWARN(val);
279 opline->op2.constant = zend_optimizer_add_literal(op_array, val);
280 alloc_cache_slots_op2(op_array, opline, 2);
281 break;
282 case ZEND_ASSIGN_ADD:
283 case ZEND_ASSIGN_SUB:
284 case ZEND_ASSIGN_MUL:
285 case ZEND_ASSIGN_DIV:
286 case ZEND_ASSIGN_POW:
287 case ZEND_ASSIGN_MOD:
288 case ZEND_ASSIGN_SL:
289 case ZEND_ASSIGN_SR:
290 case ZEND_ASSIGN_CONCAT:
291 case ZEND_ASSIGN_BW_OR:
292 case ZEND_ASSIGN_BW_AND:
293 case ZEND_ASSIGN_BW_XOR:
294 if (opline->extended_value == ZEND_ASSIGN_OBJ) {
295 TO_STRING_NOWARN(val);
296 opline->op2.constant = zend_optimizer_add_literal(op_array, val);
297 alloc_cache_slots_op2(op_array, opline, 2);
298 } else {
299 opline->op2.constant = zend_optimizer_add_literal(op_array, val);
300 }
301 break;
302 case ZEND_OP_DATA:
303 if ((opline-1)->opcode != ZEND_ASSIGN_DIM &&
304 ((opline-1)->extended_value != ZEND_ASSIGN_DIM ||
305 ((opline-1)->opcode != ZEND_ASSIGN_ADD &&
306 (opline-1)->opcode != ZEND_ASSIGN_SUB &&
307 (opline-1)->opcode != ZEND_ASSIGN_MUL &&
308 (opline-1)->opcode != ZEND_ASSIGN_DIV &&
309 (opline-1)->opcode != ZEND_ASSIGN_POW &&
310 (opline-1)->opcode != ZEND_ASSIGN_MOD &&
311 (opline-1)->opcode != ZEND_ASSIGN_SL &&
312 (opline-1)->opcode != ZEND_ASSIGN_SR &&
313 (opline-1)->opcode != ZEND_ASSIGN_CONCAT &&
314 (opline-1)->opcode != ZEND_ASSIGN_BW_OR &&
315 (opline-1)->opcode != ZEND_ASSIGN_BW_AND &&
316 (opline-1)->opcode != ZEND_ASSIGN_BW_XOR))
317 ) {
318 opline->op2.constant = zend_optimizer_add_literal(op_array, val);
319 break;
320 }
321 /* break missing intentionally */
322 case ZEND_ISSET_ISEMPTY_DIM_OBJ:
323 case ZEND_ADD_ARRAY_ELEMENT:
324 case ZEND_INIT_ARRAY:
325 case ZEND_ASSIGN_DIM:
326 case ZEND_UNSET_DIM:
327 case ZEND_FETCH_DIM_R:
328 case ZEND_FETCH_DIM_W:
329 case ZEND_FETCH_DIM_RW:
330 case ZEND_FETCH_DIM_IS:
331 case ZEND_FETCH_DIM_FUNC_ARG:
332 case ZEND_FETCH_DIM_UNSET:
333 case ZEND_FETCH_LIST:
334 if (Z_TYPE_P(val) == IS_STRING) {
335 zend_ulong index;
336 if (ZEND_HANDLE_NUMERIC(Z_STR_P(val), index)) {
337 zval_dtor(val);
338 ZVAL_LONG(val, index);
339 }
340 }
341 opline->op2.constant = zend_optimizer_add_literal(op_array, val);
342 break;
343 case ZEND_ROPE_INIT:
344 case ZEND_ROPE_ADD:
345 case ZEND_ROPE_END:
346 case ZEND_CONCAT:
347 case ZEND_FAST_CONCAT:
348 TO_STRING_NOWARN(val);
349 /* break missing intentionally */
350 default:
351 opline->op2.constant = zend_optimizer_add_literal(op_array, val);
352 break;
353 }
354
355 ZEND_OP2_TYPE(opline) = IS_CONST;
356 if (Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_STRING) {
357 zend_string_hash_val(Z_STR(ZEND_OP2_LITERAL(opline)));
358 }
359 return 1;
360 }
361
zend_optimizer_replace_by_const(zend_op_array * op_array,zend_op * opline,zend_uchar type,uint32_t var,zval * val)362 int zend_optimizer_replace_by_const(zend_op_array *op_array,
363 zend_op *opline,
364 zend_uchar type,
365 uint32_t var,
366 zval *val)
367 {
368 zend_op *end = op_array->opcodes + op_array->last;
369
370 while (opline < end) {
371 if (ZEND_OP1_TYPE(opline) == type &&
372 ZEND_OP1(opline).var == var) {
373 switch (opline->opcode) {
374 case ZEND_FETCH_DIM_W:
375 case ZEND_FETCH_DIM_RW:
376 case ZEND_FETCH_DIM_FUNC_ARG:
377 case ZEND_FETCH_DIM_UNSET:
378 case ZEND_ASSIGN_DIM:
379 case ZEND_SEPARATE:
380 case ZEND_RETURN_BY_REF:
381 zval_dtor(val);
382 return 0;
383 case ZEND_SEND_VAR:
384 opline->extended_value = 0;
385 opline->opcode = ZEND_SEND_VAL;
386 break;
387 case ZEND_SEND_VAR_EX:
388 opline->extended_value = 0;
389 opline->opcode = ZEND_SEND_VAL_EX;
390 break;
391 case ZEND_SEND_VAR_NO_REF:
392 if (opline->extended_value & ZEND_ARG_COMPILE_TIME_BOUND) {
393 if (opline->extended_value & ZEND_ARG_SEND_BY_REF) {
394 zval_dtor(val);
395 return 0;
396 }
397 opline->extended_value = 0;
398 opline->opcode = ZEND_SEND_VAL_EX;
399 } else {
400 opline->extended_value = 0;
401 opline->opcode = ZEND_SEND_VAL;
402 }
403 break;
404 case ZEND_SEND_USER:
405 opline->opcode = ZEND_SEND_VAL_EX;
406 break;
407 /* In most cases IS_TMP_VAR operand may be used only once.
408 * The operands are usually destroyed by the opcode handler.
409 * ZEND_CASE and ZEND_FETCH_LIST are exceptions, they keeps operand
410 * unchanged, and allows its reuse. these instructions
411 * usually terminated by ZEND_FREE that finally kills the value.
412 */
413 case ZEND_FETCH_LIST: {
414 zend_op *m = opline;
415 do {
416 if (m->opcode == ZEND_FETCH_LIST &&
417 ZEND_OP1_TYPE(m) == type &&
418 ZEND_OP1(m).var == var) {
419 zend_optimizer_update_op1_const(op_array, m, val);
420 }
421 m++;
422 } while (m->opcode != ZEND_FREE || ZEND_OP1_TYPE(m) != type || ZEND_OP1(m).var != var);
423 ZEND_ASSERT(m->opcode == ZEND_FREE && ZEND_OP1_TYPE(m) == type && ZEND_OP1(m).var == var);
424 MAKE_NOP(m);
425 return 1;
426 }
427 case ZEND_CASE:
428 case ZEND_FREE: {
429 zend_op *m, *n;
430 int brk = op_array->last_brk_cont;
431 zend_bool in_switch = 0;
432 while (brk--) {
433 if (op_array->brk_cont_array[brk].start <= (opline - op_array->opcodes) &&
434 op_array->brk_cont_array[brk].brk > (opline - op_array->opcodes)) {
435 in_switch = 1;
436 break;
437 }
438 }
439
440 if (!in_switch) {
441 ZEND_ASSERT(opline->opcode == ZEND_FREE);
442 MAKE_NOP(opline);
443 zval_dtor(val);
444 return 1;
445 }
446
447 m = opline;
448 n = op_array->opcodes + op_array->brk_cont_array[brk].brk + 1;
449 while (m < n) {
450 if (ZEND_OP1_TYPE(m) == type &&
451 ZEND_OP1(m).var == var) {
452 if (m->opcode == ZEND_CASE) {
453 zval old_val;
454 ZVAL_COPY_VALUE(&old_val, val);
455 zval_copy_ctor(val);
456 zend_optimizer_update_op1_const(op_array, m, val);
457 ZVAL_COPY_VALUE(val, &old_val);
458 } else if (m->opcode == ZEND_FREE) {
459 MAKE_NOP(m);
460 } else {
461 ZEND_ASSERT(0);
462 }
463 }
464 m++;
465 }
466 zval_dtor(val);
467 return 1;
468 }
469 case ZEND_VERIFY_RETURN_TYPE: {
470 zend_arg_info *ret_info = op_array->arg_info - 1;
471 ZEND_ASSERT((opline + 1)->opcode == ZEND_RETURN || (opline + 1)->opcode == ZEND_RETURN_BY_REF);
472 if (ret_info->class_name
473 || ret_info->type_hint == IS_CALLABLE
474 || !ZEND_SAME_FAKE_TYPE(ret_info->type_hint, Z_TYPE_P(val))
475 || (op_array->fn_flags & ZEND_ACC_RETURN_REFERENCE)) {
476 zval_dtor(val);
477 return 0;
478 }
479 MAKE_NOP(opline);
480 zend_optimizer_update_op1_const(op_array, opline + 1, val);
481 return 1;
482 }
483 default:
484 break;
485 }
486 return zend_optimizer_update_op1_const(op_array, opline, val);
487 }
488
489 if (ZEND_OP2_TYPE(opline) == type &&
490 ZEND_OP2(opline).var == var) {
491 return zend_optimizer_update_op2_const(op_array, opline, val);
492 }
493 opline++;
494 }
495
496 return 1;
497 }
498
zend_optimize(zend_op_array * op_array,zend_optimizer_ctx * ctx)499 static void zend_optimize(zend_op_array *op_array,
500 zend_optimizer_ctx *ctx)
501 {
502 if (op_array->type == ZEND_EVAL_CODE) {
503 return;
504 }
505
506 /* pass 1
507 * - substitute persistent constants (true, false, null, etc)
508 * - perform compile-time evaluation of constant binary and unary operations
509 * - optimize series of ADD_STRING and/or ADD_CHAR
510 * - convert CAST(IS_BOOL,x) into BOOL(x)
511 */
512 if (ZEND_OPTIMIZER_PASS_1 & OPTIMIZATION_LEVEL) {
513 zend_optimizer_pass1(op_array, ctx);
514 }
515
516 /* pass 2:
517 * - convert non-numeric constants to numeric constants in numeric operators
518 * - optimize constant conditional JMPs
519 * - optimize static BRKs and CONTs
520 * - pre-evaluate constant function calls
521 */
522 if (ZEND_OPTIMIZER_PASS_2 & OPTIMIZATION_LEVEL) {
523 zend_optimizer_pass2(op_array);
524 }
525
526 /* pass 3:
527 * - optimize $i = $i+expr to $i+=expr
528 * - optimize series of JMPs
529 * - change $i++ to ++$i where possible
530 */
531 if (ZEND_OPTIMIZER_PASS_3 & OPTIMIZATION_LEVEL) {
532 zend_optimizer_pass3(op_array);
533 }
534
535 /* pass 4:
536 * - INIT_FCALL_BY_NAME -> DO_FCALL
537 */
538 if (ZEND_OPTIMIZER_PASS_4 & OPTIMIZATION_LEVEL) {
539 optimize_func_calls(op_array, ctx);
540 }
541
542 /* pass 5:
543 * - CFG optimization
544 */
545 if (ZEND_OPTIMIZER_PASS_5 & OPTIMIZATION_LEVEL) {
546 optimize_cfg(op_array, ctx);
547 }
548
549 /* pass 9:
550 * - Optimize temp variables usage
551 */
552 if (ZEND_OPTIMIZER_PASS_9 & OPTIMIZATION_LEVEL) {
553 optimize_temporary_variables(op_array, ctx);
554 }
555
556 /* pass 10:
557 * - remove NOPs
558 */
559 if (((ZEND_OPTIMIZER_PASS_10|ZEND_OPTIMIZER_PASS_5) & OPTIMIZATION_LEVEL) == ZEND_OPTIMIZER_PASS_10) {
560 zend_optimizer_nop_removal(op_array);
561 }
562
563 /* pass 11:
564 * - Compact literals table
565 */
566 if (ZEND_OPTIMIZER_PASS_11 & OPTIMIZATION_LEVEL) {
567 zend_optimizer_compact_literals(op_array, ctx);
568 }
569 }
570
zend_accel_optimize(zend_op_array * op_array,zend_optimizer_ctx * ctx)571 static void zend_accel_optimize(zend_op_array *op_array,
572 zend_optimizer_ctx *ctx)
573 {
574 zend_op *opline, *end;
575
576 /* Revert pass_two() */
577 opline = op_array->opcodes;
578 end = opline + op_array->last;
579 while (opline < end) {
580 if (opline->op1_type == IS_CONST) {
581 ZEND_PASS_TWO_UNDO_CONSTANT(op_array, opline->op1);
582 }
583 if (opline->op2_type == IS_CONST) {
584 ZEND_PASS_TWO_UNDO_CONSTANT(op_array, opline->op2);
585 }
586 switch (opline->opcode) {
587 case ZEND_JMP:
588 case ZEND_FAST_CALL:
589 case ZEND_DECLARE_ANON_CLASS:
590 case ZEND_DECLARE_ANON_INHERITED_CLASS:
591 ZEND_PASS_TWO_UNDO_JMP_TARGET(op_array, opline, ZEND_OP1(opline));
592 break;
593 case ZEND_JMPZNZ:
594 /* relative offset into absolute index */
595 opline->extended_value = ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value);
596 /* break omitted intentionally */
597 case ZEND_JMPZ:
598 case ZEND_JMPNZ:
599 case ZEND_JMPZ_EX:
600 case ZEND_JMPNZ_EX:
601 case ZEND_JMP_SET:
602 case ZEND_COALESCE:
603 case ZEND_NEW:
604 case ZEND_FE_RESET_R:
605 case ZEND_FE_RESET_RW:
606 case ZEND_ASSERT_CHECK:
607 ZEND_PASS_TWO_UNDO_JMP_TARGET(op_array, opline, ZEND_OP2(opline));
608 break;
609 case ZEND_FE_FETCH_R:
610 case ZEND_FE_FETCH_RW:
611 opline->extended_value = ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value);
612 break;
613 }
614 opline++;
615 }
616
617 /* Do actual optimizations */
618 zend_optimize(op_array, ctx);
619
620 /* Redo pass_two() */
621 opline = op_array->opcodes;
622 end = opline + op_array->last;
623 while (opline < end) {
624 if (opline->op1_type == IS_CONST) {
625 ZEND_PASS_TWO_UPDATE_CONSTANT(op_array, opline->op1);
626 }
627 if (opline->op2_type == IS_CONST) {
628 ZEND_PASS_TWO_UPDATE_CONSTANT(op_array, opline->op2);
629 }
630 switch (opline->opcode) {
631 case ZEND_JMP:
632 case ZEND_FAST_CALL:
633 case ZEND_DECLARE_ANON_CLASS:
634 case ZEND_DECLARE_ANON_INHERITED_CLASS:
635 ZEND_PASS_TWO_UPDATE_JMP_TARGET(op_array, opline, ZEND_OP1(opline));
636 break;
637 case ZEND_JMPZNZ:
638 /* absolute index to relative offset */
639 opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, opline, opline->extended_value);
640 /* break omitted intentionally */
641 case ZEND_JMPZ:
642 case ZEND_JMPNZ:
643 case ZEND_JMPZ_EX:
644 case ZEND_JMPNZ_EX:
645 case ZEND_JMP_SET:
646 case ZEND_COALESCE:
647 case ZEND_NEW:
648 case ZEND_FE_RESET_R:
649 case ZEND_FE_RESET_RW:
650 case ZEND_ASSERT_CHECK:
651 ZEND_PASS_TWO_UPDATE_JMP_TARGET(op_array, opline, ZEND_OP2(opline));
652 break;
653 case ZEND_FE_FETCH_R:
654 case ZEND_FE_FETCH_RW:
655 opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, opline, opline->extended_value);
656 break;
657 }
658 ZEND_VM_SET_OPCODE_HANDLER(opline);
659 opline++;
660 }
661 }
662
zend_accel_adjust_fcall_stack_size(zend_op_array * op_array,zend_optimizer_ctx * ctx)663 static void zend_accel_adjust_fcall_stack_size(zend_op_array *op_array, zend_optimizer_ctx *ctx)
664 {
665 zend_function *func;
666 zend_op *opline, *end;
667
668 opline = op_array->opcodes;
669 end = opline + op_array->last;
670 while (opline < end) {
671 if (opline->opcode == ZEND_INIT_FCALL) {
672 func = zend_hash_find_ptr(
673 &ctx->script->function_table,
674 Z_STR_P(RT_CONSTANT(op_array, opline->op2)));
675 if (func) {
676 opline->op1.num = zend_vm_calc_used_stack(opline->extended_value, func);
677 }
678 }
679 opline++;
680 }
681 }
682
zend_accel_script_optimize(zend_persistent_script * script)683 int zend_accel_script_optimize(zend_persistent_script *script)
684 {
685 uint idx, j;
686 Bucket *p, *q;
687 zend_class_entry *ce;
688 zend_op_array *op_array;
689 zend_optimizer_ctx ctx;
690
691 ctx.arena = zend_arena_create(64 * 1024);
692 ctx.script = script;
693 ctx.constants = NULL;
694
695 zend_accel_optimize(&script->main_op_array, &ctx);
696
697 for (idx = 0; idx < script->function_table.nNumUsed; idx++) {
698 p = script->function_table.arData + idx;
699 if (Z_TYPE(p->val) == IS_UNDEF) continue;
700 op_array = (zend_op_array*)Z_PTR(p->val);
701 zend_accel_optimize(op_array, &ctx);
702 }
703
704 for (idx = 0; idx < script->class_table.nNumUsed; idx++) {
705 p = script->class_table.arData + idx;
706 if (Z_TYPE(p->val) == IS_UNDEF) continue;
707 ce = (zend_class_entry*)Z_PTR(p->val);
708 for (j = 0; j < ce->function_table.nNumUsed; j++) {
709 q = ce->function_table.arData + j;
710 if (Z_TYPE(q->val) == IS_UNDEF) continue;
711 op_array = (zend_op_array*)Z_PTR(q->val);
712 if (op_array->scope == ce) {
713 zend_accel_optimize(op_array, &ctx);
714 } else if (op_array->type == ZEND_USER_FUNCTION) {
715 zend_op_array *orig_op_array;
716 if ((orig_op_array = zend_hash_find_ptr(&op_array->scope->function_table, q->key)) != NULL) {
717 HashTable *ht = op_array->static_variables;
718 *op_array = *orig_op_array;
719 op_array->static_variables = ht;
720 }
721 }
722 }
723 }
724
725 if (ZEND_OPTIMIZER_PASS_12 & OPTIMIZATION_LEVEL) {
726 zend_accel_adjust_fcall_stack_size(&script->main_op_array, &ctx);
727
728 for (idx = 0; idx < script->function_table.nNumUsed; idx++) {
729 p = script->function_table.arData + idx;
730 if (Z_TYPE(p->val) == IS_UNDEF) continue;
731 op_array = (zend_op_array*)Z_PTR(p->val);
732 zend_accel_adjust_fcall_stack_size(op_array, &ctx);
733 }
734
735 for (idx = 0; idx < script->class_table.nNumUsed; idx++) {
736 p = script->class_table.arData + idx;
737 if (Z_TYPE(p->val) == IS_UNDEF) continue;
738 ce = (zend_class_entry*)Z_PTR(p->val);
739 for (j = 0; j < ce->function_table.nNumUsed; j++) {
740 q = ce->function_table.arData + j;
741 if (Z_TYPE(q->val) == IS_UNDEF) continue;
742 op_array = (zend_op_array*)Z_PTR(q->val);
743 if (op_array->scope == ce) {
744 zend_accel_adjust_fcall_stack_size(op_array, &ctx);
745 } else if (op_array->type == ZEND_USER_FUNCTION) {
746 zend_op_array *orig_op_array;
747 if ((orig_op_array = zend_hash_find_ptr(&op_array->scope->function_table, q->key)) != NULL) {
748 HashTable *ht = op_array->static_variables;
749 *op_array = *orig_op_array;
750 op_array->static_variables = ht;
751 }
752 }
753 }
754 }
755 }
756
757 if (ctx.constants) {
758 zend_hash_destroy(ctx.constants);
759 }
760 zend_arena_destroy(ctx.arena);
761
762 return 1;
763 }
764