xref: /PHP-7.1/ext/opcache/Optimizer/nop_removal.c (revision ccd4716e)
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 10:
23  * - remove NOPs
24  */
25 
26 #include "php.h"
27 #include "Optimizer/zend_optimizer.h"
28 #include "Optimizer/zend_optimizer_internal.h"
29 #include "zend_API.h"
30 #include "zend_constants.h"
31 #include "zend_execute.h"
32 #include "zend_vm.h"
33 
zend_optimizer_nop_removal(zend_op_array * op_array)34 void zend_optimizer_nop_removal(zend_op_array *op_array)
35 {
36 	zend_op *end, *opline;
37 	uint32_t new_count, i, shift;
38 	int j;
39 	uint32_t *shiftlist;
40 	ALLOCA_FLAG(use_heap);
41 
42 	shiftlist = (uint32_t *)do_alloca(sizeof(uint32_t) * op_array->last, use_heap);
43 	i = new_count = shift = 0;
44 	end = op_array->opcodes + op_array->last;
45 	for (opline = op_array->opcodes; opline < end; opline++) {
46 
47 		/* Kill JMP-over-NOP-s */
48 		if (opline->opcode == ZEND_JMP && ZEND_OP1_JMP_ADDR(opline) > op_array->opcodes + i) {
49 			/* check if there are only NOPs under the branch */
50 			zend_op *target = ZEND_OP1_JMP_ADDR(opline) - 1;
51 
52 			while (target->opcode == ZEND_NOP) {
53 				target--;
54 			}
55 			if (target == opline) {
56 				/* only NOPs */
57 				opline->opcode = ZEND_NOP;
58 			}
59 		}
60 
61 		shiftlist[i++] = shift;
62 		if (opline->opcode == ZEND_NOP) {
63 			shift++;
64 		} else {
65 			if (shift) {
66 				zend_op *new_opline = op_array->opcodes + new_count;
67 
68 				*new_opline = *opline;
69 				switch (new_opline->opcode) {
70 					case ZEND_JMP:
71 					case ZEND_FAST_CALL:
72 						ZEND_SET_OP_JMP_ADDR(new_opline, new_opline->op1, ZEND_OP1_JMP_ADDR(opline));
73 						break;
74 					case ZEND_JMPZNZ:
75 						new_opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, new_opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value));
76 						/* break missing intentionally */
77 					case ZEND_JMPZ:
78 					case ZEND_JMPNZ:
79 					case ZEND_JMPZ_EX:
80 					case ZEND_JMPNZ_EX:
81 					case ZEND_FE_RESET_R:
82 					case ZEND_FE_RESET_RW:
83 					case ZEND_JMP_SET:
84 					case ZEND_COALESCE:
85 					case ZEND_ASSERT_CHECK:
86 						ZEND_SET_OP_JMP_ADDR(new_opline, new_opline->op2, ZEND_OP2_JMP_ADDR(opline));
87 						break;
88 					case ZEND_CATCH:
89 						if (!opline->result.num) {
90 							new_opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, new_opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value));
91 						}
92 						break;
93 					case ZEND_DECLARE_ANON_CLASS:
94 					case ZEND_DECLARE_ANON_INHERITED_CLASS:
95 					case ZEND_FE_FETCH_R:
96 					case ZEND_FE_FETCH_RW:
97 						new_opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, new_opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value));
98 						break;
99 				}
100 			}
101 			new_count++;
102 		}
103 	}
104 
105 	if (shift) {
106 		op_array->last = new_count;
107 		end = op_array->opcodes + op_array->last;
108 
109 		/* update JMPs */
110 		for (opline = op_array->opcodes; opline<end; opline++) {
111 			switch (opline->opcode) {
112 				case ZEND_JMP:
113 				case ZEND_FAST_CALL:
114 					ZEND_SET_OP_JMP_ADDR(opline, opline->op1, ZEND_OP1_JMP_ADDR(opline) - shiftlist[ZEND_OP1_JMP_ADDR(opline) - op_array->opcodes]);
115 					break;
116 				case ZEND_JMPZNZ:
117 					opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value) - shiftlist[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)]);
118 					/* break missing intentionally */
119 				case ZEND_JMPZ:
120 				case ZEND_JMPNZ:
121 				case ZEND_JMPZ_EX:
122 				case ZEND_JMPNZ_EX:
123 				case ZEND_FE_RESET_R:
124 				case ZEND_FE_RESET_RW:
125 				case ZEND_JMP_SET:
126 				case ZEND_COALESCE:
127 				case ZEND_ASSERT_CHECK:
128 					ZEND_SET_OP_JMP_ADDR(opline, opline->op2, ZEND_OP2_JMP_ADDR(opline) - shiftlist[ZEND_OP2_JMP_ADDR(opline) - op_array->opcodes]);
129 					break;
130 				case ZEND_DECLARE_ANON_CLASS:
131 				case ZEND_DECLARE_ANON_INHERITED_CLASS:
132 				case ZEND_FE_FETCH_R:
133 				case ZEND_FE_FETCH_RW:
134 				case ZEND_CATCH:
135 					opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value) - shiftlist[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)]);
136 					break;
137 			}
138 		}
139 
140 		/* update brk/cont array */
141 		for (j = 0; j < op_array->last_live_range; j++) {
142 			op_array->live_range[j].start -= shiftlist[op_array->live_range[j].start];
143 			op_array->live_range[j].end   -= shiftlist[op_array->live_range[j].end];
144 		}
145 
146 		/* update try/catch array */
147 		for (j = 0; j < op_array->last_try_catch; j++) {
148 			op_array->try_catch_array[j].try_op -= shiftlist[op_array->try_catch_array[j].try_op];
149 			op_array->try_catch_array[j].catch_op -= shiftlist[op_array->try_catch_array[j].catch_op];
150 			if (op_array->try_catch_array[j].finally_op) {
151 				op_array->try_catch_array[j].finally_op -= shiftlist[op_array->try_catch_array[j].finally_op];
152 				op_array->try_catch_array[j].finally_end -= shiftlist[op_array->try_catch_array[j].finally_end];
153 			}
154 		}
155 
156 		/* update early binding list */
157 		if (op_array->early_binding != (uint32_t)-1) {
158 			uint32_t *opline_num = &op_array->early_binding;
159 
160 			do {
161 				*opline_num -= shiftlist[*opline_num];
162 				opline_num = &ZEND_RESULT(&op_array->opcodes[*opline_num]).opline_num;
163 			} while (*opline_num != (uint32_t)-1);
164 		}
165 	}
166 	free_alloca(shiftlist, use_heap);
167 }
168