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