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
81            currT = VAR_NUM(ZEND_OP1(opline).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 && !taken_T[var]) {
88                    var--;
89                }
90                max = MAX(max, var + num);
91                var = var + 1;
92                map_T[currT] = var;
93                valid_T[currT] = 1;
94                taken_T[var] = 1;
95                ZEND_OP1(opline).var = NUM_VAR(var + offset);
96                while (num > 1) {
97                    num--;
98                    taken_T[var + num] = 1;
99                }
100            } else {
101                if (!valid_T[currT]) {
102                    GET_AVAILABLE_T();
103                    map_T[currT] = i;
104                    valid_T[currT] = 1;
105                }
106                ZEND_OP1(opline).var = NUM_VAR(map_T[currT] + offset);
107            }
108        }
109
110        /* Skip OP_DATA */
111        if (opline->opcode == ZEND_OP_DATA &&
112            (opline-1)->opcode == ZEND_ASSIGN_DIM) {
113            opline--;
114            continue;
115        }
116
117        if ((ZEND_OP2_TYPE(opline) & (IS_VAR | IS_TMP_VAR))) {
118            currT = VAR_NUM(ZEND_OP2(opline).var) - offset;
119            if (!valid_T[currT]) {
120                GET_AVAILABLE_T();
121                map_T[currT] = i;
122                valid_T[currT] = 1;
123            }
124            ZEND_OP2(opline).var = NUM_VAR(map_T[currT] + offset);
125        }
126
127        if (opline->opcode == ZEND_DECLARE_INHERITED_CLASS ||
128            opline->opcode == ZEND_DECLARE_ANON_INHERITED_CLASS ||
129            opline->opcode == ZEND_DECLARE_INHERITED_CLASS_DELAYED) {
130            currT = VAR_NUM(opline->extended_value) - offset;
131            if (!valid_T[currT]) {
132                GET_AVAILABLE_T();
133                map_T[currT] = i;
134                valid_T[currT] = 1;
135            }
136            opline->extended_value = NUM_VAR(map_T[currT] + offset);
137        }
138
139        /* Allocate OP_DATA->op2 after "operands", but before "result" */
140        if (opline->opcode == ZEND_ASSIGN_DIM &&
141            (opline + 1)->opcode == ZEND_OP_DATA &&
142            ZEND_OP2_TYPE(opline + 1) & (IS_VAR | IS_TMP_VAR)) {
143            currT = VAR_NUM(ZEND_OP2(opline + 1).var) - offset;
144            GET_AVAILABLE_T();
145            map_T[currT] = i;
146            valid_T[currT] = 1;
147            taken_T[i] = 0;
148            ZEND_OP2(opline + 1).var = NUM_VAR(i + offset);
149            var_to_free = i;
150        }
151
152        if (ZEND_RESULT_TYPE(opline) & (IS_VAR | IS_TMP_VAR)) {
153            currT = VAR_NUM(ZEND_RESULT(opline).var) - offset;
154            if (valid_T[currT]) {
155                if (start_of_T[currT] == opline) {
156                    taken_T[map_T[currT]] = 0;
157                }
158                ZEND_RESULT(opline).var = NUM_VAR(map_T[currT] + offset);
159                if (opline->opcode == ZEND_ROPE_INIT) {
160                    if (start_of_T[currT] == opline) {
161                        uint32_t num = ((opline->extended_value * sizeof(zend_string*)) + (sizeof(zval) - 1)) / sizeof(zval);
162                        while (num > 1) {
163                            num--;
164                            taken_T[map_T[currT]+num] = 0;
165                        }
166                    }
167                }
168            } else { /* Au still needs to be assigned a T which is a bit dumb. Should consider changing Zend */
169                GET_AVAILABLE_T();
170
171                if (RESULT_UNUSED(opline)) {
172                    taken_T[i] = 0;
173                } else {
174                    /* Code which gets here is using a wrongly built opcode such as RECV() */
175                    map_T[currT] = i;
176                    valid_T[currT] = 1;
177                }
178                ZEND_RESULT(opline).var = NUM_VAR(i + offset);
179            }
180        }
181
182        if (var_to_free >= 0) {
183            taken_T[var_to_free] = 0;
184            var_to_free = -1;
185        }
186
187        opline--;
188    }
189
190    zend_arena_release(&ctx->arena, checkpoint);
191    op_array->T = max + 1;
192}
193