1 /*
2 +----------------------------------------------------------------------+
3 | Zend OPcache |
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: Andi Gutmans <andi@zend.com> |
16 | Zeev Suraski <zeev@zend.com> |
17 | Stanislav Malyshev <stas@zend.com> |
18 | Dmitry Stogov <dmitry@zend.com> |
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 (ZEND_OP1_TYPE(opline) == 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 /* break missing *intentionally* - the assign_op's may only optimize op2 */
57 case ZEND_ASSIGN_ADD:
58 case ZEND_ASSIGN_SUB:
59 case ZEND_ASSIGN_MUL:
60 case ZEND_ASSIGN_DIV:
61 case ZEND_ASSIGN_POW:
62 if (opline->extended_value != 0) {
63 /* object tristate op - don't attempt to optimize it! */
64 break;
65 }
66 if (ZEND_OP2_TYPE(opline) == IS_CONST) {
67 if (Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_STRING) {
68 /* don't optimise if it should produce a runtime numeric string error */
69 if (is_numeric_string(Z_STRVAL(ZEND_OP2_LITERAL(opline)), Z_STRLEN(ZEND_OP2_LITERAL(opline)), NULL, NULL, 0)) {
70 convert_scalar_to_number(&ZEND_OP2_LITERAL(opline));
71 }
72 }
73 }
74 break;
75
76 case ZEND_MOD:
77 case ZEND_SL:
78 case ZEND_SR:
79 if (ZEND_OP1_TYPE(opline) == IS_CONST) {
80 if (Z_TYPE(ZEND_OP1_LITERAL(opline)) != IS_LONG) {
81 /* don't optimise if it should produce a runtime numeric string error */
82 if (!(Z_TYPE(ZEND_OP1_LITERAL(opline)) == IS_STRING
83 && !is_numeric_string(Z_STRVAL(ZEND_OP1_LITERAL(opline)), Z_STRLEN(ZEND_OP1_LITERAL(opline)), NULL, NULL, 0))) {
84 convert_to_long(&ZEND_OP1_LITERAL(opline));
85 }
86 }
87 }
88 /* break missing *intentionally - the assign_op's may only optimize op2 */
89 case ZEND_ASSIGN_MOD:
90 case ZEND_ASSIGN_SL:
91 case ZEND_ASSIGN_SR:
92 if (opline->extended_value != 0) {
93 /* object tristate op - don't attempt to optimize it! */
94 break;
95 }
96 if (ZEND_OP2_TYPE(opline) == IS_CONST) {
97 if (Z_TYPE(ZEND_OP2_LITERAL(opline)) != IS_LONG) {
98 /* don't optimise if it should produce a runtime numeric string error */
99 if (!(Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_STRING
100 && !is_numeric_string(Z_STRVAL(ZEND_OP2_LITERAL(opline)), Z_STRLEN(ZEND_OP2_LITERAL(opline)), NULL, NULL, 0))) {
101 convert_to_long(&ZEND_OP2_LITERAL(opline));
102 }
103 }
104 }
105 break;
106
107 case ZEND_CONCAT:
108 case ZEND_FAST_CONCAT:
109 if (ZEND_OP1_TYPE(opline) == IS_CONST) {
110 if (Z_TYPE(ZEND_OP1_LITERAL(opline)) != IS_STRING) {
111 convert_to_string(&ZEND_OP1_LITERAL(opline));
112 }
113 }
114 /* break missing *intentionally - the assign_op's may only optimize op2 */
115 case ZEND_ASSIGN_CONCAT:
116 if (opline->extended_value != 0) {
117 /* object tristate op - don't attempt to optimize it! */
118 break;
119 }
120 if (ZEND_OP2_TYPE(opline) == IS_CONST) {
121 if (Z_TYPE(ZEND_OP2_LITERAL(opline)) != IS_STRING) {
122 convert_to_string(&ZEND_OP2_LITERAL(opline));
123 }
124 }
125 break;
126
127 case ZEND_JMPZ_EX:
128 case ZEND_JMPNZ_EX:
129 /* convert Ti = JMPZ_EX(Ti, L) to JMPZ(Ti, L) */
130 #if 0
131 /* Disabled unsafe pattern: in conjunction with
132 * ZEND_VM_SMART_BRANCH() this may improperly eliminate
133 * assignment to Ti.
134 */
135 if (ZEND_OP1_TYPE(opline) == IS_TMP_VAR &&
136 ZEND_RESULT_TYPE(opline) == IS_TMP_VAR &&
137 ZEND_OP1(opline).var == ZEND_RESULT(opline).var) {
138 opline->opcode -= 3;
139 SET_UNUSED(opline->result);
140 } else
141 #endif
142 /* convert Ti = JMPZ_EX(C, L) => Ti = QM_ASSIGN(C)
143 in case we know it wouldn't jump */
144 if (ZEND_OP1_TYPE(opline) == IS_CONST) {
145 int should_jmp = zend_is_true(&ZEND_OP1_LITERAL(opline));
146 if (opline->opcode == ZEND_JMPZ_EX) {
147 should_jmp = !should_jmp;
148 }
149 if (!should_jmp) {
150 opline->opcode = ZEND_QM_ASSIGN;
151 SET_UNUSED(opline->op2);
152 }
153 }
154 break;
155
156 case ZEND_JMPZ:
157 case ZEND_JMPNZ:
158 if (ZEND_OP1_TYPE(opline) == IS_CONST) {
159 int should_jmp = zend_is_true(&ZEND_OP1_LITERAL(opline));
160
161 if (opline->opcode == ZEND_JMPZ) {
162 should_jmp = !should_jmp;
163 }
164 literal_dtor(&ZEND_OP1_LITERAL(opline));
165 ZEND_OP1_TYPE(opline) = IS_UNUSED;
166 if (should_jmp) {
167 opline->opcode = ZEND_JMP;
168 COPY_NODE(opline->op1, opline->op2);
169 } else {
170 MAKE_NOP(opline);
171 }
172 break;
173 }
174 if ((opline + 1)->opcode == ZEND_JMP) {
175 /* JMPZ(X, L1), JMP(L2) => JMPZNZ(X, L1, L2) */
176 /* JMPNZ(X, L1), JMP(L2) => JMPZNZ(X, L2, L1) */
177 if (ZEND_OP2_JMP_ADDR(opline) == ZEND_OP1_JMP_ADDR(opline + 1)) {
178 /* JMPZ(X, L1), JMP(L1) => NOP, JMP(L1) */
179 if (opline->op1_type == IS_CV) {
180 opline->opcode = ZEND_CHECK_VAR;
181 opline->op2.num = 0;
182 } else if (opline->op1_type & (IS_TMP_VAR|IS_VAR)) {
183 opline->opcode = ZEND_FREE;
184 opline->op2.num = 0;
185 } else {
186 MAKE_NOP(opline);
187 }
188 } else {
189 if (opline->opcode == ZEND_JMPZ) {
190 opline->extended_value = ZEND_OPLINE_TO_OFFSET(opline, ZEND_OP1_JMP_ADDR(opline + 1));
191 } else {
192 opline->extended_value = ZEND_OPLINE_TO_OFFSET(opline, ZEND_OP2_JMP_ADDR(opline));
193 ZEND_SET_OP_JMP_ADDR(opline, opline->op2, ZEND_OP1_JMP_ADDR(opline + 1));
194 }
195 opline->opcode = ZEND_JMPZNZ;
196 }
197 }
198 break;
199
200 case ZEND_JMPZNZ:
201 if (ZEND_OP1_TYPE(opline) == IS_CONST) {
202 zend_op *target_opline;
203
204 if (zend_is_true(&ZEND_OP1_LITERAL(opline))) {
205 target_opline = ZEND_OFFSET_TO_OPLINE(opline, opline->extended_value); /* JMPNZ */
206 } else {
207 target_opline = ZEND_OP2_JMP_ADDR(opline); /* JMPZ */
208 }
209 literal_dtor(&ZEND_OP1_LITERAL(opline));
210 ZEND_SET_OP_JMP_ADDR(opline, opline->op1, target_opline);
211 ZEND_OP1_TYPE(opline) = IS_UNUSED;
212 opline->opcode = ZEND_JMP;
213 }
214 break;
215 }
216 opline++;
217 }
218 }
219