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 #include "zend_bitset.h"
30 
31 #define GET_AVAILABLE_T()					\
32 	for (i = 0; i < T; i++) {				\
33 		if (!zend_bitset_in(taken_T, i)) {	\
34 			break;							\
35 		}									\
36 	}										\
37 	zend_bitset_incl(taken_T, i);			\
38 	if (i > max) {							\
39 		max = i;							\
40 	}
41 
optimize_temporary_variables(zend_op_array * op_array,zend_optimizer_ctx * ctx)42 void optimize_temporary_variables(zend_op_array *op_array, zend_optimizer_ctx *ctx)
43 {
44 	int T = op_array->T;
45 	int offset = op_array->last_var;
46 	uint32_t bitset_len;
47 	zend_bitset taken_T;	/* T index in use */
48 	zend_op **start_of_T;	/* opline where T is first used */
49 	zend_bitset valid_T;	/* Is the map_T valid */
50 	int *map_T;				/* Map's the T to its new index */
51 	zend_op *opline, *end;
52 	int currT;
53 	int i;
54 	int max = -1;
55 	int var_to_free = -1;
56 	void *checkpoint = zend_arena_checkpoint(ctx->arena);
57 
58 	bitset_len = zend_bitset_len(T);
59 	taken_T = (zend_bitset) zend_arena_alloc(&ctx->arena, bitset_len * ZEND_BITSET_ELM_SIZE);
60 	start_of_T = (zend_op **) zend_arena_alloc(&ctx->arena, T * sizeof(zend_op *));
61 	valid_T = (zend_bitset) zend_arena_alloc(&ctx->arena, bitset_len * ZEND_BITSET_ELM_SIZE);
62 	map_T = (int *) zend_arena_alloc(&ctx->arena, T * sizeof(int));
63 
64     end = op_array->opcodes;
65     opline = &op_array->opcodes[op_array->last - 1];
66 
67     /* Find T definition points */
68     while (opline >= end) {
69         if (ZEND_RESULT_TYPE(opline) & (IS_VAR | IS_TMP_VAR)) {
70 			start_of_T[VAR_NUM(ZEND_RESULT(opline).var) - offset] = opline;
71 		}
72 		opline--;
73 	}
74 
75 	zend_bitset_clear(valid_T, bitset_len);
76 	zend_bitset_clear(taken_T, bitset_len);
77 
78     end = op_array->opcodes;
79     opline = &op_array->opcodes[op_array->last - 1];
80 
81     while (opline >= end) {
82 		if ((ZEND_OP1_TYPE(opline) & (IS_VAR | IS_TMP_VAR))) {
83 			currT = VAR_NUM(ZEND_OP1(opline).var) - offset;
84 			if (opline->opcode == ZEND_ROPE_END) {
85 				int num = (((opline->extended_value + 1) * sizeof(zend_string*)) + (sizeof(zval) - 1)) / sizeof(zval);
86 				int var;
87 
88 				var = max;
89 				while (var >= 0 && !zend_bitset_in(taken_T, var)) {
90 					var--;
91 				}
92 				max = MAX(max, var + num);
93 				var = var + 1;
94 				map_T[currT] = var;
95 				zend_bitset_incl(valid_T, currT);
96 				zend_bitset_incl(taken_T, var);
97 				ZEND_OP1(opline).var = NUM_VAR(var + offset);
98 				while (num > 1) {
99 					num--;
100 					zend_bitset_incl(taken_T, var + num);
101 				}
102 			} else {
103 				if (!zend_bitset_in(valid_T, currT)) {
104 					int use_new_var = 0;
105 
106 					/* Code in "finally" blocks may modify temorary variables.
107 					 * We allocate new temporaries for values that need to
108 					 * relive FAST_CALLs.
109 					 */
110 					if ((op_array->fn_flags & ZEND_ACC_HAS_FINALLY_BLOCK) &&
111 					    (opline->opcode == ZEND_RETURN ||
112 					     opline->opcode == ZEND_GENERATOR_RETURN ||
113 					     opline->opcode == ZEND_RETURN_BY_REF ||
114 					     opline->opcode == ZEND_FREE ||
115 					     opline->opcode == ZEND_FE_FREE)) {
116 						zend_op *curr = opline;
117 
118 						while (--curr >= end) {
119 							if (curr->opcode == ZEND_FAST_CALL) {
120 								use_new_var = 1;
121 								break;
122 							} else if (curr->opcode != ZEND_FREE &&
123 							           curr->opcode != ZEND_FE_FREE &&
124 							           curr->opcode != ZEND_VERIFY_RETURN_TYPE &&
125 							           curr->opcode != ZEND_DISCARD_EXCEPTION) {
126 								break;
127 							}
128 						}
129 					}
130 					if (use_new_var) {
131 						i = ++max;
132 						zend_bitset_incl(taken_T, i);
133 					} else {
134 						GET_AVAILABLE_T();
135 					}
136 					map_T[currT] = i;
137 					zend_bitset_incl(valid_T, currT);
138 				}
139 				ZEND_OP1(opline).var = NUM_VAR(map_T[currT] + offset);
140 			}
141 		}
142 
143 		/* Skip OP_DATA */
144 		if (opline->opcode == ZEND_OP_DATA &&
145 		    (opline-1)->opcode == ZEND_ASSIGN_DIM) {
146 		    opline--;
147 		    continue;
148 		}
149 
150 		if ((ZEND_OP2_TYPE(opline) & (IS_VAR | IS_TMP_VAR))) {
151 			currT = VAR_NUM(ZEND_OP2(opline).var) - offset;
152 			if (!zend_bitset_in(valid_T, currT)) {
153 				GET_AVAILABLE_T();
154 				map_T[currT] = i;
155 				zend_bitset_incl(valid_T, currT);
156 			}
157 			ZEND_OP2(opline).var = NUM_VAR(map_T[currT] + offset);
158 		}
159 
160 		if (opline->opcode == ZEND_DECLARE_INHERITED_CLASS ||
161 		    opline->opcode == ZEND_DECLARE_ANON_INHERITED_CLASS ||
162             opline->opcode == ZEND_DECLARE_INHERITED_CLASS_DELAYED) {
163 			currT = VAR_NUM(opline->extended_value) - offset;
164 			if (!zend_bitset_in(valid_T, currT)) {
165 				GET_AVAILABLE_T();
166 				map_T[currT] = i;
167 				zend_bitset_incl(valid_T, currT);
168 			}
169 			opline->extended_value = NUM_VAR(map_T[currT] + offset);
170 		}
171 
172 		/* Allocate OP_DATA->op2 after "operands", but before "result" */
173 		if (opline->opcode == ZEND_ASSIGN_DIM &&
174 		    (opline + 1)->opcode == ZEND_OP_DATA &&
175 		    ZEND_OP2_TYPE(opline + 1) & (IS_VAR | IS_TMP_VAR)) {
176 			currT = VAR_NUM(ZEND_OP2(opline + 1).var) - offset;
177 			GET_AVAILABLE_T();
178 			map_T[currT] = i;
179 			zend_bitset_incl(valid_T, currT);
180 			zend_bitset_excl(taken_T, i);
181 			ZEND_OP2(opline + 1).var = NUM_VAR(i + offset);
182 			var_to_free = i;
183 		}
184 
185 		if (ZEND_RESULT_TYPE(opline) & (IS_VAR | IS_TMP_VAR)) {
186 			currT = VAR_NUM(ZEND_RESULT(opline).var) - offset;
187 			if (zend_bitset_in(valid_T, currT)) {
188 				if (start_of_T[currT] == opline) {
189 					/* ZEND_FAST_CALL can not share temporary var with others
190 					 * since the fast_var could also be set by ZEND_HANDLE_EXCEPTION
191 					 * which could be ahead of it */
192 					if (opline->opcode != ZEND_FAST_CALL) {
193 						zend_bitset_excl(taken_T, map_T[currT]);
194 					}
195 				}
196 				ZEND_RESULT(opline).var = NUM_VAR(map_T[currT] + offset);
197 				if (opline->opcode == ZEND_ROPE_INIT) {
198 					if (start_of_T[currT] == opline) {
199 						uint32_t num = ((opline->extended_value * sizeof(zend_string*)) + (sizeof(zval) - 1)) / sizeof(zval);
200 						while (num > 1) {
201 							num--;
202 							zend_bitset_excl(taken_T, map_T[currT]+num);
203 						}
204 					}
205 				}
206 			} else { /* Au still needs to be assigned a T which is a bit dumb. Should consider changing Zend */
207 				GET_AVAILABLE_T();
208 
209 				if (RESULT_UNUSED(opline)) {
210 					zend_bitset_excl(taken_T, i);
211 				} else {
212 					/* Code which gets here is using a wrongly built opcode such as RECV() */
213 					map_T[currT] = i;
214 					zend_bitset_incl(valid_T, currT);
215 				}
216 				ZEND_RESULT(opline).var = NUM_VAR(i + offset);
217 			}
218 		}
219 
220 		if (var_to_free >= 0) {
221 			zend_bitset_excl(taken_T, var_to_free);
222 			var_to_free = -1;
223 		}
224 
225 		opline--;
226 	}
227 
228 	zend_arena_release(&ctx->arena, checkpoint);
229 	op_array->T = max + 1;
230 }
231