xref: /PHP-7.0/ext/opcache/Optimizer/pass2.c (revision 6f950e83)
1 /*
2    +----------------------------------------------------------------------+
3    | Zend OPcache                                                         |
4    +----------------------------------------------------------------------+
5    | Copyright (c) 1998-2017 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  * - optimize static BRKs and CONTs
26  */
27 
28 #include "php.h"
29 #include "Optimizer/zend_optimizer.h"
30 #include "Optimizer/zend_optimizer_internal.h"
31 #include "zend_API.h"
32 #include "zend_constants.h"
33 #include "zend_execute.h"
34 #include "zend_vm.h"
35 
zend_optimizer_pass2(zend_op_array * op_array)36 void zend_optimizer_pass2(zend_op_array *op_array)
37 {
38 	zend_op *opline;
39 	zend_op *end = op_array->opcodes + op_array->last;
40 
41 	opline = op_array->opcodes;
42 	while (opline < end) {
43 		switch (opline->opcode) {
44 			case ZEND_ADD:
45 			case ZEND_SUB:
46 			case ZEND_MUL:
47 			case ZEND_DIV:
48 			case ZEND_POW:
49 				if (ZEND_OP1_TYPE(opline) == IS_CONST) {
50 					if (Z_TYPE(ZEND_OP1_LITERAL(opline)) == IS_STRING) {
51 						convert_scalar_to_number(&ZEND_OP1_LITERAL(opline));
52 					}
53 				}
54 				/* break missing *intentionally* - the assign_op's may only optimize op2 */
55 			case ZEND_ASSIGN_ADD:
56 			case ZEND_ASSIGN_SUB:
57 			case ZEND_ASSIGN_MUL:
58 			case ZEND_ASSIGN_DIV:
59 			case ZEND_ASSIGN_POW:
60 				if (opline->extended_value != 0) {
61 					/* object tristate op - don't attempt to optimize it! */
62 					break;
63 				}
64 				if (ZEND_OP2_TYPE(opline) == IS_CONST) {
65 					if (Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_STRING) {
66 						convert_scalar_to_number(&ZEND_OP2_LITERAL(opline));
67 					}
68 				}
69 				break;
70 
71 			case ZEND_MOD:
72 			case ZEND_SL:
73 			case ZEND_SR:
74 				if (ZEND_OP1_TYPE(opline) == IS_CONST) {
75 					if (Z_TYPE(ZEND_OP1_LITERAL(opline)) != IS_LONG) {
76 						convert_to_long(&ZEND_OP1_LITERAL(opline));
77 					}
78 				}
79 				/* break missing *intentionally - the assign_op's may only optimize op2 */
80 			case ZEND_ASSIGN_MOD:
81 			case ZEND_ASSIGN_SL:
82 			case ZEND_ASSIGN_SR:
83 				if (opline->extended_value != 0) {
84 					/* object tristate op - don't attempt to optimize it! */
85 					break;
86 				}
87 				if (ZEND_OP2_TYPE(opline) == IS_CONST) {
88 					if (Z_TYPE(ZEND_OP2_LITERAL(opline)) != IS_LONG) {
89 						convert_to_long(&ZEND_OP2_LITERAL(opline));
90 					}
91 				}
92 				break;
93 
94 			case ZEND_CONCAT:
95 			case ZEND_FAST_CONCAT:
96 				if (ZEND_OP1_TYPE(opline) == IS_CONST) {
97 					if (Z_TYPE(ZEND_OP1_LITERAL(opline)) != IS_STRING) {
98 						convert_to_string(&ZEND_OP1_LITERAL(opline));
99 					}
100 				}
101 				/* break missing *intentionally - the assign_op's may only optimize op2 */
102 			case ZEND_ASSIGN_CONCAT:
103 				if (opline->extended_value != 0) {
104 					/* object tristate op - don't attempt to optimize it! */
105 					break;
106 				}
107 				if (ZEND_OP2_TYPE(opline) == IS_CONST) {
108 					if (Z_TYPE(ZEND_OP2_LITERAL(opline)) != IS_STRING) {
109 						convert_to_string(&ZEND_OP2_LITERAL(opline));
110 					}
111 				}
112 				break;
113 
114 			case ZEND_JMPZ_EX:
115 			case ZEND_JMPNZ_EX:
116 				/* convert Ti = JMPZ_EX(Ti, L) to JMPZ(Ti, L) */
117 				if (0 && /* FIXME: temporary disable unsafe pattern */
118 				    ZEND_OP1_TYPE(opline) == IS_TMP_VAR &&
119 				    ZEND_RESULT_TYPE(opline) == IS_TMP_VAR &&
120 				    ZEND_OP1(opline).var == ZEND_RESULT(opline).var) {
121 					opline->opcode -= 3;
122 				/* convert Ti = JMPZ_EX(C, L) => Ti = QM_ASSIGN(C)
123 				   in case we know it wouldn't jump */
124 				} else if (ZEND_OP1_TYPE(opline) == IS_CONST) {
125 					int should_jmp = zend_is_true(&ZEND_OP1_LITERAL(opline));
126 					if (opline->opcode == ZEND_JMPZ_EX) {
127 						should_jmp = !should_jmp;
128 					}
129 					if (!should_jmp) {
130 						opline->opcode = ZEND_QM_ASSIGN;
131 						SET_UNUSED(opline->op2);
132 					}
133 				}
134 				break;
135 
136 			case ZEND_JMPZ:
137 			case ZEND_JMPNZ:
138 				if (ZEND_OP1_TYPE(opline) == IS_CONST) {
139 					int should_jmp = zend_is_true(&ZEND_OP1_LITERAL(opline));
140 
141 					if (opline->opcode == ZEND_JMPZ) {
142 						should_jmp = !should_jmp;
143 					}
144 					literal_dtor(&ZEND_OP1_LITERAL(opline));
145 					ZEND_OP1_TYPE(opline) = IS_UNUSED;
146 					if (should_jmp) {
147 						opline->opcode = ZEND_JMP;
148 						COPY_NODE(opline->op1, opline->op2);
149 					} else {
150 						MAKE_NOP(opline);
151 					}
152 					break;
153 				}
154 				if ((opline + 1)->opcode == ZEND_JMP) {
155 					/* JMPZ(X, L1), JMP(L2) => JMPZNZ(X, L1, L2) */
156 					/* JMPNZ(X, L1), JMP(L2) => JMPZNZ(X, L2, L1) */
157 					if (ZEND_OP2(opline).opline_num == ZEND_OP1(opline + 1).opline_num) {
158 						/* JMPZ(X, L1), JMP(L1) => NOP, JMP(L1) */
159 						if (opline->op1_type == IS_CV) {
160 							break;
161 						} else if (opline->op1_type & (IS_TMP_VAR|IS_VAR)) {
162 							opline->opcode = ZEND_FREE;
163 							opline->op2.num = 0;
164 						} else {
165 							MAKE_NOP(opline);
166 						}
167 					} else {
168 						if (opline->opcode == ZEND_JMPZ) {
169 							opline->extended_value = ZEND_OP1(opline + 1).opline_num;
170 						} else {
171 							opline->extended_value = ZEND_OP2(opline).opline_num;
172 							COPY_NODE(opline->op2, (opline + 1)->op1);
173 						}
174 						opline->opcode = ZEND_JMPZNZ;
175 					}
176 				}
177 				break;
178 
179 			case ZEND_JMPZNZ:
180 				if (ZEND_OP1_TYPE(opline) == IS_CONST) {
181 					int opline_num;
182 					if (zend_is_true(&ZEND_OP1_LITERAL(opline))) {
183 						opline_num = opline->extended_value; /* JMPNZ */
184 					} else {
185 						opline_num = ZEND_OP2(opline).opline_num; /* JMPZ */
186 					}
187 					literal_dtor(&ZEND_OP1_LITERAL(opline));
188 					ZEND_OP1(opline).opline_num = opline_num;
189 					ZEND_OP1_TYPE(opline) = IS_UNUSED;
190 					opline->opcode = ZEND_JMP;
191 				}
192 				break;
193 		}
194 		opline++;
195 	}
196 }
197