/src/php-src/Zend/Optimizer/optimize_temp_vars_5.c
Line | Count | Source |
1 | | /* |
2 | | +----------------------------------------------------------------------+ |
3 | | | Zend OPcache | |
4 | | +----------------------------------------------------------------------+ |
5 | | | Copyright (c) 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 | | | https://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@php.net> | |
16 | | | Zeev Suraski <zeev@php.net> | |
17 | | | Stanislav Malyshev <stas@zend.com> | |
18 | | | Dmitry Stogov <dmitry@php.net> | |
19 | | +----------------------------------------------------------------------+ |
20 | | */ |
21 | | |
22 | | #include "Optimizer/zend_optimizer.h" |
23 | | #include "Optimizer/zend_optimizer_internal.h" |
24 | | #include "zend_API.h" |
25 | | #include "zend_constants.h" |
26 | | #include "zend_execute.h" |
27 | | #include "zend_vm.h" |
28 | | #include "zend_bitset.h" |
29 | | #include "zend_observer.h" |
30 | | |
31 | 2.67M | #define INVALID_VAR ((uint32_t)-1) |
32 | | #define GET_AVAILABLE_T() \ |
33 | 174M | for (i = 0; i < T; i++) { \ |
34 | 174M | if (!zend_bitset_in(taken_T, i)) { \ |
35 | 935k | break; \ |
36 | 935k | } \ |
37 | 174M | } \ |
38 | 935k | zend_bitset_incl(taken_T, i); \ |
39 | 935k | if (i > max) { \ |
40 | 257k | max = i; \ |
41 | 257k | } |
42 | | |
43 | | void zend_optimize_temporary_variables(zend_op_array *op_array, zend_optimizer_ctx *ctx) |
44 | 111k | { |
45 | 111k | uint32_t T = op_array->T; |
46 | 111k | int offset = op_array->last_var; |
47 | 111k | uint32_t bitset_len; |
48 | 111k | zend_bitset taken_T; /* T index in use */ |
49 | 111k | zend_op **start_of_T; /* opline where T is first used */ |
50 | 111k | int *map_T; /* Map's the T to its new index */ |
51 | 111k | zend_op *opline, *end; |
52 | 111k | int currT; |
53 | 111k | int i; |
54 | 111k | int max = -1; |
55 | 111k | void *checkpoint = zend_arena_checkpoint(ctx->arena); |
56 | | |
57 | 111k | bitset_len = zend_bitset_len(T); |
58 | 111k | taken_T = (zend_bitset) zend_arena_alloc(&ctx->arena, bitset_len * ZEND_BITSET_ELM_SIZE); |
59 | 111k | start_of_T = (zend_op **) zend_arena_alloc(&ctx->arena, T * sizeof(zend_op *)); |
60 | 111k | map_T = (int *) zend_arena_alloc(&ctx->arena, T * sizeof(int)); |
61 | 111k | memset(map_T, 0xff, T * sizeof(int)); |
62 | | |
63 | 111k | end = op_array->opcodes; |
64 | 111k | opline = &op_array->opcodes[op_array->last - 1]; |
65 | | |
66 | | /* Find T definition points */ |
67 | 2.93M | while (opline >= end) { |
68 | 2.82M | if (opline->result_type & (IS_VAR | IS_TMP_VAR)) { |
69 | 1.37M | start_of_T[VAR_NUM(opline->result.var) - offset] = opline; |
70 | 1.37M | } |
71 | 2.82M | opline--; |
72 | 2.82M | } |
73 | | |
74 | 111k | zend_bitset_clear(taken_T, bitset_len); |
75 | | |
76 | 111k | end = op_array->opcodes; |
77 | 111k | opline = &op_array->opcodes[op_array->last - 1]; |
78 | | |
79 | 2.93M | while (opline >= end) { |
80 | 2.82M | if ((opline->op1_type & (IS_VAR | IS_TMP_VAR))) { |
81 | 877k | currT = VAR_NUM(opline->op1.var) - offset; |
82 | 877k | if (opline->opcode == ZEND_ROPE_END) { |
83 | 25.8k | int num = (((opline->extended_value + 1) * sizeof(zend_string*)) + (sizeof(zval) - 1)) / sizeof(zval); |
84 | 25.8k | int var; |
85 | | |
86 | 25.8k | var = max; |
87 | 153k | while (var >= 0 && !zend_bitset_in(taken_T, var)) { |
88 | 127k | var--; |
89 | 127k | } |
90 | 25.8k | max = MAX(max, var + num); |
91 | 25.8k | var = var + 1; |
92 | 25.8k | map_T[currT] = var; |
93 | 25.8k | zend_bitset_incl(taken_T, var); |
94 | 25.8k | opline->op1.var = NUM_VAR(var + offset); |
95 | 204k | while (num > 1) { |
96 | 178k | num--; |
97 | 178k | zend_bitset_incl(taken_T, var + num); |
98 | 178k | } |
99 | 851k | } else { |
100 | 851k | if (map_T[currT] == INVALID_VAR) { |
101 | 481k | int use_new_var = 0; |
102 | | |
103 | | /* Code in "finally" blocks may modify temporary variables. |
104 | | * We allocate new temporaries for values that need to |
105 | | * relive FAST_CALLs. |
106 | | */ |
107 | 481k | if ((op_array->fn_flags & ZEND_ACC_HAS_FINALLY_BLOCK) && |
108 | 481k | (opline->opcode == ZEND_RETURN || |
109 | 1.65k | opline->opcode == ZEND_GENERATOR_RETURN || |
110 | 1.65k | opline->opcode == ZEND_RETURN_BY_REF || |
111 | 1.65k | opline->opcode == ZEND_FREE || |
112 | 1.65k | opline->opcode == ZEND_FE_FREE)) { |
113 | 218 | zend_op *curr = opline; |
114 | | |
115 | 253 | while (--curr >= end) { |
116 | 253 | if (curr->opcode == ZEND_FAST_CALL) { |
117 | 75 | use_new_var = 1; |
118 | 75 | break; |
119 | 178 | } else if (curr->opcode != ZEND_FREE && |
120 | 178 | curr->opcode != ZEND_FE_FREE && |
121 | 178 | curr->opcode != ZEND_VERIFY_RETURN_TYPE && |
122 | 178 | curr->opcode != ZEND_DISCARD_EXCEPTION) { |
123 | 143 | break; |
124 | 143 | } |
125 | 253 | } |
126 | 218 | } |
127 | 481k | if (use_new_var) { |
128 | 75 | i = ++max; |
129 | 75 | zend_bitset_incl(taken_T, i); |
130 | 481k | } else { |
131 | 481k | GET_AVAILABLE_T(); |
132 | 481k | } |
133 | 481k | map_T[currT] = i; |
134 | 481k | } |
135 | 851k | opline->op1.var = NUM_VAR(map_T[currT] + offset); |
136 | 851k | } |
137 | 877k | } |
138 | | |
139 | 2.82M | if ((opline->op2_type & (IS_VAR | IS_TMP_VAR))) { |
140 | 442k | currT = VAR_NUM(opline->op2.var) - offset; |
141 | 442k | if (map_T[currT] == INVALID_VAR) { |
142 | 442k | GET_AVAILABLE_T(); |
143 | 442k | map_T[currT] = i; |
144 | 442k | } |
145 | 442k | opline->op2.var = NUM_VAR(map_T[currT] + offset); |
146 | 442k | } |
147 | | |
148 | 2.82M | if (opline->result_type & (IS_VAR | IS_TMP_VAR)) { |
149 | 1.37M | currT = VAR_NUM(opline->result.var) - offset; |
150 | 1.37M | if (map_T[currT] == INVALID_VAR) { |
151 | | /* As a result of DCE, an opcode may have an unused result. */ |
152 | 11.8k | GET_AVAILABLE_T(); |
153 | 11.8k | map_T[currT] = i; |
154 | 11.8k | } |
155 | 1.37M | opline->result.var = NUM_VAR(map_T[currT] + offset); |
156 | 1.37M | if (start_of_T[currT] == opline) { |
157 | | /* ZEND_FAST_CALL can not share temporary var with others |
158 | | * since the fast_var could also be set by ZEND_HANDLE_EXCEPTION |
159 | | * which could be ahead of it */ |
160 | 960k | if (opline->opcode != ZEND_FAST_CALL) { |
161 | 960k | zend_bitset_excl(taken_T, map_T[currT]); |
162 | 960k | } |
163 | 960k | if (opline->opcode == ZEND_ROPE_INIT) { |
164 | 25.8k | uint32_t num = ((opline->extended_value * sizeof(zend_string*)) + (sizeof(zval) - 1)) / sizeof(zval); |
165 | 204k | while (num > 1) { |
166 | 178k | num--; |
167 | 178k | zend_bitset_excl(taken_T, map_T[currT]+num); |
168 | 178k | } |
169 | 25.8k | } |
170 | 960k | } |
171 | 1.37M | } |
172 | | |
173 | 2.82M | opline--; |
174 | 2.82M | } |
175 | | |
176 | 111k | zend_arena_release(&ctx->arena, checkpoint); |
177 | 111k | op_array->T = max + 1 + ZEND_OBSERVER_ENABLED; // reserve last temporary for observers if enabled |
178 | 111k | } |