1 /*
2 +----------------------------------------------------------------------+
3 | Zend OPcache |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 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 | https://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@php.net> |
16 | Zeev Suraski <zeev@php.net> |
17 | Stanislav Malyshev <stas@zend.com> |
18 | Dmitry Stogov <dmitry@php.net> |
19 +----------------------------------------------------------------------+
20 */
21
22 #include "Optimizer/zend_optimizer.h"
23 #include "Optimizer/zend_optimizer_internal.h"
24 #include "zend_API.h"
25 #include "zend_constants.h"
26 #include "zend_execute.h"
27 #include "zend_vm.h"
28 #include "zend_bitset.h"
29
30 #define GET_AVAILABLE_T() \
31 for (i = 0; i < T; i++) { \
32 if (!zend_bitset_in(taken_T, i)) { \
33 break; \
34 } \
35 } \
36 zend_bitset_incl(taken_T, i); \
37 if (i > max) { \
38 max = i; \
39 }
40
zend_optimize_temporary_variables(zend_op_array * op_array,zend_optimizer_ctx * ctx)41 void zend_optimize_temporary_variables(zend_op_array *op_array, zend_optimizer_ctx *ctx)
42 {
43 int T = op_array->T;
44 int offset = op_array->last_var;
45 uint32_t bitset_len;
46 zend_bitset taken_T; /* T index in use */
47 zend_op **start_of_T; /* opline where T is first used */
48 zend_bitset valid_T; /* Is the map_T valid */
49 int *map_T; /* Map's the T to its new index */
50 zend_op *opline, *end;
51 int currT;
52 int i;
53 int max = -1;
54 void *checkpoint = zend_arena_checkpoint(ctx->arena);
55
56 bitset_len = zend_bitset_len(T);
57 taken_T = (zend_bitset) zend_arena_alloc(&ctx->arena, bitset_len * ZEND_BITSET_ELM_SIZE);
58 start_of_T = (zend_op **) zend_arena_alloc(&ctx->arena, T * sizeof(zend_op *));
59 valid_T = (zend_bitset) zend_arena_alloc(&ctx->arena, bitset_len * ZEND_BITSET_ELM_SIZE);
60 map_T = (int *) zend_arena_alloc(&ctx->arena, T * sizeof(int));
61
62 end = op_array->opcodes;
63 opline = &op_array->opcodes[op_array->last - 1];
64
65 /* Find T definition points */
66 while (opline >= end) {
67 if (opline->result_type & (IS_VAR | IS_TMP_VAR)) {
68 start_of_T[VAR_NUM(opline->result.var) - offset] = opline;
69 }
70 opline--;
71 }
72
73 zend_bitset_clear(valid_T, bitset_len);
74 zend_bitset_clear(taken_T, bitset_len);
75
76 end = op_array->opcodes;
77 opline = &op_array->opcodes[op_array->last - 1];
78
79 while (opline >= end) {
80 if ((opline->op1_type & (IS_VAR | IS_TMP_VAR))) {
81 currT = VAR_NUM(opline->op1.var) - offset;
82 if (opline->opcode == ZEND_ROPE_END) {
83 int num = (((opline->extended_value + 1) * sizeof(zend_string*)) + (sizeof(zval) - 1)) / sizeof(zval);
84 int var;
85
86 var = max;
87 while (var >= 0 && !zend_bitset_in(taken_T, var)) {
88 var--;
89 }
90 max = MAX(max, var + num);
91 var = var + 1;
92 map_T[currT] = var;
93 zend_bitset_incl(valid_T, currT);
94 zend_bitset_incl(taken_T, var);
95 opline->op1.var = NUM_VAR(var + offset);
96 while (num > 1) {
97 num--;
98 zend_bitset_incl(taken_T, var + num);
99 }
100 } else {
101 if (!zend_bitset_in(valid_T, currT)) {
102 int use_new_var = 0;
103
104 /* Code in "finally" blocks may modify temporary variables.
105 * We allocate new temporaries for values that need to
106 * relive FAST_CALLs.
107 */
108 if ((op_array->fn_flags & ZEND_ACC_HAS_FINALLY_BLOCK) &&
109 (opline->opcode == ZEND_RETURN ||
110 opline->opcode == ZEND_GENERATOR_RETURN ||
111 opline->opcode == ZEND_RETURN_BY_REF ||
112 opline->opcode == ZEND_FREE ||
113 opline->opcode == ZEND_FE_FREE)) {
114 zend_op *curr = opline;
115
116 while (--curr >= end) {
117 if (curr->opcode == ZEND_FAST_CALL) {
118 use_new_var = 1;
119 break;
120 } else if (curr->opcode != ZEND_FREE &&
121 curr->opcode != ZEND_FE_FREE &&
122 curr->opcode != ZEND_VERIFY_RETURN_TYPE &&
123 curr->opcode != ZEND_DISCARD_EXCEPTION) {
124 break;
125 }
126 }
127 }
128 if (use_new_var) {
129 i = ++max;
130 zend_bitset_incl(taken_T, i);
131 } else {
132 GET_AVAILABLE_T();
133 }
134 map_T[currT] = i;
135 zend_bitset_incl(valid_T, currT);
136 }
137 opline->op1.var = NUM_VAR(map_T[currT] + offset);
138 }
139 }
140
141 if ((opline->op2_type & (IS_VAR | IS_TMP_VAR))) {
142 currT = VAR_NUM(opline->op2.var) - offset;
143 if (!zend_bitset_in(valid_T, currT)) {
144 GET_AVAILABLE_T();
145 map_T[currT] = i;
146 zend_bitset_incl(valid_T, currT);
147 }
148 opline->op2.var = NUM_VAR(map_T[currT] + offset);
149 }
150
151 if (opline->result_type & (IS_VAR | IS_TMP_VAR)) {
152 currT = VAR_NUM(opline->result.var) - offset;
153 if (zend_bitset_in(valid_T, currT)) {
154 if (start_of_T[currT] == opline) {
155 /* ZEND_FAST_CALL can not share temporary var with others
156 * since the fast_var could also be set by ZEND_HANDLE_EXCEPTION
157 * which could be ahead of it */
158 if (opline->opcode != ZEND_FAST_CALL) {
159 zend_bitset_excl(taken_T, map_T[currT]);
160 }
161 }
162 opline->result.var = NUM_VAR(map_T[currT] + offset);
163 if (opline->opcode == ZEND_ROPE_INIT) {
164 if (start_of_T[currT] == opline) {
165 uint32_t num = ((opline->extended_value * sizeof(zend_string*)) + (sizeof(zval) - 1)) / sizeof(zval);
166 while (num > 1) {
167 num--;
168 zend_bitset_excl(taken_T, map_T[currT]+num);
169 }
170 }
171 }
172 } else {
173 /* Code which gets here is using a wrongly built opcode such as RECV() */
174 GET_AVAILABLE_T();
175 map_T[currT] = i;
176 zend_bitset_incl(valid_T, currT);
177 opline->result.var = NUM_VAR(i + offset);
178 }
179 }
180
181 opline--;
182 }
183
184 zend_arena_release(&ctx->arena, checkpoint);
185 op_array->T = max + 1;
186 }
187