1 /*
2 +----------------------------------------------------------------------+
3 | Zend OPcache |
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 | 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: Andi Gutmans <andi@php.net> |
16 | Zeev Suraski <zeev@php.net> |
17 | Stanislav Malyshev <stas@zend.com> |
18 | Dmitry Stogov <dmitry@php.net> |
19 +----------------------------------------------------------------------+
20 */
21
22 /* pass 2:
23 * - convert non-numeric constants to numeric constants in numeric operators
24 * - optimize constant conditional JMPs
25 */
26
27 #include "php.h"
28 #include "Optimizer/zend_optimizer.h"
29 #include "Optimizer/zend_optimizer_internal.h"
30 #include "zend_API.h"
31 #include "zend_constants.h"
32 #include "zend_execute.h"
33 #include "zend_vm.h"
34
zend_optimizer_pass2(zend_op_array * op_array)35 void zend_optimizer_pass2(zend_op_array *op_array)
36 {
37 zend_op *opline;
38 zend_op *end = op_array->opcodes + op_array->last;
39
40 opline = op_array->opcodes;
41 while (opline < end) {
42 switch (opline->opcode) {
43 case ZEND_ADD:
44 case ZEND_SUB:
45 case ZEND_MUL:
46 case ZEND_DIV:
47 case ZEND_POW:
48 if (opline->op1_type == IS_CONST) {
49 if (Z_TYPE(ZEND_OP1_LITERAL(opline)) == IS_STRING) {
50 /* don't optimise if it should produce a runtime numeric string error */
51 if (is_numeric_string(Z_STRVAL(ZEND_OP1_LITERAL(opline)), Z_STRLEN(ZEND_OP1_LITERAL(opline)), NULL, NULL, 0)) {
52 convert_scalar_to_number(&ZEND_OP1_LITERAL(opline));
53 }
54 }
55 }
56 if (opline->op2_type == IS_CONST) {
57 if (Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_STRING) {
58 /* don't optimise if it should produce a runtime numeric string error */
59 if (is_numeric_string(Z_STRVAL(ZEND_OP2_LITERAL(opline)), Z_STRLEN(ZEND_OP2_LITERAL(opline)), NULL, NULL, 0)) {
60 convert_scalar_to_number(&ZEND_OP2_LITERAL(opline));
61 }
62 }
63 }
64 break;
65
66 case ZEND_MOD:
67 case ZEND_SL:
68 case ZEND_SR:
69 if (opline->op1_type == IS_CONST) {
70 if (Z_TYPE(ZEND_OP1_LITERAL(opline)) != IS_LONG) {
71 /* don't optimise if it should produce a runtime numeric string error */
72 if (!(Z_TYPE(ZEND_OP1_LITERAL(opline)) == IS_STRING
73 && !is_numeric_string(Z_STRVAL(ZEND_OP1_LITERAL(opline)), Z_STRLEN(ZEND_OP1_LITERAL(opline)), NULL, NULL, 0))) {
74 convert_to_long(&ZEND_OP1_LITERAL(opline));
75 }
76 }
77 }
78 if (opline->op2_type == IS_CONST) {
79 if (Z_TYPE(ZEND_OP2_LITERAL(opline)) != IS_LONG) {
80 /* don't optimise if it should produce a runtime numeric string error */
81 if (!(Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_STRING
82 && !is_numeric_string(Z_STRVAL(ZEND_OP2_LITERAL(opline)), Z_STRLEN(ZEND_OP2_LITERAL(opline)), NULL, NULL, 0))) {
83 convert_to_long(&ZEND_OP2_LITERAL(opline));
84 }
85 }
86 }
87 break;
88
89 case ZEND_CONCAT:
90 case ZEND_FAST_CONCAT:
91 if (opline->op1_type == IS_CONST) {
92 if (Z_TYPE(ZEND_OP1_LITERAL(opline)) != IS_STRING) {
93 convert_to_string(&ZEND_OP1_LITERAL(opline));
94 }
95 }
96 if (opline->op2_type == IS_CONST) {
97 if (Z_TYPE(ZEND_OP2_LITERAL(opline)) != IS_STRING) {
98 convert_to_string(&ZEND_OP2_LITERAL(opline));
99 }
100 }
101 break;
102
103 case ZEND_ASSIGN_OP:
104 if (opline->op2_type == IS_CONST) {
105 if (opline->extended_value == ZEND_ADD
106 || opline->extended_value == ZEND_SUB
107 || opline->extended_value == ZEND_MUL
108 || opline->extended_value == ZEND_DIV
109 || opline->extended_value == ZEND_POW) {
110 if (Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_STRING) {
111 /* don't optimise if it should produce a runtime numeric string error */
112 if (is_numeric_string(Z_STRVAL(ZEND_OP2_LITERAL(opline)), Z_STRLEN(ZEND_OP2_LITERAL(opline)), NULL, NULL, 0)) {
113 convert_scalar_to_number(&ZEND_OP2_LITERAL(opline));
114 }
115 }
116 } else if (opline->extended_value == ZEND_MOD
117 || opline->extended_value == ZEND_SL
118 || opline->extended_value == ZEND_SR) {
119 if (Z_TYPE(ZEND_OP2_LITERAL(opline)) != IS_LONG) {
120 /* don't optimise if it should produce a runtime numeric string error */
121 if (!(Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_STRING
122 && !is_numeric_string(Z_STRVAL(ZEND_OP2_LITERAL(opline)), Z_STRLEN(ZEND_OP2_LITERAL(opline)), NULL, NULL, 0))) {
123 convert_to_long(&ZEND_OP2_LITERAL(opline));
124 }
125 }
126 } else if (opline->extended_value == ZEND_CONCAT) {
127 if (Z_TYPE(ZEND_OP2_LITERAL(opline)) != IS_STRING) {
128 convert_to_string(&ZEND_OP2_LITERAL(opline));
129 }
130 }
131 }
132 break;
133
134 case ZEND_JMPZ_EX:
135 case ZEND_JMPNZ_EX:
136 /* convert Ti = JMPZ_EX(Ti, L) to JMPZ(Ti, L) */
137 #if 0
138 /* Disabled unsafe pattern: in conjunction with
139 * ZEND_VM_SMART_BRANCH() this may improperly eliminate
140 * assignment to Ti.
141 */
142 if (opline->op1_type == IS_TMP_VAR &&
143 opline->result_type == IS_TMP_VAR &&
144 opline->op1.var == opline->result.var) {
145 opline->opcode -= 3;
146 SET_UNUSED(opline->result);
147 } else
148 #endif
149 /* convert Ti = JMPZ_EX(C, L) => Ti = QM_ASSIGN(C)
150 in case we know it wouldn't jump */
151 if (opline->op1_type == IS_CONST) {
152 int should_jmp = zend_is_true(&ZEND_OP1_LITERAL(opline));
153 if (opline->opcode == ZEND_JMPZ_EX) {
154 should_jmp = !should_jmp;
155 }
156 if (!should_jmp) {
157 opline->opcode = ZEND_QM_ASSIGN;
158 SET_UNUSED(opline->op2);
159 }
160 }
161 break;
162
163 case ZEND_JMPZ:
164 case ZEND_JMPNZ:
165 if (opline->op1_type == IS_CONST) {
166 int should_jmp = zend_is_true(&ZEND_OP1_LITERAL(opline));
167
168 if (opline->opcode == ZEND_JMPZ) {
169 should_jmp = !should_jmp;
170 }
171 literal_dtor(&ZEND_OP1_LITERAL(opline));
172 opline->op1_type = IS_UNUSED;
173 if (should_jmp) {
174 opline->opcode = ZEND_JMP;
175 COPY_NODE(opline->op1, opline->op2);
176 } else {
177 MAKE_NOP(opline);
178 }
179 break;
180 }
181 if ((opline + 1)->opcode == ZEND_JMP) {
182 /* JMPZ(X, L1), JMP(L2) => JMPZNZ(X, L1, L2) */
183 /* JMPNZ(X, L1), JMP(L2) => JMPZNZ(X, L2, L1) */
184 if (ZEND_OP2_JMP_ADDR(opline) == ZEND_OP1_JMP_ADDR(opline + 1)) {
185 /* JMPZ(X, L1), JMP(L1) => NOP, JMP(L1) */
186 if (opline->op1_type == IS_CV) {
187 opline->opcode = ZEND_CHECK_VAR;
188 opline->op2.num = 0;
189 } else if (opline->op1_type & (IS_TMP_VAR|IS_VAR)) {
190 opline->opcode = ZEND_FREE;
191 opline->op2.num = 0;
192 } else {
193 MAKE_NOP(opline);
194 }
195 } else {
196 if (opline->opcode == ZEND_JMPZ) {
197 opline->extended_value = ZEND_OPLINE_TO_OFFSET(opline, ZEND_OP1_JMP_ADDR(opline + 1));
198 } else {
199 opline->extended_value = ZEND_OPLINE_TO_OFFSET(opline, ZEND_OP2_JMP_ADDR(opline));
200 ZEND_SET_OP_JMP_ADDR(opline, opline->op2, ZEND_OP1_JMP_ADDR(opline + 1));
201 }
202 opline->opcode = ZEND_JMPZNZ;
203 }
204 }
205 break;
206
207 case ZEND_JMPZNZ:
208 if (opline->op1_type == IS_CONST) {
209 zend_op *target_opline;
210
211 if (zend_is_true(&ZEND_OP1_LITERAL(opline))) {
212 target_opline = ZEND_OFFSET_TO_OPLINE(opline, opline->extended_value); /* JMPNZ */
213 } else {
214 target_opline = ZEND_OP2_JMP_ADDR(opline); /* JMPZ */
215 }
216 literal_dtor(&ZEND_OP1_LITERAL(opline));
217 ZEND_SET_OP_JMP_ADDR(opline, opline->op1, target_opline);
218 opline->op1_type = IS_UNUSED;
219 opline->opcode = ZEND_JMP;
220 }
221 break;
222 }
223 opline++;
224 }
225 }
226