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