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