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_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                    taken_T[map_T[currT]] = 0;
156                }
157                ZEND_RESULT(opline).var = NUM_VAR(map_T[currT] + offset);
158                if (opline->opcode == ZEND_ROPE_INIT) {
159                    if (start_of_T[currT] == opline) {
160                        uint32_t num = ((opline->extended_value * sizeof(zend_string*)) + (sizeof(zval) - 1)) / sizeof(zval);
161                        while (num > 1) {
162                            num--;
163                            taken_T[map_T[currT]+num] = 0;
164                        }
165                    }
166                }
167            } else { /* Au still needs to be assigned a T which is a bit dumb. Should consider changing Zend */
168                GET_AVAILABLE_T();
169
170                if (RESULT_UNUSED(opline)) {
171                    taken_T[i] = 0;
172                } else {
173                    /* Code which gets here is using a wrongly built opcode such as RECV() */
174                    map_T[currT] = i;
175                    valid_T[currT] = 1;
176                }
177                ZEND_RESULT(opline).var = NUM_VAR(i + offset);
178            }
179        }
180
181        if (var_to_free >= 0) {
182            taken_T[var_to_free] = 0;
183            var_to_free = -1;
184        }
185
186        opline--;
187    }
188
189    zend_arena_release(&ctx->arena, checkpoint);
190    op_array->T = max + 1;
191}
192