xref: /PHP-8.1/Zend/Optimizer/compact_vars.c (revision 01b3fc03)
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