1/*
2   +----------------------------------------------------------------------+
3   | Zend OPcache                                                         |
4   +----------------------------------------------------------------------+
5   | Copyright (c) 1998-2015 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 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        /* Skip OP_DATA */
143        if (opline->opcode == ZEND_OP_DATA &&
144            (opline-1)->opcode == ZEND_ASSIGN_DIM) {
145            opline--;
146            continue;
147        }
148
149        if ((ZEND_OP2_TYPE(opline) & (IS_VAR | IS_TMP_VAR))) {
150            currT = VAR_NUM(ZEND_OP2(opline).var) - offset;
151            if (!zend_bitset_in(valid_T, currT)) {
152                GET_AVAILABLE_T();
153                map_T[currT] = i;
154                zend_bitset_incl(valid_T, currT);
155            }
156            ZEND_OP2(opline).var = NUM_VAR(map_T[currT] + offset);
157        }
158
159        if (opline->opcode == ZEND_DECLARE_INHERITED_CLASS ||
160            opline->opcode == ZEND_DECLARE_ANON_INHERITED_CLASS ||
161            opline->opcode == ZEND_DECLARE_INHERITED_CLASS_DELAYED) {
162            currT = VAR_NUM(opline->extended_value) - offset;
163            if (!zend_bitset_in(valid_T, currT)) {
164                GET_AVAILABLE_T();
165                map_T[currT] = i;
166                zend_bitset_incl(valid_T, currT);
167            }
168            opline->extended_value = NUM_VAR(map_T[currT] + offset);
169        }
170
171        /* Allocate OP_DATA->op2 after "operands", but before "result" */
172        if (opline->opcode == ZEND_ASSIGN_DIM &&
173            (opline + 1)->opcode == ZEND_OP_DATA &&
174            ZEND_OP2_TYPE(opline + 1) & (IS_VAR | IS_TMP_VAR)) {
175            currT = VAR_NUM(ZEND_OP2(opline + 1).var) - offset;
176            GET_AVAILABLE_T();
177            map_T[currT] = i;
178            zend_bitset_incl(valid_T, currT);
179            zend_bitset_excl(taken_T, i);
180            ZEND_OP2(opline + 1).var = NUM_VAR(i + offset);
181            var_to_free = i;
182        }
183
184        if (ZEND_RESULT_TYPE(opline) & (IS_VAR | IS_TMP_VAR)) {
185            currT = VAR_NUM(ZEND_RESULT(opline).var) - offset;
186            if (zend_bitset_in(valid_T, currT)) {
187                if (start_of_T[currT] == opline) {
188                    /* ZEND_FAST_CALL can not share temporary var with others
189                     * since the fast_var could also be set by ZEND_HANDLE_EXCEPTION
190                     * which could be ahead of it */
191                    if (opline->opcode != ZEND_FAST_CALL) {
192                        zend_bitset_excl(taken_T, map_T[currT]);
193                    }
194                }
195                ZEND_RESULT(opline).var = NUM_VAR(map_T[currT] + offset);
196                if (opline->opcode == ZEND_ROPE_INIT) {
197                    if (start_of_T[currT] == opline) {
198                        uint32_t num = ((opline->extended_value * sizeof(zend_string*)) + (sizeof(zval) - 1)) / sizeof(zval);
199                        while (num > 1) {
200                            num--;
201                            zend_bitset_excl(taken_T, map_T[currT]+num);
202                        }
203                    }
204                }
205            } else { /* Au still needs to be assigned a T which is a bit dumb. Should consider changing Zend */
206                GET_AVAILABLE_T();
207
208                if (RESULT_UNUSED(opline)) {
209                    zend_bitset_excl(taken_T, i);
210                } else {
211                    /* Code which gets here is using a wrongly built opcode such as RECV() */
212                    map_T[currT] = i;
213                    zend_bitset_incl(valid_T, currT);
214                }
215                ZEND_RESULT(opline).var = NUM_VAR(i + offset);
216            }
217        }
218
219        if (var_to_free >= 0) {
220            zend_bitset_excl(taken_T, var_to_free);
221            var_to_free = -1;
222        }
223
224        opline--;
225    }
226
227    zend_arena_release(&ctx->arena, checkpoint);
228    op_array->T = max + 1;
229}
230