1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
|
/*
+----------------------------------------------------------------------+
| Zend Engine, Removing unused variables |
+----------------------------------------------------------------------+
| Copyright (c) The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
| Authors: Nikita Popov <nikic@php.net> |
+----------------------------------------------------------------------+
*/
#include "Optimizer/zend_optimizer_internal.h"
#include "zend_bitset.h"
/* This pass removes all CVs and temporaries that are completely unused. It does *not* merge any CVs or TMPs.
* This pass does not operate on SSA form anymore. */
void zend_optimizer_compact_vars(zend_op_array *op_array) {
int i;
ALLOCA_FLAG(use_heap1);
ALLOCA_FLAG(use_heap2);
uint32_t used_vars_len = zend_bitset_len(op_array->last_var + op_array->T);
zend_bitset used_vars = ZEND_BITSET_ALLOCA(used_vars_len, use_heap1);
uint32_t *vars_map = do_alloca((op_array->last_var + op_array->T) * sizeof(uint32_t), use_heap2);
uint32_t num_cvs, num_tmps;
/* Determine which CVs are used */
zend_bitset_clear(used_vars, used_vars_len);
for (i = 0; i < op_array->last; i++) {
zend_op *opline = &op_array->opcodes[i];
if (opline->op1_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
zend_bitset_incl(used_vars, VAR_NUM(opline->op1.var));
}
if (opline->op2_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
zend_bitset_incl(used_vars, VAR_NUM(opline->op2.var));
}
if (opline->result_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
zend_bitset_incl(used_vars, VAR_NUM(opline->result.var));
if (opline->opcode == ZEND_ROPE_INIT) {
uint32_t num = ((opline->extended_value * sizeof(zend_string*)) + (sizeof(zval) - 1)) / sizeof(zval);
while (num > 1) {
num--;
zend_bitset_incl(used_vars, VAR_NUM(opline->result.var) + num);
}
}
}
}
num_cvs = 0;
for (i = 0; i < op_array->last_var; i++) {
if (zend_bitset_in(used_vars, i)) {
vars_map[i] = num_cvs++;
} else {
vars_map[i] = (uint32_t) -1;
}
}
num_tmps = 0;
for (i = op_array->last_var; i < op_array->last_var + op_array->T; i++) {
if (zend_bitset_in(used_vars, i)) {
vars_map[i] = num_cvs + num_tmps++;
} else {
vars_map[i] = (uint32_t) -1;
}
}
free_alloca(used_vars, use_heap1);
if (num_cvs == op_array->last_var && num_tmps == op_array->T) {
free_alloca(vars_map, use_heap2);
return;
}
ZEND_ASSERT(num_cvs <= op_array->last_var);
ZEND_ASSERT(num_tmps <= op_array->T);
/* Update CV and TMP references in opcodes */
for (i = 0; i < op_array->last; i++) {
zend_op *opline = &op_array->opcodes[i];
if (opline->op1_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
opline->op1.var = NUM_VAR(vars_map[VAR_NUM(opline->op1.var)]);
}
if (opline->op2_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
opline->op2.var = NUM_VAR(vars_map[VAR_NUM(opline->op2.var)]);
}
if (opline->result_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
opline->result.var = NUM_VAR(vars_map[VAR_NUM(opline->result.var)]);
}
}
/* Update CV name table */
if (num_cvs != op_array->last_var) {
if (num_cvs) {
zend_string **names = safe_emalloc(sizeof(zend_string *), num_cvs, 0);
for (i = 0; i < op_array->last_var; i++) {
if (vars_map[i] != (uint32_t) -1) {
names[vars_map[i]] = op_array->vars[i];
} else {
zend_string_release_ex(op_array->vars[i], 0);
}
}
efree(op_array->vars);
op_array->vars = names;
} else {
for (i = 0; i < op_array->last_var; i++) {
zend_string_release_ex(op_array->vars[i], 0);
}
efree(op_array->vars);
op_array->vars = NULL;
}
op_array->last_var = num_cvs;
}
op_array->T = num_tmps;
free_alloca(vars_map, use_heap2);
}
|