/src/php-src/Zend/Optimizer/compact_vars.c
Line | Count | Source |
1 | | /* |
2 | | +----------------------------------------------------------------------+ |
3 | | | Zend Engine, Removing unused variables | |
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: Nikita Popov <nikic@php.net> | |
14 | | +----------------------------------------------------------------------+ |
15 | | */ |
16 | | |
17 | | #include "Optimizer/zend_optimizer_internal.h" |
18 | | #include "zend_bitset.h" |
19 | | #include "zend_observer.h" |
20 | | |
21 | | /* This pass removes all CVs and temporaries that are completely unused. It does *not* merge any CVs or TMPs. |
22 | | * This pass does not operate on SSA form anymore. */ |
23 | 47.7k | void zend_optimizer_compact_vars(zend_op_array *op_array) { |
24 | 47.7k | int i; |
25 | | |
26 | 47.7k | ALLOCA_FLAG(use_heap1); |
27 | 47.7k | ALLOCA_FLAG(use_heap2); |
28 | 47.7k | uint32_t used_vars_len = zend_bitset_len(op_array->last_var + op_array->T); |
29 | 47.7k | zend_bitset used_vars = ZEND_BITSET_ALLOCA(used_vars_len, use_heap1); |
30 | 47.7k | uint32_t *vars_map = do_alloca((op_array->last_var + op_array->T) * sizeof(uint32_t), use_heap2); |
31 | 47.7k | uint32_t num_cvs, num_tmps; |
32 | | |
33 | | /* Determine which CVs are used */ |
34 | 47.7k | zend_bitset_clear(used_vars, used_vars_len); |
35 | 1.13M | for (i = 0; i < op_array->last; i++) { |
36 | 1.08M | zend_op *opline = &op_array->opcodes[i]; |
37 | 1.08M | if (opline->op1_type & (IS_CV|IS_VAR|IS_TMP_VAR)) { |
38 | 619k | zend_bitset_incl(used_vars, VAR_NUM(opline->op1.var)); |
39 | 619k | } |
40 | 1.08M | if (opline->op2_type & (IS_CV|IS_VAR|IS_TMP_VAR)) { |
41 | 215k | zend_bitset_incl(used_vars, VAR_NUM(opline->op2.var)); |
42 | 215k | } |
43 | 1.08M | if (opline->result_type & (IS_CV|IS_VAR|IS_TMP_VAR)) { |
44 | 543k | zend_bitset_incl(used_vars, VAR_NUM(opline->result.var)); |
45 | 543k | if (opline->opcode == ZEND_ROPE_INIT) { |
46 | 13.0k | uint32_t num = ((opline->extended_value * sizeof(zend_string*)) + (sizeof(zval) - 1)) / sizeof(zval); |
47 | 85.0k | while (num > 1) { |
48 | 71.9k | num--; |
49 | 71.9k | zend_bitset_incl(used_vars, VAR_NUM(opline->result.var) + num); |
50 | 71.9k | } |
51 | 13.0k | } |
52 | 543k | } |
53 | 1.08M | } |
54 | | |
55 | 47.7k | num_cvs = 0; |
56 | 205k | for (i = 0; i < op_array->last_var; i++) { |
57 | 158k | if (zend_bitset_in(used_vars, i)) { |
58 | 155k | vars_map[i] = num_cvs++; |
59 | 155k | } else { |
60 | 2.35k | vars_map[i] = (uint32_t) -1; |
61 | 2.35k | } |
62 | 158k | } |
63 | | |
64 | 47.7k | num_tmps = 0; |
65 | 193k | for (i = op_array->last_var; i < op_array->last_var + op_array->T; i++) { |
66 | 145k | if (zend_bitset_in(used_vars, i)) { |
67 | 145k | vars_map[i] = num_cvs + num_tmps++; |
68 | 145k | } else { |
69 | 0 | vars_map[i] = (uint32_t) -1; |
70 | 0 | } |
71 | 145k | } |
72 | | |
73 | 47.7k | free_alloca(used_vars, use_heap1); |
74 | 47.7k | if (num_cvs == op_array->last_var && num_tmps == op_array->T) { |
75 | 46.2k | free_alloca(vars_map, use_heap2); |
76 | 46.2k | return; |
77 | 46.2k | } |
78 | | |
79 | 1.54k | ZEND_ASSERT(num_cvs <= op_array->last_var); |
80 | 1.54k | ZEND_ASSERT(num_tmps <= op_array->T); |
81 | | |
82 | | /* Update CV and TMP references in opcodes */ |
83 | 68.8k | for (i = 0; i < op_array->last; i++) { |
84 | 67.2k | zend_op *opline = &op_array->opcodes[i]; |
85 | 67.2k | if (opline->op1_type & (IS_CV|IS_VAR|IS_TMP_VAR)) { |
86 | 39.5k | opline->op1.var = NUM_VAR(vars_map[VAR_NUM(opline->op1.var)]); |
87 | 39.5k | } |
88 | 67.2k | if (opline->op2_type & (IS_CV|IS_VAR|IS_TMP_VAR)) { |
89 | 13.6k | opline->op2.var = NUM_VAR(vars_map[VAR_NUM(opline->op2.var)]); |
90 | 13.6k | } |
91 | 67.2k | if (opline->result_type & (IS_CV|IS_VAR|IS_TMP_VAR)) { |
92 | 34.8k | opline->result.var = NUM_VAR(vars_map[VAR_NUM(opline->result.var)]); |
93 | 34.8k | } |
94 | 67.2k | } |
95 | | |
96 | | /* Update CV name table */ |
97 | 1.54k | if (num_cvs != op_array->last_var) { |
98 | 1.54k | if (num_cvs) { |
99 | 1.31k | zend_string **names = safe_emalloc(sizeof(zend_string *), num_cvs, 0); |
100 | 11.2k | for (i = 0; i < op_array->last_var; i++) { |
101 | 9.91k | if (vars_map[i] != (uint32_t) -1) { |
102 | 7.87k | names[vars_map[i]] = op_array->vars[i]; |
103 | 7.87k | } else { |
104 | 2.04k | zend_string_release_ex(op_array->vars[i], 0); |
105 | 2.04k | } |
106 | 9.91k | } |
107 | 1.31k | efree(op_array->vars); |
108 | 1.31k | op_array->vars = names; |
109 | 1.31k | } else { |
110 | 536 | for (i = 0; i < op_array->last_var; i++) { |
111 | 311 | zend_string_release_ex(op_array->vars[i], 0); |
112 | 311 | } |
113 | 225 | efree(op_array->vars); |
114 | 225 | op_array->vars = NULL; |
115 | 225 | } |
116 | 1.54k | op_array->last_var = num_cvs; |
117 | 1.54k | } |
118 | | |
119 | 1.54k | op_array->T = num_tmps + ZEND_OBSERVER_ENABLED; // reserve last temporary for observers if enabled |
120 | | |
121 | | free_alloca(vars_map, use_heap2); |
122 | 1.54k | } |