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