1/*
2   +----------------------------------------------------------------------+
3   | Zend OPcache                                                         |
4   +----------------------------------------------------------------------+
5   | Copyright (c) 1998-2014 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        /* special puprose variable to keep HashPointer on VM stack */
70        if (opline->opcode == ZEND_OP_DATA &&
71            (opline-1)->opcode == ZEND_FE_FETCH &&
72            opline->op1_type == IS_TMP_VAR) {
73            start_of_T[VAR_NUM(ZEND_OP1(opline).var) - offset] = opline;
74            if (sizeof(HashPointer) > sizeof(zval)) {
75                /* Make shure 1 zval is enough for HashPointer (2 must be enough) */
76                start_of_T[VAR_NUM(ZEND_OP1(opline).var) + 1 - offset] = opline;
77            }
78        }
79        opline--;
80    }
81
82    memset(valid_T, 0, T);
83    memset(taken_T, 0, T);
84
85    end = op_array->opcodes;
86    opline = &op_array->opcodes[op_array->last - 1];
87
88    while (opline >= end) {
89        if ((ZEND_OP1_TYPE(opline) & (IS_VAR | IS_TMP_VAR))) {
90
91            /* special puprose variable to keep HashPointer on VM stack */
92            if (opline->opcode == ZEND_OP_DATA &&
93                (opline-1)->opcode == ZEND_FE_FETCH &&
94                opline->op1_type == IS_TMP_VAR) {
95                max++;
96                ZEND_OP1(opline).var = NUM_VAR(max + offset);
97                if (sizeof(HashPointer) > sizeof(zval)) {
98                    /* Make shure 1 zval is enough for HashPointer (2 must be enough) */
99                    max++;
100                }
101            } else {
102                currT = VAR_NUM(ZEND_OP1(opline).var) - offset;
103                if (!valid_T[currT]) {
104                    GET_AVAILABLE_T();
105                    map_T[currT] = i;
106                    valid_T[currT] = 1;
107                }
108                ZEND_OP1(opline).var = NUM_VAR(map_T[currT] + offset);
109            }
110        }
111
112        /* Skip OP_DATA */
113        if (opline->opcode == ZEND_OP_DATA &&
114            (opline-1)->opcode == ZEND_ASSIGN_DIM) {
115            opline--;
116            continue;
117        }
118
119        if ((ZEND_OP2_TYPE(opline) & (IS_VAR | IS_TMP_VAR))) {
120            currT = VAR_NUM(ZEND_OP2(opline).var) - offset;
121            if (!valid_T[currT]) {
122                GET_AVAILABLE_T();
123                map_T[currT] = i;
124                valid_T[currT] = 1;
125            }
126            ZEND_OP2(opline).var = NUM_VAR(map_T[currT] + offset);
127        }
128
129        if (opline->opcode == ZEND_DECLARE_INHERITED_CLASS ||
130            opline->opcode == ZEND_DECLARE_INHERITED_CLASS_DELAYED) {
131            currT = VAR_NUM(opline->extended_value) - offset;
132            if (!valid_T[currT]) {
133                GET_AVAILABLE_T();
134                map_T[currT] = i;
135                valid_T[currT] = 1;
136            }
137            opline->extended_value = NUM_VAR(map_T[currT] + offset);
138        }
139
140        /* Allocate OP_DATA->op2 after "operands", but before "result" */
141        if (opline->opcode == ZEND_ASSIGN_DIM &&
142            (opline + 1)->opcode == ZEND_OP_DATA &&
143            ZEND_OP2_TYPE(opline + 1) & (IS_VAR | IS_TMP_VAR)) {
144            currT = VAR_NUM(ZEND_OP2(opline + 1).var) - offset;
145            GET_AVAILABLE_T();
146            map_T[currT] = i;
147            valid_T[currT] = 1;
148            taken_T[i] = 0;
149            ZEND_OP2(opline + 1).var = NUM_VAR(i + offset);
150            var_to_free = i;
151        }
152
153        if (ZEND_RESULT_TYPE(opline) & (IS_VAR | IS_TMP_VAR)) {
154            currT = VAR_NUM(ZEND_RESULT(opline).var) - offset;
155            if (valid_T[currT]) {
156                if (start_of_T[currT] == opline) {
157                    taken_T[map_T[currT]] = 0;
158                }
159                ZEND_RESULT(opline).var = NUM_VAR(map_T[currT] + offset);
160            } else { /* Au still needs to be assigned a T which is a bit dumb. Should consider changing Zend */
161                GET_AVAILABLE_T();
162
163                if (RESULT_UNUSED(opline)) {
164                    taken_T[i] = 0;
165                } else {
166                    /* Code which gets here is using a wrongly built opcode such as RECV() */
167                    map_T[currT] = i;
168                    valid_T[currT] = 1;
169                }
170                ZEND_RESULT(opline).var = NUM_VAR(i + offset);
171            }
172        }
173
174        if (var_to_free >= 0) {
175            taken_T[var_to_free] = 0;
176            var_to_free = -1;
177        }
178
179        opline--;
180    }
181
182    zend_arena_release(&ctx->arena, checkpoint);
183    op_array->T = max + 1;
184}
185