/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.32M | #define INVALID_VAR ((uint32_t)-1) |
32 | | #define GET_AVAILABLE_T() \ |
33 | 1.31M | for (i = 0; i < T; i++) { \ |
34 | 1.31M | if (!zend_bitset_in(taken_T, i)) { \ |
35 | 741k | break; \ |
36 | 741k | } \ |
37 | 1.31M | } \ |
38 | 741k | zend_bitset_incl(taken_T, i); \ |
39 | 741k | if (i > max) { \ |
40 | 159k | max = i; \ |
41 | 159k | } |
42 | | |
43 | | void zend_optimize_temporary_variables(zend_op_array *op_array, zend_optimizer_ctx *ctx) |
44 | 109k | { |
45 | 109k | uint32_t T = op_array->T; |
46 | 109k | int offset = op_array->last_var; |
47 | 109k | uint32_t bitset_len; |
48 | 109k | zend_bitset taken_T; /* T index in use */ |
49 | 109k | zend_op **start_of_T; /* opline where T is first used */ |
50 | 109k | int *map_T; /* Map's the T to its new index */ |
51 | 109k | zend_op *opline, *end; |
52 | 109k | int currT; |
53 | 109k | int i; |
54 | 109k | int max = -1; |
55 | 109k | void *checkpoint = zend_arena_checkpoint(ctx->arena); |
56 | | |
57 | 109k | bitset_len = zend_bitset_len(T); |
58 | 109k | taken_T = (zend_bitset) zend_arena_alloc(&ctx->arena, bitset_len * ZEND_BITSET_ELM_SIZE); |
59 | 109k | start_of_T = (zend_op **) zend_arena_alloc(&ctx->arena, T * sizeof(zend_op *)); |
60 | 109k | map_T = (int *) zend_arena_alloc(&ctx->arena, T * sizeof(int)); |
61 | 109k | memset(map_T, 0xff, T * sizeof(int)); |
62 | | |
63 | 109k | end = op_array->opcodes; |
64 | 109k | opline = &op_array->opcodes[op_array->last - 1]; |
65 | | |
66 | | /* Find T definition points */ |
67 | 2.61M | while (opline >= end) { |
68 | 2.50M | if (opline->result_type & (IS_VAR | IS_TMP_VAR)) { |
69 | 1.21M | start_of_T[VAR_NUM(opline->result.var) - offset] = opline; |
70 | 1.21M | } |
71 | 2.50M | opline--; |
72 | 2.50M | } |
73 | | |
74 | 109k | zend_bitset_clear(taken_T, bitset_len); |
75 | | |
76 | 109k | end = op_array->opcodes; |
77 | 109k | opline = &op_array->opcodes[op_array->last - 1]; |
78 | | |
79 | 2.61M | while (opline >= end) { |
80 | 2.50M | if ((opline->op1_type & (IS_VAR | IS_TMP_VAR))) { |
81 | 701k | currT = VAR_NUM(opline->op1.var) - offset; |
82 | 701k | if (opline->opcode == ZEND_ROPE_END) { |
83 | 26.0k | int num = (((opline->extended_value + 1) * sizeof(zend_string*)) + (sizeof(zval) - 1)) / sizeof(zval); |
84 | 26.0k | int var; |
85 | | |
86 | 26.0k | var = max; |
87 | 165k | while (var >= 0 && !zend_bitset_in(taken_T, var)) { |
88 | 139k | var--; |
89 | 139k | } |
90 | 26.0k | max = MAX(max, var + num); |
91 | 26.0k | var = var + 1; |
92 | 26.0k | map_T[currT] = var; |
93 | 26.0k | zend_bitset_incl(taken_T, var); |
94 | 26.0k | opline->op1.var = NUM_VAR(var + offset); |
95 | 214k | while (num > 1) { |
96 | 188k | num--; |
97 | 188k | zend_bitset_incl(taken_T, var + num); |
98 | 188k | } |
99 | 675k | } else { |
100 | 675k | if (map_T[currT] == INVALID_VAR) { |
101 | 297k | 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 | 297k | if ((op_array->fn_flags & ZEND_ACC_HAS_FINALLY_BLOCK) && |
108 | 297k | (opline->opcode == ZEND_RETURN || |
109 | 1.61k | opline->opcode == ZEND_GENERATOR_RETURN || |
110 | 1.61k | opline->opcode == ZEND_RETURN_BY_REF || |
111 | 1.61k | opline->opcode == ZEND_FREE || |
112 | 1.61k | opline->opcode == ZEND_FE_FREE)) { |
113 | 226 | zend_op *curr = opline; |
114 | | |
115 | 262 | while (--curr >= end) { |
116 | 262 | if (curr->opcode == ZEND_FAST_CALL) { |
117 | 78 | use_new_var = 1; |
118 | 78 | break; |
119 | 184 | } else if (curr->opcode != ZEND_FREE && |
120 | 184 | curr->opcode != ZEND_FE_FREE && |
121 | 184 | curr->opcode != ZEND_VERIFY_RETURN_TYPE && |
122 | 184 | curr->opcode != ZEND_DISCARD_EXCEPTION) { |
123 | 148 | break; |
124 | 148 | } |
125 | 262 | } |
126 | 226 | } |
127 | 297k | if (use_new_var) { |
128 | 78 | i = ++max; |
129 | 78 | zend_bitset_incl(taken_T, i); |
130 | 297k | } else { |
131 | 297k | GET_AVAILABLE_T(); |
132 | 297k | } |
133 | 297k | map_T[currT] = i; |
134 | 297k | } |
135 | 675k | opline->op1.var = NUM_VAR(map_T[currT] + offset); |
136 | 675k | } |
137 | 701k | } |
138 | | |
139 | 2.50M | if ((opline->op2_type & (IS_VAR | IS_TMP_VAR))) { |
140 | 436k | currT = VAR_NUM(opline->op2.var) - offset; |
141 | 436k | if (map_T[currT] == INVALID_VAR) { |
142 | 436k | GET_AVAILABLE_T(); |
143 | 436k | map_T[currT] = i; |
144 | 436k | } |
145 | 436k | opline->op2.var = NUM_VAR(map_T[currT] + offset); |
146 | 436k | } |
147 | | |
148 | 2.50M | if (opline->result_type & (IS_VAR | IS_TMP_VAR)) { |
149 | 1.21M | currT = VAR_NUM(opline->result.var) - offset; |
150 | 1.21M | if (map_T[currT] == INVALID_VAR) { |
151 | | /* As a result of DCE, an opcode may have an unused result. */ |
152 | 8.03k | GET_AVAILABLE_T(); |
153 | 8.03k | map_T[currT] = i; |
154 | 8.03k | } |
155 | 1.21M | opline->result.var = NUM_VAR(map_T[currT] + offset); |
156 | 1.21M | 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 | 767k | if (opline->opcode != ZEND_FAST_CALL) { |
161 | 767k | zend_bitset_excl(taken_T, map_T[currT]); |
162 | 767k | } |
163 | 767k | if (opline->opcode == ZEND_ROPE_INIT) { |
164 | 26.0k | uint32_t num = ((opline->extended_value * sizeof(zend_string*)) + (sizeof(zval) - 1)) / sizeof(zval); |
165 | 214k | while (num > 1) { |
166 | 188k | num--; |
167 | 188k | zend_bitset_excl(taken_T, map_T[currT]+num); |
168 | 188k | } |
169 | 26.0k | } |
170 | 767k | } |
171 | 1.21M | } |
172 | | |
173 | 2.50M | opline--; |
174 | 2.50M | } |
175 | | |
176 | 109k | zend_arena_release(&ctx->arena, checkpoint); |
177 | 109k | op_array->T = max + 1 + ZEND_OBSERVER_ENABLED; // reserve last temporary for observers if enabled |
178 | 109k | } |