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