1 #if ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO
2 
3 /* ops that use CLs:
4 op1:
5 ZEND_FETCH_CONSTANT:
6 ZEND_INIT_CTOR_CALL:
7 ZEND_INIT_STATIC_METHOD_CALL:
8 ZEND_INIT_METHOD_CALL:
9 ZEND_IMPORT_CLASS:
10 ZEND_IMPORT_FUNCTION:
11 ZEND_IMPORT_CONST:
12 ZEND_ADD_INTERFACE:
13 ZEND_VERIFY_ABSTRACT_CLASS:
14 ZEND_NEW:
15 ZEND_CATCH:
16 ZEND_INIT_FCALL_BY_NAME:
17 
18 op2:
19 ZEND_UNSET_VAR:
20 ZEND_ISSET_ISEMPTY_VAR:
21 ZEND_FETCH_UNSET:
22 ZEND_FETCH_IS:
23 ZEND_FETCH_R:
24 ZEND_FETCH_W:
25 ZEND_FETCH_RW:
26 ZEND_FETCH_FUNC_ARG:
27 ZEND_ADD_INTERFACE:
28 ZEND_INSTANCEOF:
29 
30 extended_value:
31 ZEND_DECLARE_INHERITED_CLASS:
32 
33 ignore result
34 INIT_METHOD_CALL:
35 */
36 
37 #define OP1_CONST_IS_CLASS 1
38 #define OP2_CONST_IS_CLASS 2
39 #define EXT_CONST_IS_CLASS 4
40 #define RESULT_IS_UNUSED   8
41 
42 static const char op_const_means_class[256]  = {
43 	/* 0 */
44 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
45 	/* 32 */
46 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0,
47 	/* 64 */
48 	0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 2, 0, 0, 2, 0, 0, 2, 0, 0, 2, 0, 0, 2, 0, 0, 2,
49 	/* 96 */
50 	0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 9, 1, 2, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
51 	/* 128 */
52 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 4, 0, 0, 0, 3, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
53 	/* 160 */
54 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
55 	/* 192 */
56 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
57 	/* 224 */
58 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
59 };
60 #endif
61 
62 #define GET_AVAILABLE_T()		\
63 	for (i = 0; i < T; i++) {	\
64 		if (!taken_T[i]) {		\
65 			break;				\
66 		}						\
67 	}							\
68 	taken_T[i] = 1;				\
69 	if (i > max) {				\
70 		max = i;				\
71 	}
72 
optimize_temporary_variables(zend_op_array * op_array)73 static void optimize_temporary_variables(zend_op_array *op_array)
74 {
75 	int T = op_array->T;
76 	char *taken_T;			/* T index in use */
77 	zend_op **start_of_T;	/* opline where T is first used */
78 	char *valid_T;			/* Is the map_T valid */
79 	int *map_T;				/* Map's the T to its new index */
80 	zend_op *opline, *end;
81 	int currT;
82 	int i;
83 	int max = -1;
84 	int var_to_free = -1;
85 
86 	taken_T = (char *) emalloc(T);
87 	start_of_T = (zend_op **) emalloc(T * sizeof(zend_op *));
88 	valid_T = (char *) emalloc(T);
89 	map_T = (int *) emalloc(T * sizeof(int));
90 
91     end = op_array->opcodes;
92     opline = &op_array->opcodes[op_array->last - 1];
93 
94     /* Find T definition points */
95     while (opline >= end) {
96 #if ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO
97         if (ZEND_RESULT_TYPE(opline) & (IS_VAR | IS_TMP_VAR | IS_CONST)) {
98 			if (!(op_const_means_class[opline->opcode] & RESULT_IS_UNUSED)) {
99 				start_of_T[VAR_NUM(ZEND_RESULT(opline).var)] = opline;
100 			}
101 		}
102 #else
103         if (ZEND_RESULT_TYPE(opline) & (IS_VAR | IS_TMP_VAR)) {
104 			start_of_T[VAR_NUM(ZEND_RESULT(opline).var)] = opline;
105 		}
106 #endif
107 		opline--;
108 	}
109 
110 	memset(valid_T, 0, T);
111 	memset(taken_T, 0, T);
112 
113     end = op_array->opcodes;
114     opline = &op_array->opcodes[op_array->last - 1];
115 
116     while (opline >= end) {
117 		if ((ZEND_OP1_TYPE(opline) & (IS_VAR | IS_TMP_VAR))
118 #if ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO
119 			|| ((op_const_means_class[opline->opcode] & OP1_CONST_IS_CLASS) && ZEND_OP1_TYPE(opline) == IS_CONST)
120 #endif
121 			) {
122 			currT = VAR_NUM(ZEND_OP1(opline).var);
123 			if (!valid_T[currT]) {
124 				GET_AVAILABLE_T();
125 				map_T[currT] = i;
126 				valid_T[currT] = 1;
127 			}
128 			ZEND_OP1(opline).var = NUM_VAR(map_T[currT]);
129 		}
130 
131 		/* Skip OP_DATA */
132 		if (opline->opcode == ZEND_OP_DATA &&
133 		    (opline-1)->opcode == ZEND_ASSIGN_DIM) {
134 		    opline--;
135 		    continue;
136 		}
137 
138 		if ((ZEND_OP2_TYPE(opline) & (IS_VAR | IS_TMP_VAR))
139 #if ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO
140 			|| ((op_const_means_class[opline->opcode] & OP2_CONST_IS_CLASS) && ZEND_OP2_TYPE(opline) == IS_CONST)
141 #endif
142 		   ) {
143 			currT = VAR_NUM(ZEND_OP2(opline).var);
144 			if (!valid_T[currT]) {
145 				GET_AVAILABLE_T();
146 				map_T[currT] = i;
147 				valid_T[currT] = 1;
148 			}
149 			ZEND_OP2(opline).var = NUM_VAR(map_T[currT]);
150 		}
151 
152 #if ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO
153 		if ((op_const_means_class[opline->opcode] & EXT_CONST_IS_CLASS)) {
154 #else
155 		if (opline->opcode == ZEND_DECLARE_INHERITED_CLASS ||
156             opline->opcode == ZEND_DECLARE_INHERITED_CLASS_DELAYED) {
157 #endif
158 			currT = VAR_NUM(opline->extended_value);
159 			if (!valid_T[currT]) {
160 				GET_AVAILABLE_T();
161 				map_T[currT] = i;
162 				valid_T[currT] = 1;
163 			}
164 			opline->extended_value = NUM_VAR(map_T[currT]);
165 		}
166 
167 		/* Allocate OP_DATA->op2 after "operands", but before "result" */
168 		if (opline->opcode == ZEND_ASSIGN_DIM &&
169 		    (opline + 1)->opcode == ZEND_OP_DATA &&
170 		    ZEND_OP2_TYPE(opline + 1) & (IS_VAR | IS_TMP_VAR)) {
171 			currT = VAR_NUM(ZEND_OP2(opline + 1).var);
172 			GET_AVAILABLE_T();
173 			map_T[currT] = i;
174 			valid_T[currT] = 1;
175 			taken_T[i] = 0;
176 			ZEND_OP2(opline + 1).var = NUM_VAR(i);
177 			var_to_free = i;
178 		}
179 
180 #if ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO
181 		if (ZEND_RESULT_TYPE(opline) & (IS_VAR | IS_TMP_VAR | IS_CONST)) {
182 			if (!(op_const_means_class[opline->opcode] & RESULT_IS_UNUSED)) {
183 #else
184 		if (ZEND_RESULT_TYPE(opline) & (IS_VAR | IS_TMP_VAR)) {
185 #endif
186 				currT = VAR_NUM(ZEND_RESULT(opline).var);
187 				if (valid_T[currT]) {
188 					if (start_of_T[currT] == opline) {
189 						taken_T[map_T[currT]] = 0;
190 					}
191 					ZEND_RESULT(opline).var = NUM_VAR(map_T[currT]);
192 				} else { /* Au still needs to be assigned a T which is a bit dumb. Should consider changing Zend */
193 					GET_AVAILABLE_T();
194 
195 					if (RESULT_UNUSED(opline)) {
196 						taken_T[i] = 0;
197 					} else {
198 						/* Code which gets here is using a wrongly built opcode such as RECV() */
199 						map_T[currT] = i;
200 						valid_T[currT] = 1;
201 					}
202 					ZEND_RESULT(opline).var = NUM_VAR(i);
203 				}
204 #if ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO
205 			}
206 #endif
207 		}
208 
209 		if (var_to_free >= 0) {
210 			taken_T[var_to_free] = 0;
211 			var_to_free = -1;
212 		}
213 
214 		opline--;
215 	}
216 
217 	efree(taken_T);
218 	efree(start_of_T);
219 	efree(valid_T);
220 	efree(map_T);
221 	op_array->T = max + 1;
222 }
223