xref: /PHP-8.1/Zend/Optimizer/zend_inference.h (revision 1ffbb737)
1 /*
2    +----------------------------------------------------------------------+
3    | Zend Engine, e-SSA based Type & Range Inference                      |
4    +----------------------------------------------------------------------+
5    | Copyright (c) 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    | https://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: Dmitry Stogov <dmitry@php.net>                              |
16    +----------------------------------------------------------------------+
17 */
18 
19 #ifndef ZEND_INFERENCE_H
20 #define ZEND_INFERENCE_H
21 
22 #include "zend_optimizer.h"
23 #include "zend_ssa.h"
24 #include "zend_bitset.h"
25 
26 /* Bitmask for type inference (zend_ssa_var_info.type) */
27 #include "zend_type_info.h"
28 
29 #define MAY_BE_PACKED_GUARD         (1<<27) /* needs packed array guard */
30 #define MAY_BE_CLASS_GUARD          (1<<27) /* needs class guard */
31 #define MAY_BE_GUARD                (1<<28) /* needs type guard */
32 //#define MAY_BE_IN_REG               (1<<29) /* deprecated and not used */
33 
34 #define MAY_HAVE_DTOR \
35 	(MAY_BE_OBJECT|MAY_BE_RESOURCE \
36 	|MAY_BE_ARRAY_OF_ARRAY|MAY_BE_ARRAY_OF_OBJECT|MAY_BE_ARRAY_OF_RESOURCE)
37 
38 #define DEFINE_SSA_OP_HAS_RANGE(opN) \
39 	static zend_always_inline bool _ssa_##opN##_has_range(const zend_op_array *op_array, const zend_ssa *ssa, const zend_op *opline, const zend_ssa_op *ssa_op) \
40 	{ \
41 		if (opline->opN##_type == IS_CONST) { \
42 			zval *zv = CRT_CONSTANT(opline->opN); \
43 			return (Z_TYPE_P(zv) == IS_LONG || Z_TYPE_P(zv) == IS_TRUE || Z_TYPE_P(zv) == IS_FALSE || Z_TYPE_P(zv) == IS_NULL); \
44 		} else { \
45 			return (opline->opN##_type != IS_UNUSED && \
46 		        ssa->var_info && \
47 		        ssa_op->opN##_use >= 0 && \
48 			    ssa->var_info[ssa_op->opN##_use].has_range); \
49 		} \
50 		return 0; \
51 	} \
52 
53 #define DEFINE_SSA_OP_MIN_RANGE(opN) \
54 	static zend_always_inline zend_long _ssa_##opN##_min_range(const zend_op_array *op_array, const zend_ssa *ssa, const zend_op *opline, const zend_ssa_op *ssa_op) \
55 	{ \
56 		if (opline->opN##_type == IS_CONST) { \
57 			zval *zv = CRT_CONSTANT(opline->opN); \
58 			if (Z_TYPE_P(zv) == IS_LONG) { \
59 				return Z_LVAL_P(zv); \
60 			} else if (Z_TYPE_P(zv) == IS_TRUE) { \
61 				return 1; \
62 			} else if (Z_TYPE_P(zv) == IS_FALSE) { \
63 				return 0; \
64 			} else if (Z_TYPE_P(zv) == IS_NULL) { \
65 				return 0; \
66 			} \
67 		} else if (opline->opN##_type != IS_UNUSED && \
68 		    ssa->var_info && \
69 		    ssa_op->opN##_use >= 0 && \
70 		    ssa->var_info[ssa_op->opN##_use].has_range) { \
71 			return ssa->var_info[ssa_op->opN##_use].range.min; \
72 		} \
73 		return ZEND_LONG_MIN; \
74 	} \
75 
76 #define DEFINE_SSA_OP_MAX_RANGE(opN) \
77 	static zend_always_inline zend_long _ssa_##opN##_max_range(const zend_op_array *op_array, const zend_ssa *ssa, const zend_op *opline, const zend_ssa_op *ssa_op) \
78 	{ \
79 		if (opline->opN##_type == IS_CONST) { \
80 			zval *zv = CRT_CONSTANT(opline->opN); \
81 			if (Z_TYPE_P(zv) == IS_LONG) { \
82 				return Z_LVAL_P(zv); \
83 			} else if (Z_TYPE_P(zv) == IS_TRUE) { \
84 				return 1; \
85 			} else if (Z_TYPE_P(zv) == IS_FALSE) { \
86 				return 0; \
87 			} else if (Z_TYPE_P(zv) == IS_NULL) { \
88 				return 0; \
89 			} \
90 		} else if (opline->opN##_type != IS_UNUSED && \
91 		    ssa->var_info && \
92 		    ssa_op->opN##_use >= 0 && \
93 		    ssa->var_info[ssa_op->opN##_use].has_range) { \
94 			return ssa->var_info[ssa_op->opN##_use].range.max; \
95 		} \
96 		return ZEND_LONG_MAX; \
97 	} \
98 
99 #define DEFINE_SSA_OP_RANGE_UNDERFLOW(opN) \
100 	static zend_always_inline char _ssa_##opN##_range_underflow(const zend_op_array *op_array, const zend_ssa *ssa, const zend_op *opline, const zend_ssa_op *ssa_op) \
101 	{ \
102 		if (opline->opN##_type == IS_CONST) { \
103 			zval *zv = CRT_CONSTANT(opline->opN); \
104 			if (Z_TYPE_P(zv) == IS_LONG || Z_TYPE_P(zv) == IS_TRUE || Z_TYPE_P(zv) == IS_FALSE || Z_TYPE_P(zv) == IS_NULL) { \
105 				return 0; \
106 			} \
107 		} else if (opline->opN##_type != IS_UNUSED && \
108 		    ssa->var_info && \
109 		    ssa_op->opN##_use >= 0 && \
110 		    ssa->var_info[ssa_op->opN##_use].has_range) { \
111 			return ssa->var_info[ssa_op->opN##_use].range.underflow; \
112 		} \
113 		return 1; \
114 	} \
115 
116 #define DEFINE_SSA_OP_RANGE_OVERFLOW(opN) \
117 	static zend_always_inline char _ssa_##opN##_range_overflow(const zend_op_array *op_array, const zend_ssa *ssa, const zend_op *opline, const zend_ssa_op *ssa_op) \
118 	{ \
119 		if (opline->opN##_type == IS_CONST) { \
120 			zval *zv = CRT_CONSTANT(opline->opN); \
121 			if (Z_TYPE_P(zv) == IS_LONG || Z_TYPE_P(zv) == IS_TRUE || Z_TYPE_P(zv) == IS_FALSE || Z_TYPE_P(zv) == IS_NULL) { \
122 				return 0; \
123 			} \
124 		} else if (opline->opN##_type != IS_UNUSED && \
125 		    ssa->var_info && \
126 		    ssa_op->opN##_use >= 0 && \
127 		    ssa->var_info[ssa_op->opN##_use].has_range) { \
128 			return ssa->var_info[ssa_op->opN##_use].range.overflow; \
129 		} \
130 		return 1; \
131 	} \
132 
133 DEFINE_SSA_OP_HAS_RANGE(op1)
DEFINE_SSA_OP_MIN_RANGE(op1)134 DEFINE_SSA_OP_MIN_RANGE(op1)
135 DEFINE_SSA_OP_MAX_RANGE(op1)
136 DEFINE_SSA_OP_RANGE_UNDERFLOW(op1)
137 DEFINE_SSA_OP_RANGE_OVERFLOW(op1)
138 DEFINE_SSA_OP_HAS_RANGE(op2)
139 DEFINE_SSA_OP_MIN_RANGE(op2)
140 DEFINE_SSA_OP_MAX_RANGE(op2)
141 DEFINE_SSA_OP_RANGE_UNDERFLOW(op2)
142 DEFINE_SSA_OP_RANGE_OVERFLOW(op2)
143 
144 #define OP1_HAS_RANGE()       (_ssa_op1_has_range (op_array, ssa, opline, ssa_op))
145 #define OP1_MIN_RANGE()       (_ssa_op1_min_range (op_array, ssa, opline, ssa_op))
146 #define OP1_MAX_RANGE()       (_ssa_op1_max_range (op_array, ssa, opline, ssa_op))
147 #define OP1_RANGE_UNDERFLOW() (_ssa_op1_range_underflow (op_array, ssa, opline, ssa_op))
148 #define OP1_RANGE_OVERFLOW()  (_ssa_op1_range_overflow (op_array, ssa, opline, ssa_op))
149 #define OP2_HAS_RANGE()       (_ssa_op2_has_range (op_array, ssa, opline, ssa_op))
150 #define OP2_MIN_RANGE()       (_ssa_op2_min_range (op_array, ssa, opline, ssa_op))
151 #define OP2_MAX_RANGE()       (_ssa_op2_max_range (op_array, ssa, opline, ssa_op))
152 #define OP2_RANGE_UNDERFLOW() (_ssa_op2_range_underflow (op_array, ssa, opline, ssa_op))
153 #define OP2_RANGE_OVERFLOW()  (_ssa_op2_range_overflow (op_array, ssa, opline, ssa_op))
154 
155 static zend_always_inline uint32_t _const_op_type(const zval *zv) {
156 	if (Z_TYPE_P(zv) == IS_CONSTANT_AST) {
157 		return MAY_BE_RC1 | MAY_BE_RCN | MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY;
158 	} else if (Z_TYPE_P(zv) == IS_ARRAY) {
159 		HashTable *ht = Z_ARRVAL_P(zv);
160 		uint32_t tmp = MAY_BE_ARRAY;
161 		zend_string *str;
162 		zval *val;
163 
164 		if (Z_REFCOUNTED_P(zv)) {
165 			tmp |= MAY_BE_RC1 | MAY_BE_RCN;
166 		} else {
167 			tmp |= MAY_BE_RCN;
168 		}
169 
170 		ZEND_HASH_FOREACH_STR_KEY_VAL(ht, str, val) {
171 			if (str) {
172 				tmp |= MAY_BE_ARRAY_KEY_STRING;
173 			} else {
174 				tmp |= MAY_BE_ARRAY_KEY_LONG;
175 			}
176 			tmp |= 1 << (Z_TYPE_P(val) + MAY_BE_ARRAY_SHIFT);
177 		} ZEND_HASH_FOREACH_END();
178 		if (HT_IS_PACKED(ht)) {
179 			tmp &= ~(MAY_BE_ARRAY_NUMERIC_HASH|MAY_BE_ARRAY_STRING_HASH);
180 		}
181 		return tmp;
182 	} else {
183 		uint32_t tmp = (1 << Z_TYPE_P(zv));
184 
185 		if (Z_REFCOUNTED_P(zv)) {
186 			tmp |= MAY_BE_RC1 | MAY_BE_RCN;
187 		} else if (Z_TYPE_P(zv) == IS_STRING) {
188 			tmp |= MAY_BE_RCN;
189 		}
190 		return tmp;
191 	}
192 }
193 
get_ssa_var_info(const zend_ssa * ssa,int ssa_var_num)194 static zend_always_inline uint32_t get_ssa_var_info(const zend_ssa *ssa, int ssa_var_num)
195 {
196 	if (ssa->var_info && ssa_var_num >= 0) {
197 		return ssa->var_info[ssa_var_num].type;
198 	} else {
199 		return MAY_BE_UNDEF | MAY_BE_RC1 | MAY_BE_RCN | MAY_BE_REF | MAY_BE_INDIRECT | MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF;
200 	}
201 }
202 
203 #define DEFINE_SSA_OP_INFO(opN) \
204 	static zend_always_inline uint32_t _ssa_##opN##_info(const zend_op_array *op_array, const zend_ssa *ssa, const zend_op *opline, const zend_ssa_op *ssa_op) \
205 	{																		\
206 		if (opline->opN##_type == IS_CONST) {							\
207 			return _const_op_type(CRT_CONSTANT(opline->opN)); \
208 		} else { \
209 			return get_ssa_var_info(ssa, ssa->var_info ? ssa_op->opN##_use : -1); \
210 		} \
211 	} \
212 
213 #define DEFINE_SSA_OP_DEF_INFO(opN) \
214 	static zend_always_inline uint32_t _ssa_##opN##_def_info(const zend_op_array *op_array, const zend_ssa *ssa, const zend_op *opline, const zend_ssa_op *ssa_op) \
215 	{ \
216 		return get_ssa_var_info(ssa, ssa->var_info ? ssa_op->opN##_def : -1); \
217 	} \
218 
219 
220 DEFINE_SSA_OP_INFO(op1)
DEFINE_SSA_OP_INFO(op2)221 DEFINE_SSA_OP_INFO(op2)
222 DEFINE_SSA_OP_INFO(result)
223 DEFINE_SSA_OP_DEF_INFO(op1)
224 DEFINE_SSA_OP_DEF_INFO(op2)
225 DEFINE_SSA_OP_DEF_INFO(result)
226 
227 #define OP1_INFO()           (_ssa_op1_info(op_array, ssa, opline, ssa_op))
228 #define OP2_INFO()           (_ssa_op2_info(op_array, ssa, opline, ssa_op))
229 #define OP1_DATA_INFO()      (_ssa_op1_info(op_array, ssa, (opline+1), (ssa_op+1)))
230 #define OP2_DATA_INFO()      (_ssa_op2_info(op_array, ssa, (opline+1), (ssa_op+1)))
231 #define RES_USE_INFO()       (_ssa_result_info(op_array, ssa, opline, ssa_op))
232 #define OP1_DEF_INFO()       (_ssa_op1_def_info(op_array, ssa, opline, ssa_op))
233 #define OP2_DEF_INFO()       (_ssa_op2_def_info(op_array, ssa, opline, ssa_op))
234 #define OP1_DATA_DEF_INFO()  (_ssa_op1_def_info(op_array, ssa, (opline+1), (ssa_op+1)))
235 #define OP2_DATA_DEF_INFO()  (_ssa_op2_def_info(op_array, ssa, (opline+1), (ssa_op+1)))
236 #define RES_INFO()           (_ssa_result_def_info(op_array, ssa, opline, ssa_op))
237 
238 static zend_always_inline bool zend_add_will_overflow(zend_long a, zend_long b) {
239 	return (b > 0 && a > ZEND_LONG_MAX - b)
240 		|| (b < 0 && a < ZEND_LONG_MIN - b);
241 }
zend_sub_will_overflow(zend_long a,zend_long b)242 static zend_always_inline bool zend_sub_will_overflow(zend_long a, zend_long b) {
243 	return (b > 0 && a < ZEND_LONG_MIN + b)
244 		|| (b < 0 && a > ZEND_LONG_MAX + b);
245 }
246 
247 BEGIN_EXTERN_C()
248 
249 ZEND_API int zend_ssa_find_false_dependencies(const zend_op_array *op_array, zend_ssa *ssa);
250 ZEND_API int zend_ssa_find_sccs(const zend_op_array *op_array, zend_ssa *ssa);
251 ZEND_API int zend_ssa_inference(zend_arena **raena, const zend_op_array *op_array, const zend_script *script, zend_ssa *ssa, zend_long optimization_level);
252 
253 ZEND_API uint32_t zend_array_element_type(uint32_t t1, zend_uchar op_type, int write, int insert);
254 
255 ZEND_API int zend_inference_propagate_range(const zend_op_array *op_array, zend_ssa *ssa, zend_op *opline, zend_ssa_op* ssa_op, int var, zend_ssa_range *tmp);
256 
257 ZEND_API uint32_t zend_fetch_arg_info_type(
258 	const zend_script *script, zend_arg_info *arg_info, zend_class_entry **pce);
259 ZEND_API void zend_init_func_return_info(
260 	const zend_op_array *op_array, const zend_script *script, zend_ssa_var_info *ret);
261 uint32_t zend_get_return_info_from_signature_only(
262 		const zend_function *func, const zend_script *script,
263 		zend_class_entry **ce, bool *ce_is_instanceof, bool use_tentative_return_info);
264 
265 ZEND_API int zend_may_throw_ex(const zend_op *opline, const zend_ssa_op *ssa_op, const zend_op_array *op_array, zend_ssa *ssa, uint32_t t1, uint32_t t2);
266 ZEND_API int zend_may_throw(const zend_op *opline, const zend_ssa_op *ssa_op, const zend_op_array *op_array, zend_ssa *ssa);
267 
268 ZEND_API int zend_update_type_info(
269 	const zend_op_array *op_array, zend_ssa *ssa, const zend_script *script,
270 	zend_op *opline, zend_ssa_op *ssa_op, const zend_op **ssa_opcodes,
271 	zend_long optimization_level);
272 
273 END_EXTERN_C()
274 
275 #endif /* ZEND_INFERENCE_H */
276