xref: /PHP-7.3/ext/opcache/Optimizer/pass2.c (revision 9afce019)
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@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 				/* 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 (opline->op2_type == 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 (opline->op1_type == 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 (opline->op2_type == 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 (opline->op1_type == 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 (opline->op2_type == 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 (opline->op1_type == IS_TMP_VAR &&
136 				    opline->result_type == IS_TMP_VAR &&
137 				    opline->op1.var == opline->result.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 (opline->op1_type == 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 (opline->op1_type == 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 					opline->op1_type = 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 (opline->op1_type == 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 					opline->op1_type = IS_UNUSED;
212 					opline->opcode = ZEND_JMP;
213 				}
214 				break;
215 		}
216 		opline++;
217 	}
218 }
219