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#define GET_AVAILABLE_T()       \
23    for (i = 0; i < T; i++) {   \
24        if (!taken_T[i]) {      \
25            break;              \
26        }                       \
27    }                           \
28    taken_T[i] = 1;             \
29    if (i > max) {              \
30        max = i;                \
31    }
32
33static void optimize_temporary_variables(zend_op_array *op_array, zend_optimizer_ctx *ctx)
34{
35    int T = op_array->T;
36    int offset = op_array->last_var;
37    char *taken_T;          /* T index in use */
38    zend_op **start_of_T;   /* opline where T is first used */
39    char *valid_T;          /* Is the map_T valid */
40    int *map_T;             /* Map's the T to its new index */
41    zend_op *opline, *end;
42    int currT;
43    int i;
44    int max = -1;
45    int var_to_free = -1;
46    void *checkpoint = zend_arena_checkpoint(ctx->arena);
47
48    taken_T = (char *) zend_arena_alloc(&ctx->arena, T);
49    start_of_T = (zend_op **) zend_arena_alloc(&ctx->arena, T * sizeof(zend_op *));
50    valid_T = (char *) zend_arena_alloc(&ctx->arena, T);
51    map_T = (int *) zend_arena_alloc(&ctx->arena, T * sizeof(int));
52
53    end = op_array->opcodes;
54    opline = &op_array->opcodes[op_array->last - 1];
55
56    /* Find T definition points */
57    while (opline >= end) {
58        if (ZEND_RESULT_TYPE(opline) & (IS_VAR | IS_TMP_VAR)) {
59            start_of_T[VAR_NUM(ZEND_RESULT(opline).var) - offset] = opline;
60        }
61        /* special puprose variable to keep HashPointer on VM stack */
62        if (opline->opcode == ZEND_OP_DATA &&
63            (opline-1)->opcode == ZEND_FE_FETCH &&
64            opline->op1_type == IS_TMP_VAR) {
65            start_of_T[VAR_NUM(ZEND_OP1(opline).var) - offset] = opline;
66            if (sizeof(HashPointer) > sizeof(zval)) {
67                /* Make shure 1 zval is enough for HashPointer (2 must be enough) */
68                start_of_T[VAR_NUM(ZEND_OP1(opline).var) + 1 - offset] = opline;
69            }
70        }
71        opline--;
72    }
73
74    memset(valid_T, 0, T);
75    memset(taken_T, 0, T);
76
77    end = op_array->opcodes;
78    opline = &op_array->opcodes[op_array->last - 1];
79
80    while (opline >= end) {
81        if ((ZEND_OP1_TYPE(opline) & (IS_VAR | IS_TMP_VAR))) {
82
83            /* special puprose variable to keep HashPointer on VM stack */
84            if (opline->opcode == ZEND_OP_DATA &&
85                (opline-1)->opcode == ZEND_FE_FETCH &&
86                opline->op1_type == IS_TMP_VAR) {
87                max++;
88                ZEND_OP1(opline).var = NUM_VAR(max + offset);
89                if (sizeof(HashPointer) > sizeof(zval)) {
90                    /* Make shure 1 zval is enough for HashPointer (2 must be enough) */
91                    max++;
92                }
93            } else {
94                currT = VAR_NUM(ZEND_OP1(opline).var) - offset;
95                if (!valid_T[currT]) {
96                    GET_AVAILABLE_T();
97                    map_T[currT] = i;
98                    valid_T[currT] = 1;
99                }
100                ZEND_OP1(opline).var = NUM_VAR(map_T[currT] + offset);
101            }
102        }
103
104        /* Skip OP_DATA */
105        if (opline->opcode == ZEND_OP_DATA &&
106            (opline-1)->opcode == ZEND_ASSIGN_DIM) {
107            opline--;
108            continue;
109        }
110
111        if ((ZEND_OP2_TYPE(opline) & (IS_VAR | IS_TMP_VAR))) {
112            currT = VAR_NUM(ZEND_OP2(opline).var) - offset;
113            if (!valid_T[currT]) {
114                GET_AVAILABLE_T();
115                map_T[currT] = i;
116                valid_T[currT] = 1;
117            }
118            ZEND_OP2(opline).var = NUM_VAR(map_T[currT] + offset);
119        }
120
121        if (opline->opcode == ZEND_DECLARE_INHERITED_CLASS ||
122            opline->opcode == ZEND_DECLARE_INHERITED_CLASS_DELAYED) {
123            currT = VAR_NUM(opline->extended_value) - offset;
124            if (!valid_T[currT]) {
125                GET_AVAILABLE_T();
126                map_T[currT] = i;
127                valid_T[currT] = 1;
128            }
129            opline->extended_value = NUM_VAR(map_T[currT] + offset);
130        }
131
132        /* Allocate OP_DATA->op2 after "operands", but before "result" */
133        if (opline->opcode == ZEND_ASSIGN_DIM &&
134            (opline + 1)->opcode == ZEND_OP_DATA &&
135            ZEND_OP2_TYPE(opline + 1) & (IS_VAR | IS_TMP_VAR)) {
136            currT = VAR_NUM(ZEND_OP2(opline + 1).var) - offset;
137            GET_AVAILABLE_T();
138            map_T[currT] = i;
139            valid_T[currT] = 1;
140            taken_T[i] = 0;
141            ZEND_OP2(opline + 1).var = NUM_VAR(i + offset);
142            var_to_free = i;
143        }
144
145        if (ZEND_RESULT_TYPE(opline) & (IS_VAR | IS_TMP_VAR)) {
146            currT = VAR_NUM(ZEND_RESULT(opline).var) - offset;
147            if (valid_T[currT]) {
148                if (start_of_T[currT] == opline) {
149                    taken_T[map_T[currT]] = 0;
150                }
151                ZEND_RESULT(opline).var = NUM_VAR(map_T[currT] + offset);
152            } else { /* Au still needs to be assigned a T which is a bit dumb. Should consider changing Zend */
153                GET_AVAILABLE_T();
154
155                if (RESULT_UNUSED(opline)) {
156                    taken_T[i] = 0;
157                } else {
158                    /* Code which gets here is using a wrongly built opcode such as RECV() */
159                    map_T[currT] = i;
160                    valid_T[currT] = 1;
161                }
162                ZEND_RESULT(opline).var = NUM_VAR(i + offset);
163            }
164        }
165
166        if (var_to_free >= 0) {
167            taken_T[var_to_free] = 0;
168            var_to_free = -1;
169        }
170
171        opline--;
172    }
173
174    zend_arena_release(&ctx->arena, checkpoint);
175    op_array->T = max + 1;
176}
177