1/*
2   +----------------------------------------------------------------------+
3   | Zend OPcache                                                         |
4   +----------------------------------------------------------------------+
5   | Copyright (c) 1998-2016 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
42void zend_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_RETURN_BY_REF ||
113					     opline->opcode == ZEND_FREE ||
114					     opline->opcode == ZEND_FE_FREE)) {
115						zend_op *curr = opline;
116
117						while (--curr >= end) {
118							if (curr->opcode == ZEND_FAST_CALL) {
119								use_new_var = 1;
120								break;
121							} else if (curr->opcode != ZEND_FREE &&
122							           curr->opcode != ZEND_FE_FREE &&
123							           curr->opcode != ZEND_VERIFY_RETURN_TYPE &&
124							           curr->opcode != ZEND_DISCARD_EXCEPTION) {
125								break;
126							}
127						}
128					}
129					if (use_new_var) {
130						i = ++max;
131						zend_bitset_incl(taken_T, i);
132					} else {
133						GET_AVAILABLE_T();
134					}
135					map_T[currT] = i;
136					zend_bitset_incl(valid_T, currT);
137				}
138				ZEND_OP1(opline).var = NUM_VAR(map_T[currT] + offset);
139			}
140		}
141
142		if ((ZEND_OP2_TYPE(opline) & (IS_VAR | IS_TMP_VAR))) {
143			currT = VAR_NUM(ZEND_OP2(opline).var) - offset;
144			if (!zend_bitset_in(valid_T, currT)) {
145				GET_AVAILABLE_T();
146				map_T[currT] = i;
147				zend_bitset_incl(valid_T, currT);
148			}
149			ZEND_OP2(opline).var = NUM_VAR(map_T[currT] + offset);
150		}
151
152		if (ZEND_RESULT_TYPE(opline) & (IS_VAR | IS_TMP_VAR)) {
153			currT = VAR_NUM(ZEND_RESULT(opline).var) - offset;
154			if (zend_bitset_in(valid_T, currT)) {
155				if (start_of_T[currT] == opline) {
156					/* ZEND_FAST_CALL can not share temporary var with others
157					 * since the fast_var could also be set by ZEND_HANDLE_EXCEPTION
158					 * which could be ahead of it */
159					if (opline->opcode != ZEND_FAST_CALL) {
160						zend_bitset_excl(taken_T, map_T[currT]);
161					}
162				}
163				ZEND_RESULT(opline).var = NUM_VAR(map_T[currT] + offset);
164				if (opline->opcode == ZEND_ROPE_INIT) {
165					if (start_of_T[currT] == opline) {
166						uint32_t num = ((opline->extended_value * sizeof(zend_string*)) + (sizeof(zval) - 1)) / sizeof(zval);
167						while (num > 1) {
168							num--;
169							zend_bitset_excl(taken_T, map_T[currT]+num);
170						}
171					}
172				}
173			} else {
174				/* Code which gets here is using a wrongly built opcode such as RECV() */
175				GET_AVAILABLE_T();
176				map_T[currT] = i;
177				zend_bitset_incl(valid_T, currT);
178				ZEND_RESULT(opline).var = NUM_VAR(i + offset);
179			}
180		}
181
182		if (var_to_free >= 0) {
183			zend_bitset_excl(taken_T, var_to_free);
184			var_to_free = -1;
185		}
186
187		opline--;
188	}
189
190	if (op_array->live_range) {
191		for (i = 0; i < op_array->last_live_range; i++) {
192			op_array->live_range[i].var =
193				NUM_VAR(map_T[VAR_NUM(op_array->live_range[i].var & ~ZEND_LIVE_MASK) - offset] + offset) |
194				(op_array->live_range[i].var & ZEND_LIVE_MASK);
195		}
196	}
197
198	zend_arena_release(&ctx->arena, checkpoint);
199	op_array->T = max + 1;
200}
201