xref: /PHP-8.0/Zend/zend_opcode.c (revision 0d7e10c1)
1 /*
2    +----------------------------------------------------------------------+
3    | Zend Engine                                                          |
4    +----------------------------------------------------------------------+
5    | Copyright (c) Zend Technologies Ltd. (http://www.zend.com)           |
6    +----------------------------------------------------------------------+
7    | This source file is subject to version 2.00 of the Zend 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.zend.com/license/2_00.txt.                                |
11    | If you did not receive a copy of the Zend license and are unable to  |
12    | obtain it through the world-wide-web, please send a note to          |
13    | license@zend.com so we can mail you a copy immediately.              |
14    +----------------------------------------------------------------------+
15    | Authors: Andi Gutmans <andi@php.net>                                 |
16    |          Zeev Suraski <zeev@php.net>                                 |
17    |          Dmitry Stogov <dmitry@php.net>                              |
18    +----------------------------------------------------------------------+
19 */
20 
21 #include <stdio.h>
22 
23 #include "zend.h"
24 #include "zend_alloc.h"
25 #include "zend_compile.h"
26 #include "zend_extensions.h"
27 #include "zend_API.h"
28 #include "zend_sort.h"
29 
30 #include "zend_vm.h"
31 
zend_extension_op_array_ctor_handler(zend_extension * extension,zend_op_array * op_array)32 static void zend_extension_op_array_ctor_handler(zend_extension *extension, zend_op_array *op_array)
33 {
34 	if (extension->op_array_ctor) {
35 		extension->op_array_ctor(op_array);
36 	}
37 }
38 
zend_extension_op_array_dtor_handler(zend_extension * extension,zend_op_array * op_array)39 static void zend_extension_op_array_dtor_handler(zend_extension *extension, zend_op_array *op_array)
40 {
41 	if (extension->op_array_dtor) {
42 		extension->op_array_dtor(op_array);
43 	}
44 }
45 
init_op_array(zend_op_array * op_array,zend_uchar type,int initial_ops_size)46 void init_op_array(zend_op_array *op_array, zend_uchar type, int initial_ops_size)
47 {
48 	op_array->type = type;
49 	op_array->arg_flags[0] = 0;
50 	op_array->arg_flags[1] = 0;
51 	op_array->arg_flags[2] = 0;
52 
53 	op_array->refcount = (uint32_t *) emalloc(sizeof(uint32_t));
54 	*op_array->refcount = 1;
55 	op_array->last = 0;
56 	op_array->opcodes = emalloc(initial_ops_size * sizeof(zend_op));
57 
58 	op_array->last_var = 0;
59 	op_array->vars = NULL;
60 
61 	op_array->T = 0;
62 
63 	op_array->function_name = NULL;
64 	op_array->filename = zend_string_copy(zend_get_compiled_filename());
65 	op_array->doc_comment = NULL;
66 	op_array->attributes = NULL;
67 
68 	op_array->arg_info = NULL;
69 	op_array->num_args = 0;
70 	op_array->required_num_args = 0;
71 
72 	op_array->scope = NULL;
73 	op_array->prototype = NULL;
74 
75 	op_array->live_range = NULL;
76 	op_array->try_catch_array = NULL;
77 	op_array->last_live_range = 0;
78 
79 	op_array->static_variables = NULL;
80 	ZEND_MAP_PTR_INIT(op_array->static_variables_ptr, &op_array->static_variables);
81 	op_array->last_try_catch = 0;
82 
83 	op_array->fn_flags = 0;
84 
85 	op_array->last_literal = 0;
86 	op_array->literals = NULL;
87 
88 	ZEND_MAP_PTR_INIT(op_array->run_time_cache, NULL);
89 	op_array->cache_size = zend_op_array_extension_handles * sizeof(void*);
90 
91 	memset(op_array->reserved, 0, ZEND_MAX_RESERVED_RESOURCES * sizeof(void*));
92 
93 	if (zend_extension_flags & ZEND_EXTENSIONS_HAVE_OP_ARRAY_CTOR) {
94 		zend_llist_apply_with_argument(&zend_extensions, (llist_apply_with_arg_func_t) zend_extension_op_array_ctor_handler, op_array);
95 	}
96 }
97 
destroy_zend_function(zend_function * function)98 ZEND_API void destroy_zend_function(zend_function *function)
99 {
100 	zval tmp;
101 
102 	ZVAL_PTR(&tmp, function);
103 	zend_function_dtor(&tmp);
104 }
105 
zend_type_release(zend_type type,zend_bool persistent)106 ZEND_API void zend_type_release(zend_type type, zend_bool persistent) {
107 	if (ZEND_TYPE_HAS_LIST(type)) {
108 		zend_type *list_type;
109 		ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(type), list_type) {
110 			if (ZEND_TYPE_HAS_NAME(*list_type)) {
111 				zend_string_release(ZEND_TYPE_NAME(*list_type));
112 			}
113 		} ZEND_TYPE_LIST_FOREACH_END();
114 		if (!ZEND_TYPE_USES_ARENA(type)) {
115 			pefree(ZEND_TYPE_LIST(type), persistent);
116 		}
117 	} else if (ZEND_TYPE_HAS_NAME(type)) {
118 		zend_string_release(ZEND_TYPE_NAME(type));
119 	}
120 }
121 
zend_free_internal_arg_info(zend_internal_function * function)122 void zend_free_internal_arg_info(zend_internal_function *function) {
123 	if ((function->fn_flags & (ZEND_ACC_HAS_RETURN_TYPE|ZEND_ACC_HAS_TYPE_HINTS)) &&
124 		function->arg_info) {
125 
126 		uint32_t i;
127 		uint32_t num_args = function->num_args + 1;
128 		zend_internal_arg_info *arg_info = function->arg_info - 1;
129 
130 		if (function->fn_flags & ZEND_ACC_VARIADIC) {
131 			num_args++;
132 		}
133 		for (i = 0 ; i < num_args; i++) {
134 			zend_type_release(arg_info[i].type, /* persistent */ 1);
135 		}
136 		free(arg_info);
137 	}
138 }
139 
zend_function_dtor(zval * zv)140 ZEND_API void zend_function_dtor(zval *zv)
141 {
142 	zend_function *function = Z_PTR_P(zv);
143 
144 	if (function->type == ZEND_USER_FUNCTION) {
145 		ZEND_ASSERT(function->common.function_name);
146 		destroy_op_array(&function->op_array);
147 		/* op_arrays are allocated on arena, so we don't have to free them */
148 	} else {
149 		ZEND_ASSERT(function->type == ZEND_INTERNAL_FUNCTION);
150 		ZEND_ASSERT(function->common.function_name);
151 		zend_string_release_ex(function->common.function_name, 1);
152 
153 		/* For methods this will be called explicitly. */
154 		if (!function->common.scope) {
155 			zend_free_internal_arg_info(&function->internal_function);
156 
157 			if (function->common.attributes) {
158 				zend_hash_release(function->common.attributes);
159 				function->common.attributes = NULL;
160 			}
161 		}
162 
163 		if (!(function->common.fn_flags & ZEND_ACC_ARENA_ALLOCATED)) {
164 			pefree(function, 1);
165 		}
166 	}
167 }
168 
zend_cleanup_internal_class_data(zend_class_entry * ce)169 ZEND_API void zend_cleanup_internal_class_data(zend_class_entry *ce)
170 {
171 	if (CE_STATIC_MEMBERS(ce)) {
172 		zval *static_members = CE_STATIC_MEMBERS(ce);
173 		zval *p = static_members;
174 		zval *end = p + ce->default_static_members_count;
175 		if (UNEXPECTED(ZEND_MAP_PTR(ce->static_members_table) == &ce->default_static_members_table)) {
176 			/* Special case: If this is a static property on a dl'ed internal class, then the
177 			 * static property table and the default property table are the same. In this case we
178 			 * destroy the values here, but leave behind valid UNDEF zvals and don't free the
179 			 * table itself. */
180 			while (p != end) {
181 				if (UNEXPECTED(Z_ISREF_P(p))) {
182 					zend_property_info *prop_info;
183 					ZEND_REF_FOREACH_TYPE_SOURCES(Z_REF_P(p), prop_info) {
184 						if (prop_info->ce == ce && p - static_members == prop_info->offset) {
185 							ZEND_REF_DEL_TYPE_SOURCE(Z_REF_P(p), prop_info);
186 							break; /* stop iteration here, the array might be realloc()'ed */
187 						}
188 					} ZEND_REF_FOREACH_TYPE_SOURCES_END();
189 				}
190 				i_zval_ptr_dtor(p);
191 				ZVAL_UNDEF(p);
192 				p++;
193 			}
194 		} else {
195 			ZEND_MAP_PTR_SET(ce->static_members_table, NULL);
196 			while (p != end) {
197 				if (UNEXPECTED(Z_ISREF_P(p))) {
198 					zend_property_info *prop_info;
199 					ZEND_REF_FOREACH_TYPE_SOURCES(Z_REF_P(p), prop_info) {
200 						if (prop_info->ce == ce && p - static_members == prop_info->offset) {
201 							ZEND_REF_DEL_TYPE_SOURCE(Z_REF_P(p), prop_info);
202 							break; /* stop iteration here, the array might be realloc()'ed */
203 						}
204 					} ZEND_REF_FOREACH_TYPE_SOURCES_END();
205 				}
206 				i_zval_ptr_dtor(p);
207 				p++;
208 			}
209 			efree(static_members);
210 		}
211 	}
212 }
213 
_destroy_zend_class_traits_info(zend_class_entry * ce)214 static void _destroy_zend_class_traits_info(zend_class_entry *ce)
215 {
216 	uint32_t i;
217 
218 	for (i = 0; i < ce->num_traits; i++) {
219 		zend_string_release_ex(ce->trait_names[i].name, 0);
220 		zend_string_release_ex(ce->trait_names[i].lc_name, 0);
221 	}
222 	efree(ce->trait_names);
223 
224 	if (ce->trait_aliases) {
225 		i = 0;
226 		while (ce->trait_aliases[i]) {
227 			if (ce->trait_aliases[i]->trait_method.method_name) {
228 				zend_string_release_ex(ce->trait_aliases[i]->trait_method.method_name, 0);
229 			}
230 			if (ce->trait_aliases[i]->trait_method.class_name) {
231 				zend_string_release_ex(ce->trait_aliases[i]->trait_method.class_name, 0);
232 			}
233 
234 			if (ce->trait_aliases[i]->alias) {
235 				zend_string_release_ex(ce->trait_aliases[i]->alias, 0);
236 			}
237 
238 			efree(ce->trait_aliases[i]);
239 			i++;
240 		}
241 
242 		efree(ce->trait_aliases);
243 	}
244 
245 	if (ce->trait_precedences) {
246 		uint32_t j;
247 
248 		i = 0;
249 		while (ce->trait_precedences[i]) {
250 			zend_string_release_ex(ce->trait_precedences[i]->trait_method.method_name, 0);
251 			zend_string_release_ex(ce->trait_precedences[i]->trait_method.class_name, 0);
252 
253 			for (j = 0; j < ce->trait_precedences[i]->num_excludes; j++) {
254 				zend_string_release_ex(ce->trait_precedences[i]->exclude_class_names[j], 0);
255 			}
256 			efree(ce->trait_precedences[i]);
257 			i++;
258 		}
259 		efree(ce->trait_precedences);
260 	}
261 }
262 
destroy_zend_class(zval * zv)263 ZEND_API void destroy_zend_class(zval *zv)
264 {
265 	zend_property_info *prop_info;
266 	zend_class_entry *ce = Z_PTR_P(zv);
267 	zend_function *fn;
268 
269 	if (ce->ce_flags & (ZEND_ACC_IMMUTABLE|ZEND_ACC_PRELOADED)) {
270 		zend_op_array *op_array;
271 
272 		if (ce->default_static_members_count) {
273 			zend_cleanup_internal_class_data(ce);
274 		}
275 		if (ce->ce_flags & ZEND_HAS_STATIC_IN_METHODS) {
276 			ZEND_HASH_FOREACH_PTR(&ce->function_table, op_array) {
277 				if (op_array->type == ZEND_USER_FUNCTION) {
278 					destroy_op_array(op_array);
279 				}
280 			} ZEND_HASH_FOREACH_END();
281 		}
282 		return;
283 	} else if (--ce->refcount > 0) {
284 		return;
285 	}
286 	switch (ce->type) {
287 		case ZEND_USER_CLASS:
288 			if (ce->parent_name && !(ce->ce_flags & ZEND_ACC_RESOLVED_PARENT)) {
289 				zend_string_release_ex(ce->parent_name, 0);
290 			}
291 			if (ce->default_properties_table) {
292 				zval *p = ce->default_properties_table;
293 				zval *end = p + ce->default_properties_count;
294 
295 				while (p != end) {
296 					i_zval_ptr_dtor(p);
297 					p++;
298 				}
299 				efree(ce->default_properties_table);
300 			}
301 			if (ce->default_static_members_table) {
302 				zval *p = ce->default_static_members_table;
303 				zval *end = p + ce->default_static_members_count;
304 
305 				while (p != end) {
306 					if (UNEXPECTED(Z_ISREF_P(p))) {
307 						zend_property_info *prop_info;
308 						ZEND_REF_FOREACH_TYPE_SOURCES(Z_REF_P(p), prop_info) {
309 							if (prop_info->ce == ce && p - ce->default_static_members_table == prop_info->offset) {
310 								ZEND_REF_DEL_TYPE_SOURCE(Z_REF_P(p), prop_info);
311 								break; /* stop iteration here, the array might be realloc()'ed */
312 							}
313 						} ZEND_REF_FOREACH_TYPE_SOURCES_END();
314 					}
315 					i_zval_ptr_dtor(p);
316 					p++;
317 				}
318 				efree(ce->default_static_members_table);
319 			}
320 			ZEND_HASH_FOREACH_PTR(&ce->properties_info, prop_info) {
321 				if (prop_info->ce == ce) {
322 					zend_string_release_ex(prop_info->name, 0);
323 					if (prop_info->doc_comment) {
324 						zend_string_release_ex(prop_info->doc_comment, 0);
325 					}
326 					if (prop_info->attributes) {
327 						zend_hash_release(prop_info->attributes);
328 					}
329 					zend_type_release(prop_info->type, /* persistent */ 0);
330 				}
331 			} ZEND_HASH_FOREACH_END();
332 			zend_hash_destroy(&ce->properties_info);
333 			zend_string_release_ex(ce->name, 0);
334 			zend_hash_destroy(&ce->function_table);
335 			if (zend_hash_num_elements(&ce->constants_table)) {
336 				zend_class_constant *c;
337 
338 				ZEND_HASH_FOREACH_PTR(&ce->constants_table, c) {
339 					if (c->ce == ce) {
340 						zval_ptr_dtor_nogc(&c->value);
341 						if (c->doc_comment) {
342 							zend_string_release_ex(c->doc_comment, 0);
343 						}
344 						if (c->attributes) {
345 							zend_hash_release(c->attributes);
346 						}
347 					}
348 				} ZEND_HASH_FOREACH_END();
349 			}
350 			zend_hash_destroy(&ce->constants_table);
351 			if (ce->num_interfaces > 0) {
352 				if (!(ce->ce_flags & ZEND_ACC_RESOLVED_INTERFACES)) {
353 					uint32_t i;
354 
355 					for (i = 0; i < ce->num_interfaces; i++) {
356 						zend_string_release_ex(ce->interface_names[i].name, 0);
357 						zend_string_release_ex(ce->interface_names[i].lc_name, 0);
358 					}
359 				}
360 				efree(ce->interfaces);
361 			}
362 			zend_string_release_ex(ce->info.user.filename, 0);
363 			if (ce->info.user.doc_comment) {
364 				zend_string_release_ex(ce->info.user.doc_comment, 0);
365 			}
366 			if (ce->attributes) {
367 				zend_hash_release(ce->attributes);
368 			}
369 
370 			if (ce->num_traits > 0) {
371 				_destroy_zend_class_traits_info(ce);
372 			}
373 
374 			break;
375 		case ZEND_INTERNAL_CLASS:
376 			if (ce->default_properties_table) {
377 				zval *p = ce->default_properties_table;
378 				zval *end = p + ce->default_properties_count;
379 
380 				while (p != end) {
381 					zval_internal_ptr_dtor(p);
382 					p++;
383 				}
384 				free(ce->default_properties_table);
385 			}
386 			if (ce->default_static_members_table) {
387 				zval *p = ce->default_static_members_table;
388 				zval *end = p + ce->default_static_members_count;
389 
390 				while (p != end) {
391 					zval_internal_ptr_dtor(p);
392 					p++;
393 				}
394 				free(ce->default_static_members_table);
395 				if (ZEND_MAP_PTR(ce->static_members_table) != &ce->default_static_members_table) {
396 					zend_cleanup_internal_class_data(ce);
397 				}
398 			}
399 			zend_hash_destroy(&ce->properties_info);
400 			zend_string_release_ex(ce->name, 1);
401 
402 			/* TODO: eliminate this loop for classes without functions with arg_info / attributes */
403 			ZEND_HASH_FOREACH_PTR(&ce->function_table, fn) {
404 				if (fn->common.scope == ce) {
405 					if (fn->common.fn_flags & (ZEND_ACC_HAS_RETURN_TYPE|ZEND_ACC_HAS_TYPE_HINTS)) {
406 						zend_free_internal_arg_info(&fn->internal_function);
407 					}
408 
409 					if (fn->common.attributes) {
410 						zend_hash_release(fn->common.attributes);
411 						fn->common.attributes = NULL;
412 					}
413 				}
414 			} ZEND_HASH_FOREACH_END();
415 
416 			zend_hash_destroy(&ce->function_table);
417 			if (zend_hash_num_elements(&ce->constants_table)) {
418 				zend_class_constant *c;
419 
420 				ZEND_HASH_FOREACH_PTR(&ce->constants_table, c) {
421 					if (c->ce == ce) {
422 						zval_internal_ptr_dtor(&c->value);
423 						if (c->doc_comment) {
424 							zend_string_release_ex(c->doc_comment, 1);
425 						}
426 						if (c->attributes) {
427 							zend_hash_release(c->attributes);
428 						}
429 					}
430 					free(c);
431 				} ZEND_HASH_FOREACH_END();
432 				zend_hash_destroy(&ce->constants_table);
433 			}
434 			if (ce->iterator_funcs_ptr) {
435 				free(ce->iterator_funcs_ptr);
436 			}
437 			if (ce->num_interfaces > 0) {
438 				free(ce->interfaces);
439 			}
440 			if (ce->properties_info_table) {
441 				free(ce->properties_info_table);
442 			}
443 			if (ce->attributes) {
444 				zend_hash_release(ce->attributes);
445 			}
446 			free(ce);
447 			break;
448 	}
449 }
450 
zend_class_add_ref(zval * zv)451 void zend_class_add_ref(zval *zv)
452 {
453 	zend_class_entry *ce = Z_PTR_P(zv);
454 
455 	if (!(ce->ce_flags & ZEND_ACC_IMMUTABLE)) {
456 		ce->refcount++;
457 	}
458 }
459 
destroy_op_array(zend_op_array * op_array)460 ZEND_API void destroy_op_array(zend_op_array *op_array)
461 {
462 	uint32_t i;
463 
464 	if (op_array->static_variables) {
465 		HashTable *ht = ZEND_MAP_PTR_GET(op_array->static_variables_ptr);
466 		if (ht && !(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE)) {
467 			if (GC_DELREF(ht) == 0) {
468 				zend_array_destroy(ht);
469 			}
470 		}
471 	}
472 
473 	if ((op_array->fn_flags & ZEND_ACC_HEAP_RT_CACHE)
474 	 && ZEND_MAP_PTR(op_array->run_time_cache)) {
475 		efree(ZEND_MAP_PTR(op_array->run_time_cache));
476 	}
477 
478 	if (op_array->function_name) {
479 		zend_string_release_ex(op_array->function_name, 0);
480 	}
481 
482 	if (!op_array->refcount || --(*op_array->refcount) > 0) {
483 		return;
484 	}
485 
486 	efree_size(op_array->refcount, sizeof(*(op_array->refcount)));
487 
488 	if (op_array->vars) {
489 		i = op_array->last_var;
490 		while (i > 0) {
491 			i--;
492 			zend_string_release_ex(op_array->vars[i], 0);
493 		}
494 		efree(op_array->vars);
495 	}
496 
497 	if (op_array->literals) {
498 		zval *literal = op_array->literals;
499 		zval *end = literal + op_array->last_literal;
500 	 	while (literal < end) {
501 			zval_ptr_dtor_nogc(literal);
502 			literal++;
503 		}
504 		if (ZEND_USE_ABS_CONST_ADDR
505 		 || !(op_array->fn_flags & ZEND_ACC_DONE_PASS_TWO)) {
506 			efree(op_array->literals);
507 		}
508 	}
509 	efree(op_array->opcodes);
510 
511 	zend_string_release_ex(op_array->filename, 0);
512 	if (op_array->doc_comment) {
513 		zend_string_release_ex(op_array->doc_comment, 0);
514 	}
515 	if (op_array->attributes) {
516 		zend_hash_release(op_array->attributes);
517 	}
518 	if (op_array->live_range) {
519 		efree(op_array->live_range);
520 	}
521 	if (op_array->try_catch_array) {
522 		efree(op_array->try_catch_array);
523 	}
524 	if (zend_extension_flags & ZEND_EXTENSIONS_HAVE_OP_ARRAY_DTOR) {
525 		if (op_array->fn_flags & ZEND_ACC_DONE_PASS_TWO) {
526 			zend_llist_apply_with_argument(&zend_extensions, (llist_apply_with_arg_func_t) zend_extension_op_array_dtor_handler, op_array);
527 		}
528 	}
529 	if (op_array->arg_info) {
530 		uint32_t num_args = op_array->num_args;
531 		zend_arg_info *arg_info = op_array->arg_info;
532 
533 		if (op_array->fn_flags & ZEND_ACC_HAS_RETURN_TYPE) {
534 			arg_info--;
535 			num_args++;
536 		}
537 		if (op_array->fn_flags & ZEND_ACC_VARIADIC) {
538 			num_args++;
539 		}
540 		for (i = 0 ; i < num_args; i++) {
541 			if (arg_info[i].name) {
542 				zend_string_release_ex(arg_info[i].name, 0);
543 			}
544 			zend_type_release(arg_info[i].type, /* persistent */ 0);
545 		}
546 		efree(arg_info);
547 	}
548 }
549 
zend_update_extended_stmts(zend_op_array * op_array)550 static void zend_update_extended_stmts(zend_op_array *op_array)
551 {
552 	zend_op *opline = op_array->opcodes, *end=opline+op_array->last;
553 
554 	while (opline<end) {
555 		if (opline->opcode == ZEND_EXT_STMT) {
556 			if (opline+1<end) {
557 				if ((opline+1)->opcode == ZEND_EXT_STMT) {
558 					opline->opcode = ZEND_NOP;
559 					opline++;
560 					continue;
561 				}
562 				if (opline+1<end) {
563 					opline->lineno = (opline+1)->lineno;
564 				}
565 			} else {
566 				opline->opcode = ZEND_NOP;
567 			}
568 		}
569 		opline++;
570 	}
571 }
572 
zend_extension_op_array_handler(zend_extension * extension,zend_op_array * op_array)573 static void zend_extension_op_array_handler(zend_extension *extension, zend_op_array *op_array)
574 {
575 	if (extension->op_array_handler) {
576 		extension->op_array_handler(op_array);
577 	}
578 }
579 
zend_check_finally_breakout(zend_op_array * op_array,uint32_t op_num,uint32_t dst_num)580 static void zend_check_finally_breakout(zend_op_array *op_array, uint32_t op_num, uint32_t dst_num)
581 {
582 	int i;
583 
584 	for (i = 0; i < op_array->last_try_catch; i++) {
585 		if ((op_num < op_array->try_catch_array[i].finally_op ||
586 					op_num >= op_array->try_catch_array[i].finally_end)
587 				&& (dst_num >= op_array->try_catch_array[i].finally_op &&
588 					 dst_num <= op_array->try_catch_array[i].finally_end)) {
589 			CG(in_compilation) = 1;
590 			CG(active_op_array) = op_array;
591 			CG(zend_lineno) = op_array->opcodes[op_num].lineno;
592 			zend_error_noreturn(E_COMPILE_ERROR, "jump into a finally block is disallowed");
593 		} else if ((op_num >= op_array->try_catch_array[i].finally_op
594 					&& op_num <= op_array->try_catch_array[i].finally_end)
595 				&& (dst_num > op_array->try_catch_array[i].finally_end
596 					|| dst_num < op_array->try_catch_array[i].finally_op)) {
597 			CG(in_compilation) = 1;
598 			CG(active_op_array) = op_array;
599 			CG(zend_lineno) = op_array->opcodes[op_num].lineno;
600 			zend_error_noreturn(E_COMPILE_ERROR, "jump out of a finally block is disallowed");
601 		}
602 	}
603 }
604 
zend_get_brk_cont_target(const zend_op_array * op_array,const zend_op * opline)605 static uint32_t zend_get_brk_cont_target(const zend_op_array *op_array, const zend_op *opline) {
606 	int nest_levels = opline->op2.num;
607 	int array_offset = opline->op1.num;
608 	zend_brk_cont_element *jmp_to;
609 	do {
610 		jmp_to = &CG(context).brk_cont_array[array_offset];
611 		if (nest_levels > 1) {
612 			array_offset = jmp_to->parent;
613 		}
614 	} while (--nest_levels > 0);
615 
616 	return opline->opcode == ZEND_BRK ? jmp_to->brk : jmp_to->cont;
617 }
618 
emit_live_range_raw(zend_op_array * op_array,uint32_t var_num,uint32_t kind,uint32_t start,uint32_t end)619 static void emit_live_range_raw(
620 		zend_op_array *op_array, uint32_t var_num, uint32_t kind, uint32_t start, uint32_t end) {
621 	zend_live_range *range;
622 
623 	op_array->last_live_range++;
624 	op_array->live_range = erealloc(op_array->live_range,
625 		sizeof(zend_live_range) * op_array->last_live_range);
626 
627 	ZEND_ASSERT(start < end);
628 	range = &op_array->live_range[op_array->last_live_range - 1];
629 	range->var = EX_NUM_TO_VAR(op_array->last_var + var_num);
630 	range->var |= kind;
631 	range->start = start;
632 	range->end = end;
633 }
634 
emit_live_range(zend_op_array * op_array,uint32_t var_num,uint32_t start,uint32_t end,zend_needs_live_range_cb needs_live_range)635 static void emit_live_range(
636 		zend_op_array *op_array, uint32_t var_num, uint32_t start, uint32_t end,
637 		zend_needs_live_range_cb needs_live_range) {
638 	zend_op *def_opline = &op_array->opcodes[start], *orig_def_opline = def_opline;
639 	zend_op *use_opline = &op_array->opcodes[end];
640 	uint32_t kind;
641 
642 	switch (def_opline->opcode) {
643 		/* These should never be the first def. */
644 		case ZEND_ADD_ARRAY_ELEMENT:
645 		case ZEND_ADD_ARRAY_UNPACK:
646 		case ZEND_ROPE_ADD:
647 			ZEND_UNREACHABLE();
648 			return;
649 		/* Result is boolean, it doesn't have to be destroyed. */
650 		case ZEND_JMPZ_EX:
651 		case ZEND_JMPNZ_EX:
652 		case ZEND_BOOL:
653 		case ZEND_BOOL_NOT:
654 		/* Classes don't have to be destroyed. */
655 		case ZEND_FETCH_CLASS:
656 		case ZEND_DECLARE_ANON_CLASS:
657 		/* FAST_CALLs don't have to be destroyed. */
658 		case ZEND_FAST_CALL:
659 			return;
660 		case ZEND_BEGIN_SILENCE:
661 			kind = ZEND_LIVE_SILENCE;
662 			start++;
663 			break;
664 		case ZEND_ROPE_INIT:
665 			kind = ZEND_LIVE_ROPE;
666 			/* ROPE live ranges include the generating opcode. */
667 			def_opline--;
668 			break;
669 		case ZEND_FE_RESET_R:
670 		case ZEND_FE_RESET_RW:
671 			kind = ZEND_LIVE_LOOP;
672 			start++;
673 			break;
674 		/* Objects created via ZEND_NEW are only fully initialized
675 		 * after the DO_FCALL (constructor call).
676 		 * We are creating two live-ranges: ZEND_LINE_NEW for uninitialized
677 		 * part, and ZEND_LIVE_TMPVAR for initialized.
678 		 */
679 		case ZEND_NEW:
680 		{
681 			int level = 0;
682 			uint32_t orig_start = start;
683 
684 			while (def_opline + 1 < use_opline) {
685 				def_opline++;
686 				start++;
687 				if (def_opline->opcode == ZEND_DO_FCALL) {
688 					if (level == 0) {
689 						break;
690 					}
691 					level--;
692 				} else {
693 					switch (def_opline->opcode) {
694 						case ZEND_INIT_FCALL:
695 						case ZEND_INIT_FCALL_BY_NAME:
696 						case ZEND_INIT_NS_FCALL_BY_NAME:
697 						case ZEND_INIT_DYNAMIC_CALL:
698 						case ZEND_INIT_USER_CALL:
699 						case ZEND_INIT_METHOD_CALL:
700 						case ZEND_INIT_STATIC_METHOD_CALL:
701 						case ZEND_NEW:
702 							level++;
703 							break;
704 						case ZEND_DO_ICALL:
705 						case ZEND_DO_UCALL:
706 						case ZEND_DO_FCALL_BY_NAME:
707 							level--;
708 							break;
709 					}
710 				}
711 			}
712 			emit_live_range_raw(op_array, var_num, ZEND_LIVE_NEW, orig_start + 1, start + 1);
713 			if (start + 1 == end) {
714 				/* Trivial live-range, no need to store it. */
715 				return;
716 			}
717 		}
718 		/* explicit fallthrough */
719 		default:
720 			start++;
721 			kind = ZEND_LIVE_TMPVAR;
722 
723 			/* Check hook to determine whether a live range is necessary,
724 			 * e.g. based on type info. */
725 			if (needs_live_range && !needs_live_range(op_array, orig_def_opline)) {
726 				return;
727 			}
728 			break;
729 		case ZEND_COPY_TMP:
730 		{
731 			/* COPY_TMP has a split live-range: One from the definition until the use in
732 			 * "null" branch, and another from the start of the "non-null" branch to the
733 			 * FREE opcode. */
734 			uint32_t rt_var_num = EX_NUM_TO_VAR(op_array->last_var + var_num);
735 			if (needs_live_range && !needs_live_range(op_array, orig_def_opline)) {
736 				return;
737 			}
738 
739 			kind = ZEND_LIVE_TMPVAR;
740 			if (use_opline->opcode != ZEND_FREE) {
741 				/* This can happen if one branch of the coalesce has been optimized away.
742 				 * In this case we should emit a normal live-range instead. */
743 				start++;
744 				break;
745 			}
746 
747 			zend_op *block_start_op = use_opline;
748 			while ((block_start_op-1)->opcode == ZEND_FREE) {
749 				block_start_op--;
750 			}
751 
752 			start = block_start_op - op_array->opcodes;
753 			if (start != end) {
754 				emit_live_range_raw(op_array, var_num, kind, start, end);
755 			}
756 
757 			do {
758 				use_opline--;
759 
760 				/* The use might have been optimized away, in which case we will hit the def
761 				 * instead. */
762 				if (use_opline->opcode == ZEND_COPY_TMP && use_opline->result.var == rt_var_num) {
763 					return;
764 				}
765 			} while (!(
766 				((use_opline->op1_type & (IS_TMP_VAR|IS_VAR)) && use_opline->op1.var == rt_var_num) ||
767 				((use_opline->op2_type & (IS_TMP_VAR|IS_VAR)) && use_opline->op2.var == rt_var_num)
768 			));
769 
770 			start = def_opline + 1 - op_array->opcodes;
771 			end = use_opline - op_array->opcodes;
772 			emit_live_range_raw(op_array, var_num, kind, start, end);
773 			return;
774 		}
775 	}
776 
777 	emit_live_range_raw(op_array, var_num, kind, start, end);
778 }
779 
is_fake_def(zend_op * opline)780 static zend_bool is_fake_def(zend_op *opline) {
781 	/* These opcodes only modify the result, not create it. */
782 	return opline->opcode == ZEND_ROPE_ADD
783 		|| opline->opcode == ZEND_ADD_ARRAY_ELEMENT
784 		|| opline->opcode == ZEND_ADD_ARRAY_UNPACK;
785 }
786 
keeps_op1_alive(zend_op * opline)787 static zend_bool keeps_op1_alive(zend_op *opline) {
788 	/* These opcodes don't consume their OP1 operand,
789 	 * it is later freed by something else. */
790 	if (opline->opcode == ZEND_CASE
791 	 || opline->opcode == ZEND_CASE_STRICT
792 	 || opline->opcode == ZEND_SWITCH_LONG
793 	 || opline->opcode == ZEND_SWITCH_STRING
794 	 || opline->opcode == ZEND_MATCH
795 	 || opline->opcode == ZEND_FETCH_LIST_R
796 	 || opline->opcode == ZEND_COPY_TMP) {
797 		return 1;
798 	}
799 	ZEND_ASSERT(opline->opcode != ZEND_FE_FETCH_R
800 		&& opline->opcode != ZEND_FE_FETCH_RW
801 		&& opline->opcode != ZEND_FETCH_LIST_W
802 		&& opline->opcode != ZEND_VERIFY_RETURN_TYPE
803 		&& opline->opcode != ZEND_BIND_LEXICAL
804 		&& opline->opcode != ZEND_ROPE_ADD);
805 	return 0;
806 }
807 
808 /* Live ranges must be sorted by increasing start opline */
cmp_live_range(const zend_live_range * a,const zend_live_range * b)809 static int cmp_live_range(const zend_live_range *a, const zend_live_range *b) {
810 	return a->start - b->start;
811 }
swap_live_range(zend_live_range * a,zend_live_range * b)812 static void swap_live_range(zend_live_range *a, zend_live_range *b) {
813 	uint32_t tmp;
814 	tmp = a->var;
815 	a->var = b->var;
816 	b->var = tmp;
817 	tmp = a->start;
818 	a->start = b->start;
819 	b->start = tmp;
820 	tmp = a->end;
821 	a->end = b->end;
822 	b->end = tmp;
823 }
824 
zend_calc_live_ranges(zend_op_array * op_array,zend_needs_live_range_cb needs_live_range)825 static void zend_calc_live_ranges(
826 		zend_op_array *op_array, zend_needs_live_range_cb needs_live_range) {
827 	uint32_t opnum = op_array->last;
828 	zend_op *opline = &op_array->opcodes[opnum];
829 	ALLOCA_FLAG(use_heap)
830 	uint32_t var_offset = op_array->last_var;
831 	uint32_t *last_use = do_alloca(sizeof(uint32_t) * op_array->T, use_heap);
832 	memset(last_use, -1, sizeof(uint32_t) * op_array->T);
833 
834 	ZEND_ASSERT(!op_array->live_range);
835 	while (opnum > 0) {
836 		opnum--;
837 		opline--;
838 
839 		if ((opline->result_type & (IS_TMP_VAR|IS_VAR)) && !is_fake_def(opline)) {
840 			uint32_t var_num = EX_VAR_TO_NUM(opline->result.var) - var_offset;
841 			/* Defs without uses can occur for two reasons: Either because the result is
842 			 * genuinely unused (e.g. omitted FREE opcode for an unused boolean result), or
843 			 * because there are multiple defining opcodes (e.g. JMPZ_EX and QM_ASSIGN), in
844 			 * which case the last one starts the live range. As such, we can simply ignore
845 			 * missing uses here. */
846 			if (EXPECTED(last_use[var_num] != (uint32_t) -1)) {
847 				/* Skip trivial live-range */
848 				if (opnum + 1 != last_use[var_num]) {
849 					uint32_t num;
850 
851 #if 1
852 					/* OP_DATA uses only op1 operand */
853 					ZEND_ASSERT(opline->opcode != ZEND_OP_DATA);
854 					num = opnum;
855 #else
856 					/* OP_DATA is really part of the previous opcode. */
857 					num = opnum - (opline->opcode == ZEND_OP_DATA);
858 #endif
859 					emit_live_range(op_array, var_num, num, last_use[var_num], needs_live_range);
860 				}
861 				last_use[var_num] = (uint32_t) -1;
862 			}
863 		}
864 
865 		if ((opline->op1_type & (IS_TMP_VAR|IS_VAR))) {
866 			uint32_t var_num = EX_VAR_TO_NUM(opline->op1.var) - var_offset;
867 			if (EXPECTED(last_use[var_num] == (uint32_t) -1)) {
868 				if (EXPECTED(!keeps_op1_alive(opline))) {
869 					/* OP_DATA is really part of the previous opcode. */
870 					last_use[var_num] = opnum - (opline->opcode == ZEND_OP_DATA);
871 				}
872 			}
873 		}
874 		if (opline->op2_type & (IS_TMP_VAR|IS_VAR)) {
875 			uint32_t var_num = EX_VAR_TO_NUM(opline->op2.var) - var_offset;
876 			if (UNEXPECTED(opline->opcode == ZEND_FE_FETCH_R
877 					|| opline->opcode == ZEND_FE_FETCH_RW)) {
878 				/* OP2 of FE_FETCH is actually a def, not a use. */
879 				if (last_use[var_num] != (uint32_t) -1) {
880 					if (opnum + 1 != last_use[var_num]) {
881 						emit_live_range(
882 							op_array, var_num, opnum, last_use[var_num], needs_live_range);
883 					}
884 					last_use[var_num] = (uint32_t) -1;
885 				}
886 			} else if (EXPECTED(last_use[var_num] == (uint32_t) -1)) {
887 #if 1
888 				/* OP_DATA uses only op1 operand */
889 				ZEND_ASSERT(opline->opcode != ZEND_OP_DATA);
890 				last_use[var_num] = opnum;
891 #else
892 				/* OP_DATA is really part of the previous opcode. */
893 				last_use[var_num] = opnum - (opline->opcode == ZEND_OP_DATA);
894 #endif
895 			}
896 		}
897 	}
898 
899 	if (op_array->last_live_range > 1) {
900 		zend_live_range *r1 = op_array->live_range;
901 		zend_live_range *r2 = r1 + op_array->last_live_range - 1;
902 
903 		/* In most cases we need just revert the array */
904 		while (r1 < r2) {
905 			swap_live_range(r1, r2);
906 			r1++;
907 			r2--;
908 		}
909 
910 		r1 = op_array->live_range;
911 		r2 = r1 + op_array->last_live_range - 1;
912 		while (r1 < r2) {
913 			if (r1->start > (r1+1)->start) {
914 				zend_sort(r1, r2 - r1 + 1, sizeof(zend_live_range),
915 					(compare_func_t) cmp_live_range, (swap_func_t) swap_live_range);
916 				break;
917 			}
918 			r1++;
919 		}
920 	}
921 
922 	free_alloca(last_use, use_heap);
923 }
924 
zend_recalc_live_ranges(zend_op_array * op_array,zend_needs_live_range_cb needs_live_range)925 ZEND_API void zend_recalc_live_ranges(
926 		zend_op_array *op_array, zend_needs_live_range_cb needs_live_range) {
927 	/* We assume that we never create live-ranges where there were none before. */
928 	ZEND_ASSERT(op_array->live_range);
929 	efree(op_array->live_range);
930 	op_array->live_range = NULL;
931 	op_array->last_live_range = 0;
932 	zend_calc_live_ranges(op_array, needs_live_range);
933 }
934 
pass_two(zend_op_array * op_array)935 ZEND_API void pass_two(zend_op_array *op_array)
936 {
937 	zend_op *opline, *end;
938 
939 	if (!ZEND_USER_CODE(op_array->type)) {
940 		return;
941 	}
942 	if (CG(compiler_options) & ZEND_COMPILE_EXTENDED_STMT) {
943 		zend_update_extended_stmts(op_array);
944 	}
945 	if (CG(compiler_options) & ZEND_COMPILE_HANDLE_OP_ARRAY) {
946 		if (zend_extension_flags & ZEND_EXTENSIONS_HAVE_OP_ARRAY_HANDLER) {
947 			zend_llist_apply_with_argument(&zend_extensions, (llist_apply_with_arg_func_t) zend_extension_op_array_handler, op_array);
948 		}
949 	}
950 
951 	if (CG(context).vars_size != op_array->last_var) {
952 		op_array->vars = (zend_string**) erealloc(op_array->vars, sizeof(zend_string*)*op_array->last_var);
953 		CG(context).vars_size = op_array->last_var;
954 	}
955 
956 #if ZEND_USE_ABS_CONST_ADDR
957 	if (CG(context).opcodes_size != op_array->last) {
958 		op_array->opcodes = (zend_op *) erealloc(op_array->opcodes, sizeof(zend_op)*op_array->last);
959 		CG(context).opcodes_size = op_array->last;
960 	}
961 	if (CG(context).literals_size != op_array->last_literal) {
962 		op_array->literals = (zval*)erealloc(op_array->literals, sizeof(zval) * op_array->last_literal);
963 		CG(context).literals_size = op_array->last_literal;
964 	}
965 #else
966 	op_array->opcodes = (zend_op *) erealloc(op_array->opcodes,
967 		ZEND_MM_ALIGNED_SIZE_EX(sizeof(zend_op) * op_array->last, 16) +
968 		sizeof(zval) * op_array->last_literal);
969 	if (op_array->literals) {
970 		memcpy(((char*)op_array->opcodes) + ZEND_MM_ALIGNED_SIZE_EX(sizeof(zend_op) * op_array->last, 16),
971 			op_array->literals, sizeof(zval) * op_array->last_literal);
972 		efree(op_array->literals);
973 		op_array->literals = (zval*)(((char*)op_array->opcodes) + ZEND_MM_ALIGNED_SIZE_EX(sizeof(zend_op) * op_array->last, 16));
974 	}
975 	CG(context).opcodes_size = op_array->last;
976 	CG(context).literals_size = op_array->last_literal;
977 #endif
978 
979 	/* Needs to be set directly after the opcode/literal reallocation, to ensure destruction
980 	 * happens correctly if any of the following fixups generate a fatal error. */
981 	op_array->fn_flags |= ZEND_ACC_DONE_PASS_TWO;
982 
983 	opline = op_array->opcodes;
984 	end = opline + op_array->last;
985 	while (opline < end) {
986 		switch (opline->opcode) {
987 			case ZEND_RECV_INIT:
988 				{
989 					zval *val = CT_CONSTANT(opline->op2);
990 					if (Z_TYPE_P(val) == IS_CONSTANT_AST) {
991 						uint32_t slot = ZEND_MM_ALIGNED_SIZE_EX(op_array->cache_size, 8);
992 						Z_CACHE_SLOT_P(val) = slot;
993 						op_array->cache_size += sizeof(zval);
994 					}
995 				}
996 				break;
997 			case ZEND_FAST_CALL:
998 				opline->op1.opline_num = op_array->try_catch_array[opline->op1.num].finally_op;
999 				ZEND_PASS_TWO_UPDATE_JMP_TARGET(op_array, opline, opline->op1);
1000 				break;
1001 			case ZEND_BRK:
1002 			case ZEND_CONT:
1003 				{
1004 					uint32_t jmp_target = zend_get_brk_cont_target(op_array, opline);
1005 
1006 					if (op_array->fn_flags & ZEND_ACC_HAS_FINALLY_BLOCK) {
1007 						zend_check_finally_breakout(op_array, opline - op_array->opcodes, jmp_target);
1008 					}
1009 					opline->opcode = ZEND_JMP;
1010 					opline->op1.opline_num = jmp_target;
1011 					opline->op2.num = 0;
1012 					ZEND_PASS_TWO_UPDATE_JMP_TARGET(op_array, opline, opline->op1);
1013 				}
1014 				break;
1015 			case ZEND_GOTO:
1016 				zend_resolve_goto_label(op_array, opline);
1017 				if (op_array->fn_flags & ZEND_ACC_HAS_FINALLY_BLOCK) {
1018 					zend_check_finally_breakout(op_array, opline - op_array->opcodes, opline->op1.opline_num);
1019 				}
1020 				/* break omitted intentionally */
1021 			case ZEND_JMP:
1022 				ZEND_PASS_TWO_UPDATE_JMP_TARGET(op_array, opline, opline->op1);
1023 				break;
1024 			case ZEND_JMPZNZ:
1025 				/* absolute index to relative offset */
1026 				opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, opline, opline->extended_value);
1027 				/* break omitted intentionally */
1028 			case ZEND_JMPZ:
1029 			case ZEND_JMPNZ:
1030 			case ZEND_JMPZ_EX:
1031 			case ZEND_JMPNZ_EX:
1032 			case ZEND_JMP_SET:
1033 			case ZEND_COALESCE:
1034 			case ZEND_FE_RESET_R:
1035 			case ZEND_FE_RESET_RW:
1036 			case ZEND_JMP_NULL:
1037 				ZEND_PASS_TWO_UPDATE_JMP_TARGET(op_array, opline, opline->op2);
1038 				break;
1039 			case ZEND_ASSERT_CHECK:
1040 			{
1041 				/* If result of assert is unused, result of check is unused as well */
1042 				zend_op *call = &op_array->opcodes[opline->op2.opline_num - 1];
1043 				if (call->opcode == ZEND_EXT_FCALL_END) {
1044 					call--;
1045 				}
1046 				if (call->result_type == IS_UNUSED) {
1047 					opline->result_type = IS_UNUSED;
1048 				}
1049 				ZEND_PASS_TWO_UPDATE_JMP_TARGET(op_array, opline, opline->op2);
1050 				break;
1051 			}
1052 			case ZEND_FE_FETCH_R:
1053 			case ZEND_FE_FETCH_RW:
1054 				/* absolute index to relative offset */
1055 				opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, opline, opline->extended_value);
1056 				break;
1057 			case ZEND_CATCH:
1058 				if (!(opline->extended_value & ZEND_LAST_CATCH)) {
1059 					ZEND_PASS_TWO_UPDATE_JMP_TARGET(op_array, opline, opline->op2);
1060 				}
1061 				break;
1062 			case ZEND_RETURN:
1063 			case ZEND_RETURN_BY_REF:
1064 				if (op_array->fn_flags & ZEND_ACC_GENERATOR) {
1065 					opline->opcode = ZEND_GENERATOR_RETURN;
1066 				}
1067 				break;
1068 			case ZEND_SWITCH_LONG:
1069 			case ZEND_SWITCH_STRING:
1070 			case ZEND_MATCH:
1071 			{
1072 				/* absolute indexes to relative offsets */
1073 				HashTable *jumptable = Z_ARRVAL_P(CT_CONSTANT(opline->op2));
1074 				zval *zv;
1075 				ZEND_HASH_FOREACH_VAL(jumptable, zv) {
1076 					Z_LVAL_P(zv) = ZEND_OPLINE_NUM_TO_OFFSET(op_array, opline, Z_LVAL_P(zv));
1077 				} ZEND_HASH_FOREACH_END();
1078 
1079 				opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, opline, opline->extended_value);
1080 				break;
1081 			}
1082 		}
1083 		if (opline->op1_type == IS_CONST) {
1084 			ZEND_PASS_TWO_UPDATE_CONSTANT(op_array, opline, opline->op1);
1085 		} else if (opline->op1_type & (IS_VAR|IS_TMP_VAR)) {
1086 			opline->op1.var = EX_NUM_TO_VAR(op_array->last_var + opline->op1.var);
1087 		}
1088 		if (opline->op2_type == IS_CONST) {
1089 			ZEND_PASS_TWO_UPDATE_CONSTANT(op_array, opline, opline->op2);
1090 		} else if (opline->op2_type & (IS_VAR|IS_TMP_VAR)) {
1091 			opline->op2.var = EX_NUM_TO_VAR(op_array->last_var + opline->op2.var);
1092 		}
1093 		if (opline->result_type & (IS_VAR|IS_TMP_VAR)) {
1094 			opline->result.var = EX_NUM_TO_VAR(op_array->last_var + opline->result.var);
1095 		}
1096 		ZEND_VM_SET_OPCODE_HANDLER(opline);
1097 		opline++;
1098 	}
1099 
1100 	zend_calc_live_ranges(op_array, NULL);
1101 
1102 	return;
1103 }
1104 
get_unary_op(int opcode)1105 ZEND_API unary_op_type get_unary_op(int opcode)
1106 {
1107 	switch (opcode) {
1108 		case ZEND_BW_NOT:
1109 			return (unary_op_type) bitwise_not_function;
1110 		case ZEND_BOOL_NOT:
1111 			return (unary_op_type) boolean_not_function;
1112 		default:
1113 			return (unary_op_type) NULL;
1114 	}
1115 }
1116 
get_binary_op(int opcode)1117 ZEND_API binary_op_type get_binary_op(int opcode)
1118 {
1119 	switch (opcode) {
1120 		case ZEND_ADD:
1121 			return (binary_op_type) add_function;
1122 		case ZEND_SUB:
1123 			return (binary_op_type) sub_function;
1124 		case ZEND_MUL:
1125 			return (binary_op_type) mul_function;
1126 		case ZEND_POW:
1127 			return (binary_op_type) pow_function;
1128 		case ZEND_DIV:
1129 			return (binary_op_type) div_function;
1130 		case ZEND_MOD:
1131 			return (binary_op_type) mod_function;
1132 		case ZEND_SL:
1133 			return (binary_op_type) shift_left_function;
1134 		case ZEND_SR:
1135 			return (binary_op_type) shift_right_function;
1136 		case ZEND_FAST_CONCAT:
1137 		case ZEND_CONCAT:
1138 			return (binary_op_type) concat_function;
1139 		case ZEND_IS_IDENTICAL:
1140 		case ZEND_CASE_STRICT:
1141 			return (binary_op_type) is_identical_function;
1142 		case ZEND_IS_NOT_IDENTICAL:
1143 			return (binary_op_type) is_not_identical_function;
1144 		case ZEND_IS_EQUAL:
1145 		case ZEND_CASE:
1146 			return (binary_op_type) is_equal_function;
1147 		case ZEND_IS_NOT_EQUAL:
1148 			return (binary_op_type) is_not_equal_function;
1149 		case ZEND_IS_SMALLER:
1150 			return (binary_op_type) is_smaller_function;
1151 		case ZEND_IS_SMALLER_OR_EQUAL:
1152 			return (binary_op_type) is_smaller_or_equal_function;
1153 		case ZEND_SPACESHIP:
1154 			return (binary_op_type) compare_function;
1155 		case ZEND_BW_OR:
1156 			return (binary_op_type) bitwise_or_function;
1157 		case ZEND_BW_AND:
1158 			return (binary_op_type) bitwise_and_function;
1159 		case ZEND_BW_XOR:
1160 			return (binary_op_type) bitwise_xor_function;
1161 		case ZEND_BOOL_XOR:
1162 			return (binary_op_type) boolean_xor_function;
1163 		default:
1164 			ZEND_UNREACHABLE();
1165 			return (binary_op_type) NULL;
1166 	}
1167 }
1168