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
30#define GET_AVAILABLE_T()       \
31    for (i = 0; i < T; i++) {   \
32        if (!taken_T[i]) {      \
33            break;              \
34        }                       \
35    }                           \
36    taken_T[i] = 1;             \
37    if (i > max) {              \
38        max = i;                \
39    }
40
41void 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    char *taken_T;          /* T index in use */
46    zend_op **start_of_T;   /* opline where T is first used */
47    char *valid_T;          /* Is the map_T valid */
48    int *map_T;             /* Map's the T to its new index */
49    zend_op *opline, *end;
50    int currT;
51    int i;
52    int max = -1;
53    int var_to_free = -1;
54    void *checkpoint = zend_arena_checkpoint(ctx->arena);
55
56    taken_T = (char *) zend_arena_alloc(&ctx->arena, T);
57    start_of_T = (zend_op **) zend_arena_alloc(&ctx->arena, T * sizeof(zend_op *));
58    valid_T = (char *) zend_arena_alloc(&ctx->arena, T);
59    map_T = (int *) zend_arena_alloc(&ctx->arena, T * sizeof(int));
60
61    end = op_array->opcodes;
62    opline = &op_array->opcodes[op_array->last - 1];
63
64    /* Find T definition points */
65    while (opline >= end) {
66        if (ZEND_RESULT_TYPE(opline) & (IS_VAR | IS_TMP_VAR)) {
67            start_of_T[VAR_NUM(ZEND_RESULT(opline).var) - offset] = opline;
68        }
69        opline--;
70    }
71
72    memset(valid_T, 0, T);
73    memset(taken_T, 0, T);
74
75    end = op_array->opcodes;
76    opline = &op_array->opcodes[op_array->last - 1];
77
78    while (opline >= end) {
79        if ((ZEND_OP1_TYPE(opline) & (IS_VAR | IS_TMP_VAR))) {
80            currT = VAR_NUM(ZEND_OP1(opline).var) - offset;
81            if (opline->opcode == ZEND_ROPE_END) {
82                int num = (((opline->extended_value + 1) * sizeof(zend_string*)) + (sizeof(zval) - 1)) / sizeof(zval);
83                int var;
84
85                var = max;
86                while (var >= 0 && !taken_T[var]) {
87                    var--;
88                }
89                max = MAX(max, var + num);
90                var = var + 1;
91                map_T[currT] = var;
92                valid_T[currT] = 1;
93                taken_T[var] = 1;
94                ZEND_OP1(opline).var = NUM_VAR(var + offset);
95                while (num > 1) {
96                    num--;
97                    taken_T[var + num] = 1;
98                }
99            } else {
100                if (!valid_T[currT]) {
101                    GET_AVAILABLE_T();
102                    map_T[currT] = i;
103                    valid_T[currT] = 1;
104                }
105                ZEND_OP1(opline).var = NUM_VAR(map_T[currT] + offset);
106            }
107        }
108
109        /* Skip OP_DATA */
110        if (opline->opcode == ZEND_OP_DATA &&
111            (opline-1)->opcode == ZEND_ASSIGN_DIM) {
112            opline--;
113            continue;
114        }
115
116        if ((ZEND_OP2_TYPE(opline) & (IS_VAR | IS_TMP_VAR))) {
117            currT = VAR_NUM(ZEND_OP2(opline).var) - offset;
118            if (!valid_T[currT]) {
119                GET_AVAILABLE_T();
120                map_T[currT] = i;
121                valid_T[currT] = 1;
122            }
123            ZEND_OP2(opline).var = NUM_VAR(map_T[currT] + offset);
124        }
125
126        if (opline->opcode == ZEND_DECLARE_INHERITED_CLASS ||
127            opline->opcode == ZEND_DECLARE_ANON_INHERITED_CLASS ||
128            opline->opcode == ZEND_DECLARE_INHERITED_CLASS_DELAYED) {
129            currT = VAR_NUM(opline->extended_value) - offset;
130            if (!valid_T[currT]) {
131                GET_AVAILABLE_T();
132                map_T[currT] = i;
133                valid_T[currT] = 1;
134            }
135            opline->extended_value = NUM_VAR(map_T[currT] + offset);
136        }
137
138        /* Allocate OP_DATA->op2 after "operands", but before "result" */
139        if (opline->opcode == ZEND_ASSIGN_DIM &&
140            (opline + 1)->opcode == ZEND_OP_DATA &&
141            ZEND_OP2_TYPE(opline + 1) & (IS_VAR | IS_TMP_VAR)) {
142            currT = VAR_NUM(ZEND_OP2(opline + 1).var) - offset;
143            GET_AVAILABLE_T();
144            map_T[currT] = i;
145            valid_T[currT] = 1;
146            taken_T[i] = 0;
147            ZEND_OP2(opline + 1).var = NUM_VAR(i + offset);
148            var_to_free = i;
149        }
150
151        if (ZEND_RESULT_TYPE(opline) & (IS_VAR | IS_TMP_VAR)) {
152            currT = VAR_NUM(ZEND_RESULT(opline).var) - offset;
153            if (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                        taken_T[map_T[currT]] = 0;
160                    }
161                }
162                ZEND_RESULT(opline).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                            taken_T[map_T[currT]+num] = 0;
169                        }
170                    }
171                }
172            } else { /* Au still needs to be assigned a T which is a bit dumb. Should consider changing Zend */
173                GET_AVAILABLE_T();
174
175                if (RESULT_UNUSED(opline)) {
176                    taken_T[i] = 0;
177                } else {
178                    /* Code which gets here is using a wrongly built opcode such as RECV() */
179                    map_T[currT] = i;
180                    valid_T[currT] = 1;
181                }
182                ZEND_RESULT(opline).var = NUM_VAR(i + offset);
183            }
184        }
185
186        if (var_to_free >= 0) {
187            taken_T[var_to_free] = 0;
188            var_to_free = -1;
189        }
190
191        opline--;
192    }
193
194    zend_arena_release(&ctx->arena, checkpoint);
195    op_array->T = max + 1;
196}
197