xref: /PHP-5.5/ext/opcache/Optimizer/pass2.c (revision aa394e70)
1 /* pass 2:
2  * - convert non-numeric constants to numeric constants in numeric operators
3  * - optimize constant conditional JMPs
4  * - optimize static BRKs and CONTs
5  */
6 
7 if (ZEND_OPTIMIZER_PASS_2 & OPTIMIZATION_LEVEL) {
8 	zend_op *opline;
9 	zend_op *end = op_array->opcodes + op_array->last;
10 
11 	opline = op_array->opcodes;
12 	while (opline < end) {
13 		switch (opline->opcode) {
14 			case ZEND_ADD:
15 			case ZEND_SUB:
16 			case ZEND_MUL:
17 			case ZEND_DIV:
18 				if (ZEND_OP1_TYPE(opline) == IS_CONST) {
19 					if (ZEND_OP1_LITERAL(opline).type == IS_STRING) {
20 						convert_scalar_to_number(&ZEND_OP1_LITERAL(opline) TSRMLS_CC);
21 					}
22 				}
23 				/* break missing *intentionally* - the assign_op's may only optimize op2 */
24 			case ZEND_ASSIGN_ADD:
25 			case ZEND_ASSIGN_SUB:
26 			case ZEND_ASSIGN_MUL:
27 			case ZEND_ASSIGN_DIV:
28 				if (opline->extended_value != 0) {
29 					/* object tristate op - don't attempt to optimize it! */
30 					break;
31 				}
32 				if (ZEND_OP2_TYPE(opline) == IS_CONST) {
33 					if (ZEND_OP2_LITERAL(opline).type == IS_STRING) {
34 						convert_scalar_to_number(&ZEND_OP2_LITERAL(opline) TSRMLS_CC);
35 					}
36 				}
37 				break;
38 
39 			case ZEND_MOD:
40 			case ZEND_SL:
41 			case ZEND_SR:
42 				if (ZEND_OP1_TYPE(opline) == IS_CONST) {
43 					if (ZEND_OP1_LITERAL(opline).type != IS_LONG) {
44 						convert_to_long(&ZEND_OP1_LITERAL(opline));
45 					}
46 				}
47 				/* break missing *intentionally - the assign_op's may only optimize op2 */
48 			case ZEND_ASSIGN_MOD:
49 			case ZEND_ASSIGN_SL:
50 			case ZEND_ASSIGN_SR:
51 				if (opline->extended_value != 0) {
52 					/* object tristate op - don't attempt to optimize it! */
53 					break;
54 				}
55 				if (ZEND_OP2_TYPE(opline) == IS_CONST) {
56 					if (ZEND_OP2_LITERAL(opline).type != IS_LONG) {
57 						convert_to_long(&ZEND_OP2_LITERAL(opline));
58 					}
59 				}
60 				break;
61 
62 			case ZEND_CONCAT:
63 				if (ZEND_OP1_TYPE(opline) == IS_CONST) {
64 					if (ZEND_OP1_LITERAL(opline).type != IS_STRING) {
65 						convert_to_string(&ZEND_OP1_LITERAL(opline));
66 					}
67 				}
68 				/* break missing *intentionally - the assign_op's may only optimize op2 */
69 			case ZEND_ASSIGN_CONCAT:
70 				if (opline->extended_value != 0) {
71 					/* object tristate op - don't attempt to optimize it! */
72 					break;
73 				}
74 				if (ZEND_OP2_TYPE(opline) == IS_CONST) {
75 					if (ZEND_OP2_LITERAL(opline).type != IS_STRING) {
76 						convert_to_string(&ZEND_OP2_LITERAL(opline));
77 					}
78 				}
79 				break;
80 
81 			case ZEND_JMPZ_EX:
82 			case ZEND_JMPNZ_EX:
83 				/* convert Ti = JMPZ_EX(Ti, L) to JMPZ(Ti, L) */
84 				if (0 && /* FIXME: temporary disable unsafe pattern */
85 				    ZEND_OP1_TYPE(opline) == IS_TMP_VAR &&
86 				    ZEND_RESULT_TYPE(opline) == IS_TMP_VAR &&
87 				    ZEND_OP1(opline).var == ZEND_RESULT(opline).var) {
88 					opline->opcode -= 3;
89 				/* convert Ti = JMPZ_EX(C, L) => Ti = QM_ASSIGN(C)
90 				   in case we know it wouldn't jump */
91 				} else if (ZEND_OP1_TYPE(opline) == IS_CONST) {
92 					int should_jmp = zend_is_true(&ZEND_OP1_LITERAL(opline));
93 					if (opline->opcode == ZEND_JMPZ_EX) {
94 						should_jmp = !should_jmp;
95 					}
96 					if (!should_jmp) {
97 						opline->opcode = ZEND_QM_ASSIGN;
98 						SET_UNUSED(opline->op2);
99 					}
100 				}
101 				break;
102 
103 			case ZEND_JMPZ:
104 			case ZEND_JMPNZ:
105 				if (ZEND_OP1_TYPE(opline) == IS_CONST) {
106 					int should_jmp = zend_is_true(&ZEND_OP1_LITERAL(opline));
107 
108 					if (opline->opcode == ZEND_JMPZ) {
109 						should_jmp = !should_jmp;
110 					}
111 					literal_dtor(&ZEND_OP1_LITERAL(opline));
112 					ZEND_OP1_TYPE(opline) = IS_UNUSED;
113 					if (should_jmp) {
114 						opline->opcode = ZEND_JMP;
115 						COPY_NODE(opline->op1, opline->op2);
116 					} else {
117 						MAKE_NOP(opline);
118 					}
119 					break;
120 				}
121 				if ((opline + 1)->opcode == ZEND_JMP) {
122 					/* JMPZ(X, L1), JMP(L2) => JMPZNZ(X, L1, L2) */
123 					/* JMPNZ(X, L1), JMP(L2) => JMPZNZ(X, L2, L1) */
124 					if (ZEND_OP2(opline).opline_num == ZEND_OP1(opline + 1).opline_num) {
125 						/* JMPZ(X, L1), JMP(L1) => NOP, JMP(L1) */
126 						MAKE_NOP(opline);
127 					} else {
128 						if (opline->opcode == ZEND_JMPZ) {
129 							opline->extended_value = ZEND_OP1(opline + 1).opline_num;
130 						} else {
131 							opline->extended_value = ZEND_OP2(opline).opline_num;
132 							COPY_NODE(opline->op2, (opline + 1)->op1);
133 						}
134 						opline->opcode = ZEND_JMPZNZ;
135 					}
136 				}
137 				break;
138 
139 			case ZEND_JMPZNZ:
140 				if (ZEND_OP1_TYPE(opline) == IS_CONST) {
141 					int opline_num;
142 
143 					if (zend_is_true(&ZEND_OP1_LITERAL(opline))) {
144 						opline_num = opline->extended_value; /* JMPNZ */
145 					} else {
146 						opline_num = ZEND_OP2(opline).opline_num; /* JMPZ */
147 					}
148 					literal_dtor(&ZEND_OP1_LITERAL(opline));
149 					ZEND_OP1(opline).opline_num = opline_num;
150 					ZEND_OP1_TYPE(opline) = IS_UNUSED;
151 					opline->opcode = ZEND_JMP;
152 				}
153 				break;
154 
155 			case ZEND_BRK:
156 			case ZEND_CONT:
157 				{
158 				    zend_brk_cont_element *jmp_to;
159 					int array_offset;
160 					int nest_levels;
161 					int dont_optimize = 0;
162 
163 					if (ZEND_OP2_TYPE(opline) != IS_CONST) {
164 						break;
165 					}
166 					convert_to_long(&ZEND_OP2_LITERAL(opline));
167 					nest_levels = ZEND_OP2_LITERAL(opline).value.lval;
168 
169 					array_offset = ZEND_OP1(opline).opline_num;
170 					while (1) {
171 						if (array_offset == -1) {
172 							dont_optimize = 1; /* don't optimize this bogus break/continue, let the executor shout */
173 							break;
174 						}
175 						jmp_to = &op_array->brk_cont_array[array_offset];
176 						array_offset = jmp_to->parent;
177 						if (--nest_levels > 0) {
178 							if (op_array->opcodes[jmp_to->brk].opcode == ZEND_FREE ||
179 							    op_array->opcodes[jmp_to->brk].opcode == ZEND_SWITCH_FREE
180 							) {
181 								dont_optimize = 1;
182 								break;
183 							}
184 						} else {
185 							break;
186 						}
187 					}
188 
189 					if (dont_optimize) {
190 						break;
191 					}
192 
193 					/* optimize - convert to a JMP */
194 					switch (opline->opcode) {
195 						case ZEND_BRK:
196 							MAKE_NOP(opline);
197 							ZEND_OP1(opline).opline_num = jmp_to->brk;
198 							break;
199 						case ZEND_CONT:
200 							MAKE_NOP(opline);
201 							ZEND_OP1(opline).opline_num = jmp_to->cont;
202 							break;
203 					}
204 					opline->opcode = ZEND_JMP;
205 					/* MAKE_NOP() already set op1 and op2 to IS_UNUSED */
206 				}
207 				break;
208 		}
209 		opline++;
210 	}
211 }
212