1 /*
2    +----------------------------------------------------------------------+
3    | Zend OPcache                                                         |
4    +----------------------------------------------------------------------+
5    | Copyright (c) 1998-2016 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 
30 #define OPTIMIZATION_LEVEL \
31 	ZCG(accel_directives).optimization_level
32 
zend_optimizer_zval_dtor_wrapper(zval * zvalue)33 static void zend_optimizer_zval_dtor_wrapper(zval *zvalue)
34 {
35 	zval_dtor(zvalue);
36 }
37 
zend_optimizer_collect_constant(HashTable ** constants,zval * name,zval * value)38 static void zend_optimizer_collect_constant(HashTable **constants, zval *name, zval* value)
39 {
40 	zval val;
41 
42 	if (!*constants) {
43 		*constants = emalloc(sizeof(HashTable));
44 		zend_hash_init(*constants, 16, NULL, (void (*)(void *))zend_optimizer_zval_dtor_wrapper, 0);
45 	}
46 	val = *value;
47 	zval_copy_ctor(&val);
48 	zend_hash_add(*constants, Z_STRVAL_P(name), Z_STRLEN_P(name)+1, (void**)&val, sizeof(zval), NULL);
49 }
50 
zend_optimizer_get_collected_constant(HashTable * constants,zval * name,zval * value)51 static int zend_optimizer_get_collected_constant(HashTable *constants, zval *name, zval* value)
52 {
53 	zval *val;
54 
55 	if (zend_hash_find(constants, Z_STRVAL_P(name), Z_STRLEN_P(name)+1, (void**)&val) == SUCCESS) {
56 		*value = *val;
57 		zval_copy_ctor(value);
58 		return 1;
59 	}
60 	return 0;
61 }
62 
63 #if ZEND_EXTENSION_API_NO >= PHP_5_5_X_API_NO
zend_optimizer_lookup_cv(zend_op_array * op_array,char * name,int name_len)64 static int zend_optimizer_lookup_cv(zend_op_array *op_array, char* name, int name_len)
65 {
66 	int i = 0;
67 	ulong hash_value = zend_inline_hash_func(name, name_len+1);
68 
69 	while (i < op_array->last_var) {
70 		if (op_array->vars[i].name == name ||
71 		    (op_array->vars[i].hash_value == hash_value &&
72 		     op_array->vars[i].name_len == name_len &&
73 		     memcmp(op_array->vars[i].name, name, name_len) == 0)) {
74 			return i;
75 		}
76 		i++;
77 	}
78 	i = op_array->last_var;
79 	op_array->last_var++;
80 	op_array->vars = erealloc(op_array->vars, op_array->last_var * sizeof(zend_compiled_variable));
81 	if (IS_INTERNED(name)) {
82 		op_array->vars[i].name = name;
83 	} else {
84 		op_array->vars[i].name = estrndup(name, name_len);
85 	}
86 	op_array->vars[i].name_len = name_len;
87 	op_array->vars[i].hash_value = hash_value;
88 	return i;
89 }
90 #endif
91 
92 #if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
zend_optimizer_add_literal(zend_op_array * op_array,const zval * zv TSRMLS_DC)93 int zend_optimizer_add_literal(zend_op_array *op_array, const zval *zv TSRMLS_DC)
94 {
95 	int i = op_array->last_literal;
96 	op_array->last_literal++;
97 	op_array->literals = (zend_literal*)erealloc(op_array->literals, op_array->last_literal * sizeof(zend_literal));
98 	op_array->literals[i].constant = *zv;
99 	op_array->literals[i].hash_value = 0;
100 	op_array->literals[i].cache_slot = -1;
101 	Z_SET_REFCOUNT(op_array->literals[i].constant, 2);
102 	Z_SET_ISREF(op_array->literals[i].constant);
103 	return i;
104 }
105 
106 # define LITERAL_LONG(op, val) do { \
107 		zval _c; \
108 		ZVAL_LONG(&_c, val); \
109 		op.constant = zend_optimizer_add_literal(op_array, &_c TSRMLS_CC); \
110 	} while (0)
111 
112 # define LITERAL_BOOL(op, val) do { \
113 		zval _c; \
114 		ZVAL_BOOL(&_c, val); \
115 		op.constant = zend_optimizer_add_literal(op_array, &_c TSRMLS_CC); \
116 	} while (0)
117 
118 # define literal_dtor(zv) do { \
119 		zval_dtor(zv); \
120 		Z_TYPE_P(zv) = IS_NULL; \
121 	} while (0)
122 
123 #define COPY_NODE(target, src) do { \
124 		target ## _type = src ## _type; \
125 		target = src; \
126 	} while (0)
127 
128 #else
129 
130 # define LITERAL_LONG(op, val) ZVAL_LONG(&op.u.constant, val)
131 
132 # define LITERAL_BOOL(op, val) ZVAL_BOOL(&op.u.constant, val)
133 
134 # define literal_dtor(zv) zval_dtor(zv)
135 
136 #define COPY_NODE(target, src) do { \
137 		target = src; \
138 	} while (0)
139 
140 #endif
141 
update_op1_const(zend_op_array * op_array,zend_op * opline,zval * val TSRMLS_DC)142 static void update_op1_const(zend_op_array *op_array,
143                              zend_op       *opline,
144                              zval          *val TSRMLS_DC)
145 {
146 	if (opline->opcode == ZEND_FREE) {
147 		MAKE_NOP(opline);
148 		zval_dtor(val);
149 	} else {
150 		ZEND_OP1_TYPE(opline) = IS_CONST;
151 #if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
152 		if (Z_TYPE_P(val) == IS_STRING) {
153 			switch (opline->opcode) {
154 				case ZEND_INIT_STATIC_METHOD_CALL:
155 				case ZEND_CATCH:
156 				case ZEND_FETCH_CONSTANT:
157 					opline->op1.constant = zend_optimizer_add_literal(op_array, val TSRMLS_CC);
158 					Z_HASH_P(&ZEND_OP1_LITERAL(opline)) = zend_hash_func(Z_STRVAL(ZEND_OP1_LITERAL(opline)), Z_STRLEN(ZEND_OP1_LITERAL(opline)) + 1);
159 					op_array->literals[opline->op1.constant].cache_slot = op_array->last_cache_slot++;
160 					Z_STRVAL_P(val) = zend_str_tolower_dup(Z_STRVAL_P(val), Z_STRLEN_P(val));
161 					zend_optimizer_add_literal(op_array, val TSRMLS_CC);
162 					op_array->literals[opline->op1.constant+1].hash_value = zend_hash_func(Z_STRVAL(op_array->literals[opline->op1.constant+1].constant), Z_STRLEN(op_array->literals[opline->op1.constant+1].constant) + 1);
163 					break;
164 				case ZEND_DO_FCALL:
165 					zend_str_tolower(Z_STRVAL_P(val), Z_STRLEN_P(val));
166 					opline->op1.constant = zend_optimizer_add_literal(op_array, val TSRMLS_CC);
167 					Z_HASH_P(&ZEND_OP1_LITERAL(opline)) = zend_hash_func(Z_STRVAL(ZEND_OP1_LITERAL(opline)), Z_STRLEN(ZEND_OP1_LITERAL(opline)) + 1);
168 					op_array->literals[opline->op1.constant].cache_slot = op_array->last_cache_slot++;
169 					break;
170 				default:
171 					opline->op1.constant = zend_optimizer_add_literal(op_array, val TSRMLS_CC);
172 					Z_HASH_P(&ZEND_OP1_LITERAL(opline)) = zend_hash_func(Z_STRVAL(ZEND_OP1_LITERAL(opline)), Z_STRLEN(ZEND_OP1_LITERAL(opline)) + 1);
173 					break;
174 			}
175 		} else {
176 			opline->op1.constant = zend_optimizer_add_literal(op_array, val TSRMLS_CC);
177 		}
178 #else
179 		ZEND_OP1_LITERAL(opline) = *val;
180 #endif
181 	}
182 }
183 
update_op2_const(zend_op_array * op_array,zend_op * opline,zval * val TSRMLS_DC)184 static void update_op2_const(zend_op_array *op_array,
185                              zend_op       *opline,
186                              zval          *val TSRMLS_DC)
187 {
188 	ZEND_OP2_TYPE(opline) = IS_CONST;
189 #if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
190 	opline->op2.constant = zend_optimizer_add_literal(op_array, val TSRMLS_CC);
191 	if (Z_TYPE_P(val) == IS_STRING) {
192 		Z_HASH_P(&ZEND_OP2_LITERAL(opline)) = zend_hash_func(Z_STRVAL(ZEND_OP2_LITERAL(opline)), Z_STRLEN(ZEND_OP2_LITERAL(opline)) + 1);
193 		switch (opline->opcode) {
194 			case ZEND_FETCH_R:
195 			case ZEND_FETCH_W:
196 			case ZEND_FETCH_RW:
197 			case ZEND_FETCH_IS:
198 			case ZEND_FETCH_UNSET:
199 			case ZEND_FETCH_FUNC_ARG:
200 			case ZEND_FETCH_CLASS:
201 			case ZEND_INIT_FCALL_BY_NAME:
202 			/*case ZEND_INIT_NS_FCALL_BY_NAME:*/
203 			case ZEND_UNSET_VAR:
204 			case ZEND_ISSET_ISEMPTY_VAR:
205 			case ZEND_ADD_INTERFACE:
206 			case ZEND_ADD_TRAIT:
207 				op_array->literals[opline->op2.constant].cache_slot = op_array->last_cache_slot++;
208 				Z_STRVAL_P(val) = zend_str_tolower_dup(Z_STRVAL_P(val), Z_STRLEN_P(val));
209 				zend_optimizer_add_literal(op_array, val TSRMLS_CC);
210 				op_array->literals[opline->op2.constant+1].hash_value = zend_hash_func(Z_STRVAL(op_array->literals[opline->op2.constant+1].constant), Z_STRLEN(op_array->literals[opline->op2.constant+1].constant) + 1);
211 				break;
212 			case ZEND_INIT_METHOD_CALL:
213 			case ZEND_INIT_STATIC_METHOD_CALL:
214 				Z_STRVAL_P(val) = zend_str_tolower_dup(Z_STRVAL_P(val), Z_STRLEN_P(val));
215 				zend_optimizer_add_literal(op_array, val TSRMLS_CC);
216 				op_array->literals[opline->op2.constant+1].hash_value = zend_hash_func(Z_STRVAL(op_array->literals[opline->op2.constant+1].constant), Z_STRLEN(op_array->literals[opline->op2.constant+1].constant) + 1);
217 				/* break missing intentionally */
218 			/*case ZEND_FETCH_CONSTANT:*/
219 			case ZEND_ASSIGN_OBJ:
220 			case ZEND_FETCH_OBJ_R:
221 			case ZEND_FETCH_OBJ_W:
222 			case ZEND_FETCH_OBJ_RW:
223 			case ZEND_FETCH_OBJ_IS:
224 			case ZEND_FETCH_OBJ_UNSET:
225 			case ZEND_FETCH_OBJ_FUNC_ARG:
226 			case ZEND_UNSET_OBJ:
227 			case ZEND_PRE_INC_OBJ:
228 			case ZEND_PRE_DEC_OBJ:
229 			case ZEND_POST_INC_OBJ:
230 			case ZEND_POST_DEC_OBJ:
231 			case ZEND_ISSET_ISEMPTY_PROP_OBJ:
232 				op_array->literals[opline->op2.constant].cache_slot = op_array->last_cache_slot;
233 				op_array->last_cache_slot += 2;
234 				break;
235 			case ZEND_ASSIGN_ADD:
236 			case ZEND_ASSIGN_SUB:
237 			case ZEND_ASSIGN_MUL:
238 			case ZEND_ASSIGN_DIV:
239 			case ZEND_ASSIGN_MOD:
240 			case ZEND_ASSIGN_SL:
241 			case ZEND_ASSIGN_SR:
242 			case ZEND_ASSIGN_CONCAT:
243 			case ZEND_ASSIGN_BW_OR:
244 			case ZEND_ASSIGN_BW_AND:
245 			case ZEND_ASSIGN_BW_XOR:
246 				if (opline->extended_value == ZEND_ASSIGN_OBJ) {
247 					op_array->literals[opline->op2.constant].cache_slot = op_array->last_cache_slot;
248 					op_array->last_cache_slot += 2;
249 				}
250 				break;
251 #if ZEND_EXTENSION_API_NO >= PHP_5_4_X_API_NO
252 			case ZEND_OP_DATA:
253 				if ((opline-1)->opcode == ZEND_ASSIGN_DIM ||
254 				    ((opline-1)->extended_value == ZEND_ASSIGN_DIM &&
255 				     ((opline-1)->opcode == ZEND_ASSIGN_ADD ||
256 				     (opline-1)->opcode == ZEND_ASSIGN_SUB ||
257 				     (opline-1)->opcode == ZEND_ASSIGN_MUL ||
258 				     (opline-1)->opcode == ZEND_ASSIGN_DIV ||
259 				     (opline-1)->opcode == ZEND_ASSIGN_MOD ||
260 				     (opline-1)->opcode == ZEND_ASSIGN_SL ||
261 				     (opline-1)->opcode == ZEND_ASSIGN_SR ||
262 				     (opline-1)->opcode == ZEND_ASSIGN_CONCAT ||
263 				     (opline-1)->opcode == ZEND_ASSIGN_BW_OR ||
264 				     (opline-1)->opcode == ZEND_ASSIGN_BW_AND ||
265 				     (opline-1)->opcode == ZEND_ASSIGN_BW_XOR))) {
266 					goto check_numeric;
267 				}
268 				break;
269 			case ZEND_ISSET_ISEMPTY_DIM_OBJ:
270 			case ZEND_ADD_ARRAY_ELEMENT:
271 			case ZEND_INIT_ARRAY:
272 			case ZEND_ASSIGN_DIM:
273 			case ZEND_UNSET_DIM:
274 			case ZEND_FETCH_DIM_R:
275 			case ZEND_FETCH_DIM_W:
276 			case ZEND_FETCH_DIM_RW:
277 			case ZEND_FETCH_DIM_IS:
278 			case ZEND_FETCH_DIM_FUNC_ARG:
279 			case ZEND_FETCH_DIM_UNSET:
280 			case ZEND_FETCH_DIM_TMP_VAR:
281 check_numeric:
282 				{
283 					ulong index;
284 					int numeric = 0;
285 
286 					ZEND_HANDLE_NUMERIC_EX(Z_STRVAL_P(val), Z_STRLEN_P(val)+1, index, numeric = 1);
287 					if (numeric) {
288 						zval_dtor(val);
289 						ZVAL_LONG(val, index);
290 						op_array->literals[opline->op2.constant].constant = *val;
291 		        	}
292 				}
293 				break;
294 #endif
295 			default:
296 				break;
297 		}
298 	}
299 #else
300 	ZEND_OP2_LITERAL(opline) = *val;
301 #endif
302 }
303 
replace_var_by_const(zend_op_array * op_array,zend_op * opline,zend_uint var,zval * val TSRMLS_DC)304 static int replace_var_by_const(zend_op_array *op_array,
305                                 zend_op       *opline,
306                                 zend_uint      var,
307                                 zval          *val TSRMLS_DC)
308 {
309 	zend_op *end = op_array->opcodes + op_array->last;
310 
311 	while (opline < end) {
312 		if (ZEND_OP1_TYPE(opline) == IS_VAR &&
313 			ZEND_OP1(opline).var == var) {
314 			switch (opline->opcode) {
315 				case ZEND_FETCH_DIM_W:
316 				case ZEND_FETCH_DIM_RW:
317 				case ZEND_FETCH_DIM_FUNC_ARG:
318 				case ZEND_FETCH_DIM_UNSET:
319 				case ZEND_ASSIGN_DIM:
320 #if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
321 				case ZEND_SEPARATE:
322 #endif
323 					return 0;
324 				case ZEND_SEND_VAR_NO_REF:
325 					if (opline->extended_value & ZEND_ARG_COMPILE_TIME_BOUND) {
326 						if (opline->extended_value & ZEND_ARG_SEND_BY_REF) {
327 							return 0;
328 						}
329 						opline->extended_value = ZEND_DO_FCALL;
330 					} else {
331 						opline->extended_value = ZEND_DO_FCALL_BY_NAME;
332 					}
333 					opline->opcode = ZEND_SEND_VAL;
334 					break;
335 				case ZEND_SWITCH_FREE:
336 				case ZEND_CASE: {
337 					zend_op *m, *n;
338 					int brk = op_array->last_brk_cont;
339 					while (brk--) {
340 						if (op_array->brk_cont_array[brk].start <= (opline - op_array->opcodes) &&
341 								op_array->brk_cont_array[brk].brk > (opline - op_array->opcodes)) {
342 							break;
343 						}
344 					}
345 					m = opline;
346 					n = op_array->opcodes + op_array->brk_cont_array[brk].brk + 1;
347 					while (m < n) {
348 						if (ZEND_OP1_TYPE(m) == IS_VAR &&
349 								ZEND_OP1(m).var == var) {
350 							if (m->opcode == ZEND_CASE) {
351 								zval old_val;
352 								old_val = *val;
353 								zval_copy_ctor(val);
354 								update_op1_const(op_array, m, val TSRMLS_CC);
355 								*val = old_val;
356 							} else if (m->opcode == ZEND_SWITCH_FREE) {
357 								MAKE_NOP(m);
358 							} else {
359 								ZEND_ASSERT(0);
360 							}
361 						}
362 						m++;
363 					}
364 					zval_dtor(val);
365 					return 1;
366 				}
367 				case ZEND_FREE:
368 					MAKE_NOP(opline);
369 					zval_dtor(val);
370 					break;
371 				default:
372 					break;
373 			}
374 			update_op1_const(op_array, opline, val TSRMLS_CC);
375 			break;
376 		}
377 
378 		if (ZEND_OP2_TYPE(opline) == IS_VAR &&
379 			ZEND_OP2(opline).var == var) {
380 			switch (opline->opcode) {
381 				case ZEND_ASSIGN_REF:
382 					return 0;
383 				default:
384 					break;
385 			}
386 			update_op2_const(op_array, opline, val TSRMLS_CC);
387 			break;
388 		}
389 		opline++;
390 	}
391 
392 	return 1;
393 }
394 
replace_tmp_by_const(zend_op_array * op_array,zend_op * opline,zend_uint var,zval * val TSRMLS_DC)395 static void replace_tmp_by_const(zend_op_array *op_array,
396                                  zend_op       *opline,
397                                  zend_uint      var,
398                                  zval          *val
399                                  TSRMLS_DC)
400 {
401 	zend_op *end = op_array->opcodes + op_array->last;
402 
403 	while (opline < end) {
404 		if (ZEND_OP1_TYPE(opline) == IS_TMP_VAR &&
405 			ZEND_OP1(opline).var == var) {
406 
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 is an exception, that keeps operand unchanged,
410 			 * and allows its reuse. The number of ZEND_CASE instructions
411 			 * usually terminated by ZEND_FREE that finally kills the value.
412 			 */
413 			if (opline->opcode == ZEND_CASE || opline->opcode == ZEND_FREE) {
414 				zend_op *m, *n;
415 				int brk = op_array->last_brk_cont;
416 				zend_bool in_switch = 0;
417 				while (brk--) {
418 					if (op_array->brk_cont_array[brk].start <= (opline - op_array->opcodes) &&
419 							op_array->brk_cont_array[brk].brk > (opline - op_array->opcodes)) {
420 						in_switch = 1;
421 						break;
422 					}
423 				}
424 
425 				if (!in_switch) {
426 					MAKE_NOP(opline);
427 					zval_dtor(val);
428 					break;
429 				}
430 
431 				m = opline;
432 				n = op_array->opcodes + op_array->brk_cont_array[brk].brk + 1;
433 				while (m < n) {
434 					if (ZEND_OP1_TYPE(m) == IS_TMP_VAR &&
435 							ZEND_OP1(m).var == var) {
436 						if (m->opcode == ZEND_CASE) {
437 							zval old_val;
438 							old_val = *val;
439 							zval_copy_ctor(val);
440 							update_op1_const(op_array, m, val TSRMLS_CC);
441 							*val = old_val;
442 						} else if (m->opcode == ZEND_FREE) {
443 							MAKE_NOP(m);
444 						} else {
445 							ZEND_ASSERT(0);
446 						}
447 					}
448 					m++;
449 				}
450 				zval_dtor(val);
451 				break;
452 			} else {
453 				update_op1_const(op_array, opline, val TSRMLS_CC);
454 				break;
455 			}
456 		}
457 
458 		if (ZEND_OP2_TYPE(opline) == IS_TMP_VAR &&
459 			ZEND_OP2(opline).var == var) {
460 
461 			update_op2_const(op_array, opline, val TSRMLS_CC);
462 			/* TMP_VAR may be used only once */
463 			break;
464 		}
465 		opline++;
466 	}
467 }
468 
469 #include "Optimizer/nop_removal.c"
470 #include "Optimizer/block_pass.c"
471 #include "Optimizer/optimize_temp_vars_5.c"
472 #include "Optimizer/compact_literals.c"
473 #include "Optimizer/optimize_func_calls.c"
474 
zend_optimize(zend_op_array * op_array,zend_persistent_script * script,HashTable ** constants TSRMLS_DC)475 static void zend_optimize(zend_op_array           *op_array,
476                           zend_persistent_script  *script,
477                           HashTable              **constants TSRMLS_DC)
478 {
479 	if (op_array->type == ZEND_EVAL_CODE ||
480 	    (op_array->fn_flags & ZEND_ACC_INTERACTIVE)) {
481 		return;
482 	}
483 
484 	/* pass 1
485 	 * - substitute persistent constants (true, false, null, etc)
486 	 * - perform compile-time evaluation of constant binary and unary operations
487 	 * - optimize series of ADD_STRING and/or ADD_CHAR
488 	 * - convert CAST(IS_BOOL,x) into BOOL(x)
489 	 */
490 #include "Optimizer/pass1_5.c"
491 
492 	/* pass 2:
493 	 * - convert non-numeric constants to numeric constants in numeric operators
494 	 * - optimize constant conditional JMPs
495 	 * - optimize static BRKs and CONTs
496 	 * - pre-evaluate constant function calls
497 	 */
498 #include "Optimizer/pass2.c"
499 
500 	/* pass 3:
501 	 * - optimize $i = $i+expr to $i+=expr
502 	 * - optimize series of JMPs
503 	 * - change $i++ to ++$i where possible
504 	 */
505 #include "Optimizer/pass3.c"
506 
507 #if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
508 	/* pass 4:
509 	 * - INIT_FCALL_BY_NAME -> DO_FCALL
510 	 */
511 	if (ZEND_OPTIMIZER_PASS_4 & OPTIMIZATION_LEVEL) {
512 		optimize_func_calls(op_array, script TSRMLS_CC);
513 	}
514 #endif
515 
516 	/* pass 5:
517 	 * - CFG optimization
518 	 */
519 #include "Optimizer/pass5.c"
520 
521 	/* pass 9:
522 	 * - Optimize temp variables usage
523 	 */
524 #include "Optimizer/pass9.c"
525 
526 	/* pass 10:
527 	 * - remove NOPs
528 	 */
529 #include "Optimizer/pass10.c"
530 
531 #if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
532 	/* pass 11:
533 	 * - Compact literals table
534 	 */
535 	if (ZEND_OPTIMIZER_PASS_11 & OPTIMIZATION_LEVEL) {
536 		optimizer_compact_literals(op_array TSRMLS_CC);
537 	}
538 #endif
539 }
540 
zend_accel_optimize(zend_op_array * op_array,zend_persistent_script * script,HashTable ** constants TSRMLS_DC)541 static void zend_accel_optimize(zend_op_array           *op_array,
542                                 zend_persistent_script  *script,
543                                 HashTable              **constants TSRMLS_DC)
544 {
545 	zend_op *opline, *end;
546 
547 	/* Revert pass_two() */
548 	opline = op_array->opcodes;
549 	end = opline + op_array->last;
550 	while (opline < end) {
551 #if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
552 		if (opline->op1_type == IS_CONST) {
553 			opline->op1.constant = opline->op1.literal - op_array->literals;
554 		}
555 		if (opline->op2_type == IS_CONST) {
556 			opline->op2.constant = opline->op2.literal - op_array->literals;
557 		}
558 #endif
559 		switch (opline->opcode) {
560 			case ZEND_JMP:
561 #if ZEND_EXTENSION_API_NO > PHP_5_2_X_API_NO
562 			case ZEND_GOTO:
563 #endif
564 #if ZEND_EXTENSION_API_NO > PHP_5_4_X_API_NO
565 			case ZEND_FAST_CALL:
566 #endif
567 				ZEND_OP1(opline).opline_num = ZEND_OP1(opline).jmp_addr - op_array->opcodes;
568 				break;
569 			case ZEND_JMPZ:
570 			case ZEND_JMPNZ:
571 			case ZEND_JMPZ_EX:
572 			case ZEND_JMPNZ_EX:
573 #if ZEND_EXTENSION_API_NO > PHP_5_2_X_API_NO
574 			case ZEND_JMP_SET:
575 #endif
576 #if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
577 			case ZEND_JMP_SET_VAR:
578 #endif
579 				ZEND_OP2(opline).opline_num = ZEND_OP2(opline).jmp_addr - op_array->opcodes;
580 				break;
581 		}
582 		opline++;
583 	}
584 
585 	/* Do actual optimizations */
586 	zend_optimize(op_array, script, constants TSRMLS_CC);
587 
588 	/* Redo pass_two() */
589 	opline = op_array->opcodes;
590 	end = opline + op_array->last;
591 	while (opline < end) {
592 #if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
593 		if (opline->op1_type == IS_CONST) {
594 			opline->op1.zv = &op_array->literals[opline->op1.constant].constant;
595 		}
596 		if (opline->op2_type == IS_CONST) {
597 			opline->op2.zv = &op_array->literals[opline->op2.constant].constant;
598 		}
599 #endif
600 		switch (opline->opcode) {
601 			case ZEND_JMP:
602 #if ZEND_EXTENSION_API_NO > PHP_5_2_X_API_NO
603 			case ZEND_GOTO:
604 #endif
605 #if ZEND_EXTENSION_API_NO > PHP_5_4_X_API_NO
606 			case ZEND_FAST_CALL:
607 #endif
608 				ZEND_OP1(opline).jmp_addr = &op_array->opcodes[ZEND_OP1(opline).opline_num];
609 				break;
610 			case ZEND_JMPZ:
611 			case ZEND_JMPNZ:
612 			case ZEND_JMPZ_EX:
613 			case ZEND_JMPNZ_EX:
614 #if ZEND_EXTENSION_API_NO > PHP_5_2_X_API_NO
615 			case ZEND_JMP_SET:
616 #endif
617 #if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
618 			case ZEND_JMP_SET_VAR:
619 #endif
620 				ZEND_OP2(opline).jmp_addr = &op_array->opcodes[ZEND_OP2(opline).opline_num];
621 				break;
622 		}
623 		ZEND_VM_SET_OPCODE_HANDLER(opline);
624 		opline++;
625 	}
626 }
627 
zend_accel_script_optimize(zend_persistent_script * script TSRMLS_DC)628 int zend_accel_script_optimize(zend_persistent_script *script TSRMLS_DC)
629 {
630 	Bucket *p, *q;
631 	HashTable *constants = NULL;
632 
633 	zend_accel_optimize(&script->main_op_array, script, &constants TSRMLS_CC);
634 
635 	p = script->function_table.pListHead;
636 	while (p) {
637 		zend_op_array *op_array = (zend_op_array*)p->pData;
638 		zend_accel_optimize(op_array, script, &constants TSRMLS_CC);
639 		p = p->pListNext;
640 	}
641 
642 	p = script->class_table.pListHead;
643 	while (p) {
644 		zend_class_entry *ce = (zend_class_entry*)p->pDataPtr;
645 		q = ce->function_table.pListHead;
646 		while (q) {
647 			zend_op_array *op_array = (zend_op_array*)q->pData;
648 			if (op_array->scope == ce) {
649 				zend_accel_optimize(op_array, script, &constants TSRMLS_CC);
650 			} else if (op_array->type == ZEND_USER_FUNCTION) {
651 				zend_op_array *orig_op_array;
652 				if (zend_hash_find(&op_array->scope->function_table, q->arKey, q->nKeyLength, (void**)&orig_op_array) == SUCCESS) {
653 					HashTable *ht = op_array->static_variables;
654 					*op_array = *orig_op_array;
655 					op_array->static_variables = ht;
656 				}
657 			}
658 			q = q->pListNext;
659 		}
660 		p = p->pListNext;
661 	}
662 
663 	if (constants) {
664 		zend_hash_destroy(constants);
665 		efree(constants);
666 	}
667 
668 	return 1;
669 }
670