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